Merge branch 'develop' of https://github.com/tidusjar/Ombi into develop

pull/2118/head
Jamie 7 years ago
commit 9bd545c736

@ -4,14 +4,68 @@
### **New Features** ### **New Features**
- Added the Recently Added Newsletter! You are welcome. [tidusjar]
- Added a new scrollbar to Ombi. [tidusjar]
- Added the ability to automatically generate the API Key on startup if it does not exist #2070. [tidusjar]
- Updated npm dependancies. [Jamie]
- Update README.md. [Jamie]
- Update README.md. [Jamie]
- Update ISSUE_TEMPLATE.md. [Jamie]
- Update appveyor.yml. [Jamie]
- Added recently added stuff. [Jamie]
- Added the recently added engine with some basic methods. [Jamie]
- Added the ability to refresh out backend metadata (#2078) [Jamie] - Added the ability to refresh out backend metadata (#2078) [Jamie]
### **Fixes** ### **Fixes**
- Fixed #2074 and #2079. [Jamie] - Specific favicons for different platforms. [louis-lau]
- MovieDbId was switched to string fron number so accomodated for change. [Anojh]
- Removing duplicate functions. [Anojh Thayaparan]
- Conflict resolving and adopting Jamie's new method. [Anojh]
- Wrote new calls to just get poster and bg. [Anojh]
- Fix for issue #1907, which is to add content poster and bg to issue details page. [Anojh]
- Dynamic Background Animation. [Anojh]
- Improved the message for #2037. [tidusjar]
- Improved the way we use the notification variables, we have now split out the Username and Alias (Requested User is depricated but not removed) [tidusjar]
- Removed redundant timers. [Anojh]
- More optimizations by reducing requests. [Anojh]
- Improved version. [Anojh]
- Dynamic Background Animation. [Anojh]
- Fixed #2055 and #1903. [Jamie]
- Small changes to the auto updater, let's see how this works. [Jamie] - Small changes to the auto updater, let's see how this works. [Jamie]
- Fixed build. [Jamie]
- Fixed the update check for the master build. [Jamie]
- Fixed build. [Jamie]
- Fixed #2074 and #2079. [Jamie]
## v3.0.3030 (2018-03-14) ## v3.0.3030 (2018-03-14)
@ -19,6 +73,10 @@
- Updated the .Net core dependancies #2072. [Jamie] - Updated the .Net core dependancies #2072. [Jamie]
### **Fixes**
- Delete Ombi.testdb. [Jamie]
## v3.0.3020 (2018-03-13) ## v3.0.3020 (2018-03-13)

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search; using Ombi.Core.Models.Search;
using Ombi.Store.Entities.Requests; using Ombi.Store.Entities.Requests;
@ -9,7 +10,7 @@ namespace Ombi.Core.Engine.Interfaces
{ {
Task RemoveTvRequest(int requestId); Task RemoveTvRequest(int requestId);
Task<RequestEngineResult> RequestTvShow(SearchTvShowViewModel tv); Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv);
Task<RequestEngineResult> DenyChildRequest(int requestId); Task<RequestEngineResult> DenyChildRequest(int requestId);
Task<IEnumerable<TvRequests>> SearchTvRequest(string search); Task<IEnumerable<TvRequests>> SearchTvRequest(string search);
Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> SearchTvRequestTree(string search); Task<IEnumerable<TreeNode<TvRequests, List<ChildRequests>>>> SearchTvRequestTree(string search);

@ -43,13 +43,13 @@ namespace Ombi.Core.Engine
private IAuditRepository Audit { get; } private IAuditRepository Audit { get; }
private readonly IRepository<RequestLog> _requestLog; private readonly IRepository<RequestLog> _requestLog;
public async Task<RequestEngineResult> RequestTvShow(SearchTvShowViewModel tv) public async Task<RequestEngineResult> RequestTvShow(TvRequestViewModel tv)
{ {
var user = await GetUser(); var user = await GetUser();
var tvBuilder = new TvShowRequestBuilder(TvApi); var tvBuilder = new TvShowRequestBuilder(TvApi);
(await tvBuilder (await tvBuilder
.GetShowInfo(tv.Id)) .GetShowInfo(tv.TvDbId))
.CreateTvList(tv) .CreateTvList(tv)
.CreateChild(tv, user.Id); .CreateChild(tv, user.Id);
@ -78,9 +78,9 @@ namespace Ombi.Core.Engine
} }
} }
await Audit.Record(AuditType.Added, AuditArea.TvRequest, $"Added Request {tv.Title}", Username); await Audit.Record(AuditType.Added, AuditArea.TvRequest, $"Added Request {tvBuilder.ChildRequest.Title}", Username);
var existingRequest = await TvRepository.Get().FirstOrDefaultAsync(x => x.TvDbId == tv.Id); var existingRequest = await TvRepository.Get().FirstOrDefaultAsync(x => x.TvDbId == tv.TvDbId);
if (existingRequest != null) if (existingRequest != null)
{ {
// Remove requests we already have, we just want new ones // Remove requests we already have, we just want new ones

@ -4,6 +4,7 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ombi.Api.TvMaze; using Ombi.Api.TvMaze;
using Ombi.Api.TvMaze.Models; using Ombi.Api.TvMaze.Models;
using Ombi.Core.Models.Requests;
using Ombi.Core.Models.Search; using Ombi.Core.Models.Search;
using Ombi.Helpers; using Ombi.Helpers;
using Ombi.Store.Entities; using Ombi.Store.Entities;
@ -23,7 +24,7 @@ namespace Ombi.Core.Helpers
private ITvMazeApi TvApi { get; } private ITvMazeApi TvApi { get; }
public ChildRequests ChildRequest { get; set; } public ChildRequests ChildRequest { get; set; }
public List<SeasonRequests> TvRequests { get; protected set; } public List<SeasonsViewModel> TvRequests { get; protected set; }
public string PosterPath { get; protected set; } public string PosterPath { get; protected set; }
public DateTime FirstAir { get; protected set; } public DateTime FirstAir { get; protected set; }
public TvRequests NewRequest { get; protected set; } public TvRequests NewRequest { get; protected set; }
@ -33,7 +34,7 @@ namespace Ombi.Core.Helpers
{ {
ShowInfo = await TvApi.ShowLookupByTheTvDbId(id); ShowInfo = await TvApi.ShowLookupByTheTvDbId(id);
DateTime.TryParse(ShowInfo.premiered, out DateTime dt); DateTime.TryParse(ShowInfo.premiered, out var dt);
FirstAir = dt; FirstAir = dt;
@ -43,37 +44,29 @@ namespace Ombi.Core.Helpers
return this; return this;
} }
public TvShowRequestBuilder CreateChild(SearchTvShowViewModel model, string userId) public TvShowRequestBuilder CreateChild(TvRequestViewModel model, string userId)
{ {
ChildRequest = new ChildRequests ChildRequest = new ChildRequests
{ {
Id = model.Id, Id = model.TvDbId,
RequestType = RequestType.TvShow, RequestType = RequestType.TvShow,
RequestedDate = DateTime.UtcNow, RequestedDate = DateTime.UtcNow,
Approved = false, Approved = false,
RequestedUserId = userId, RequestedUserId = userId,
SeasonRequests = new List<SeasonRequests>(), SeasonRequests = new List<SeasonRequests>(),
Title = model.Title, Title = ShowInfo.name,
SeriesType = ShowInfo.type.Equals("Animation", StringComparison.CurrentCultureIgnoreCase) ? SeriesType.Anime : SeriesType.Standard SeriesType = ShowInfo.type.Equals("Animation", StringComparison.CurrentCultureIgnoreCase) ? SeriesType.Anime : SeriesType.Standard
}; };
return this; return this;
} }
public TvShowRequestBuilder CreateTvList(SearchTvShowViewModel tv) public TvShowRequestBuilder CreateTvList(TvRequestViewModel tv)
{ {
TvRequests = new List<SeasonRequests>(); TvRequests = new List<SeasonsViewModel>();
// Only have the TV requests we actually requested and not everything // Only have the TV requests we actually requested and not everything
foreach (var season in tv.SeasonRequests) foreach (var season in tv.Seasons)
{ {
for (int i = season.Episodes.Count - 1; i >= 0; i--)
{
if (!season.Episodes[i].Requested)
{
season.Episodes.RemoveAt(i); // Remove the episode since it's not requested
}
}
if (season.Episodes.Any()) if (season.Episodes.Any())
{ {
TvRequests.Add(season); TvRequests.Add(season);
@ -81,11 +74,10 @@ namespace Ombi.Core.Helpers
} }
return this; return this;
} }
public async Task<TvShowRequestBuilder> BuildEpisodes(SearchTvShowViewModel tv) public async Task<TvShowRequestBuilder> BuildEpisodes(TvRequestViewModel tv)
{ {
if (tv.RequestAll) if (tv.RequestAll)
{ {
@ -173,26 +165,68 @@ namespace Ombi.Core.Helpers
else else
{ {
// It's a custom request // It's a custom request
ChildRequest.SeasonRequests = TvRequests; var seasonRequests = new List<SeasonRequests>();
var episodes = await TvApi.EpisodeLookup(ShowInfo.id);
foreach (var ep in episodes)
{
var existingSeasonRequest = seasonRequests.FirstOrDefault(x => x.SeasonNumber == ep.season);
if (existingSeasonRequest != null)
{
var requestedSeason = tv.Seasons.FirstOrDefault(x => x.SeasonNumber == ep.season);
var requestedEpisode = requestedSeason?.Episodes?.Any(x => x.EpisodeNumber == ep.number) ?? false;
if (requestedSeason != null && requestedEpisode)
{
// We already have this, let's just add the episodes to it
existingSeasonRequest.Episodes.Add(new EpisodeRequests
{
EpisodeNumber = ep.number,
AirDate = FormatDate(ep.airdate),
Title = ep.name,
Url = ep.url,
});
}
}
else
{
var newRequest = new SeasonRequests {SeasonNumber = ep.season};
var requestedSeason = tv.Seasons.FirstOrDefault(x => x.SeasonNumber == ep.season);
var requestedEpisode = requestedSeason?.Episodes?.Any(x => x.EpisodeNumber == ep.number) ?? false;
if (requestedSeason != null && requestedEpisode)
{
newRequest.Episodes.Add(new EpisodeRequests
{
EpisodeNumber = ep.number,
AirDate = FormatDate(ep.airdate),
Title = ep.name,
Url = ep.url,
});
seasonRequests.Add(newRequest);
}
}
}
foreach (var s in seasonRequests)
{
ChildRequest.SeasonRequests.Add(s);
}
} }
return this; return this;
} }
public TvShowRequestBuilder CreateNewRequest(SearchTvShowViewModel tv) public TvShowRequestBuilder CreateNewRequest(TvRequestViewModel tv)
{ {
NewRequest = new TvRequests NewRequest = new TvRequests
{ {
Id = tv.Id,
Overview = ShowInfo.summary.RemoveHtml(), Overview = ShowInfo.summary.RemoveHtml(),
PosterPath = PosterPath, PosterPath = PosterPath,
Title = ShowInfo.name, Title = ShowInfo.name,
ReleaseDate = FirstAir, ReleaseDate = FirstAir,
Status = ShowInfo.status, Status = ShowInfo.status,
ImdbId = ShowInfo.externals?.imdb ?? string.Empty, ImdbId = ShowInfo.externals?.imdb ?? string.Empty,
TvDbId = tv.Id, TvDbId = tv.TvDbId,
ChildRequests = new List<ChildRequests>(), ChildRequests = new List<ChildRequests>(),
TotalSeasons = tv.SeasonRequests.Count() TotalSeasons = tv.Seasons.Count()
}; };
NewRequest.ChildRequests.Add(ChildRequest); NewRequest.ChildRequests.Add(ChildRequest);

@ -0,0 +1,25 @@
using System.Collections.Generic;
namespace Ombi.Core.Models.Requests
{
public class TvRequestViewModel
{
public bool RequestAll { get; set; }
public bool LatestSeason { get; set; }
public bool FirstSeason { get; set; }
public int TvDbId { get; set; }
public List<SeasonsViewModel> Seasons { get; set; } = new List<SeasonsViewModel>();
}
public class SeasonsViewModel
{
public int SeasonNumber { get; set; }
public List<EpisodesViewModel> Episodes { get; set; } = new List<EpisodesViewModel>();
}
public class EpisodesViewModel
{
public int EpisodeNumber { get; set; }
}
}

@ -336,8 +336,16 @@ namespace Ombi.Schedule.Jobs.Ombi
TableData(sb); TableData(sb);
Href(sb, $"https://www.imdb.com/title/{info.ImdbId}/"); Href(sb, $"https://www.imdb.com/title/{info.ImdbId}/");
var releaseDate = DateTime.Parse(info.ReleaseDate); var releaseDate = string.Empty;
Header(sb, 3, $"{info.Title} ({releaseDate.Year})"); try
{
releaseDate = $"({DateTime.Parse(info.ReleaseDate).Year})";
}
catch (Exception)
{
// Swallow, couldn't parse the date
}
Header(sb, 3, $"{info.Title} {releaseDate}");
EndTag(sb, "a"); EndTag(sb, "a");
if (info.Genres.Any()) if (info.Genres.Any())

@ -10,7 +10,7 @@ namespace Ombi.Store.Repository.Requests
public class SeasonRequests : Entity public class SeasonRequests : Entity
{ {
public int SeasonNumber { get; set; } public int SeasonNumber { get; set; }
public List<EpisodeRequests> Episodes { get; set; } public List<EpisodeRequests> Episodes { get; set; } = new List<EpisodeRequests>();
public int ChildRequestId { get; set; } public int ChildRequestId { get; set; }
[ForeignKey(nameof(ChildRequestId))] [ForeignKey(nameof(ChildRequestId))]

@ -30,3 +30,20 @@ export interface ISearchTvResult {
firstSeason: boolean; firstSeason: boolean;
latestSeason: boolean; latestSeason: boolean;
} }
export interface ITvRequestViewModel {
requestAll: boolean;
firstSeason: boolean;
latestSeason: boolean;
tvDbId: number;
seasons: ISeasonsViewModel[];
}
export interface ISeasonsViewModel {
seasonNumber: number;
episodes: IEpisodesViewModel[];
}
export interface IEpisodesViewModel {
episodeNumber: number;
}

@ -1,13 +1,16 @@
<div *ngIf="issue"> <div *ngIf="issue">
<div class="myBg backdrop" [style.background-image]="backgroundPath"></div>
<div class="tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
<h1>{{issue.title}} </h1> <h1>{{issue.title}} </h1>
<div class="col-md-6"> <div class="col-md-6">
<span class="label label-info">{{IssueStatus[issue.status]}}</span> <img class="img-responsive poster" src="{{posterPath}}" alt="poster">
<span class="label label-success">{{issue.issueCategory.value}}</span> <span class="label label-info">{{IssueStatus[issue.status]}}</span>
<span class="label label-success">{{issue.issueCategory.value}}</span>
<h3 *ngIf="issue.userReported?.alias">{{'Issues.ReportedBy' | translate}}: {{issue.userReported.alias}}</h3> <h3 *ngIf="issue.userReported?.alias">{{'Issues.ReportedBy' | translate}}: {{issue.userReported.alias}}</h3>
<h3 *ngIf="!issue.userReported?.alias">{{'Issues.ReportedBy' | translate}}: {{issue.userReported.userName}}</h3> <h3 *ngIf="!issue.userReported?.alias">{{'Issues.ReportedBy' | translate}}: {{issue.userReported.userName}}</h3>
<h3 *ngIf="issue.subject">{{'Issues.Subject' | translate}}: {{issue.subject}}</h3> <h3 *ngIf="issue.subject">{{'Issues.Subject' | translate}}: {{issue.subject}}</h3>
<br> <br>
<div class="form-group"> <div class="form-group">
<label for="description" class="control-label" [translate]="'Issues.Description'"></label> <label for="description" class="control-label" [translate]="'Issues.Description'"></label>
<div> <div>
@ -26,7 +29,8 @@
<div class="panel-heading top-bar"> <div class="panel-heading top-bar">
<div class="col-md-8 col-xs-8"> <div class="col-md-8 col-xs-8">
<h3 class="panel-title"> <h3 class="panel-title">
<span class="glyphicon glyphicon-comment"></span> {{'Issues.Comments' | translate}}</h3> <span class="glyphicon glyphicon-comment"></span> {{'Issues.Comments' | translate}}
</h3>
</div> </div>
</div> </div>
@ -51,8 +55,7 @@
</div> </div>
<div class="panel-footer"> <div class="panel-footer">
<div class="input-group"> <div class="input-group">
<input id="btn-input" type="text" class="form-control input-sm chat_input" [(ngModel)]="newComment.comment" [attr.placeholder]="'Issues.WriteMessagePlaceholder' | translate" <input id="btn-input" type="text" class="form-control input-sm chat_input" [(ngModel)]="newComment.comment" [attr.placeholder]="'Issues.WriteMessagePlaceholder' | translate" />
/>
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-primary btn-sm" id="btn-chat" (click)="addComment()" [translate]="'Issues.SendMessageButton'"></button> <button class="btn btn-primary btn-sm" id="btn-chat" (click)="addComment()" [translate]="'Issues.SendMessageButton'"></button>
</span> </span>

@ -64,6 +64,15 @@ body{
overflow: hidden; overflow: hidden;
display: flex; display: flex;
} }
.myBg {
z-index: -1;
}
.tint {
z-index: -1;
}
img-responsive poster {
display:block;
}
img { img {
display: block; display: block;
width: 100%; width: 100%;

@ -2,8 +2,9 @@ import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { AuthService } from "../auth/auth.service"; import { AuthService } from "../auth/auth.service";
import { IssuesService, NotificationService, SettingsService } from "../services"; import { ImageService, IssuesService, NotificationService, SettingsService } from "../services";
import { DomSanitizer } from "@angular/platform-browser";
import { IIssues, IIssuesChat, IIssueSettings, INewIssueComments, IssueStatus } from "../interfaces"; import { IIssues, IIssuesChat, IIssueSettings, INewIssueComments, IssueStatus } from "../interfaces";
@Component({ @Component({
@ -22,6 +23,8 @@ export class IssueDetailsComponent implements OnInit {
public IssueStatus = IssueStatus; public IssueStatus = IssueStatus;
public isAdmin: boolean; public isAdmin: boolean;
public settings: IIssueSettings; public settings: IIssueSettings;
public backgroundPath: any;
public posterPath: any;
private issueId: number; private issueId: number;
@ -29,7 +32,9 @@ export class IssueDetailsComponent implements OnInit {
private route: ActivatedRoute, private route: ActivatedRoute,
private authService: AuthService, private authService: AuthService,
private settingsService: SettingsService, private settingsService: SettingsService,
private notificationService: NotificationService) { private notificationService: NotificationService,
private imageService: ImageService,
private sanitizer: DomSanitizer) {
this.route.params this.route.params
.subscribe((params: any) => { .subscribe((params: any) => {
this.issueId = parseInt(params.id); this.issueId = parseInt(params.id);
@ -56,8 +61,8 @@ export class IssueDetailsComponent implements OnInit {
providerId: x.providerId, providerId: x.providerId,
userReported: x.userReported, userReported: x.userReported,
}; };
this.setBackground(x);
}); });
this.loadComments(); this.loadComments();
} }
@ -85,4 +90,26 @@ export class IssueDetailsComponent implements OnInit {
private loadComments() { private loadComments() {
this.issueService.getComments(this.issueId).subscribe(x => this.comments = x); this.issueService.getComments(this.issueId).subscribe(x => this.comments = x);
} }
private setBackground(issue: any) {
if (issue.requestType === 1) {
this.imageService.getMovieBackground(issue.providerId).subscribe(x => {
this.backgroundPath = this.sanitizer.bypassSecurityTrustStyle
("url(" + x + ")");
});
this.imageService.getMoviePoster(issue.providerId).subscribe(x => {
this.posterPath = x.toString();
});
} else {
this.imageService.getTvBackground(Number(issue.providerId)).subscribe(x => {
this.backgroundPath = this.sanitizer.bypassSecurityTrustStyle
("url(" + x + ")");
});
this.imageService.getTvPoster(Number(issue.providerId)).subscribe(x => {
this.posterPath = x.toString();
});
}
}
} }

@ -5,7 +5,7 @@ import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { OrderModule } from "ngx-order-pipe"; import { OrderModule } from "ngx-order-pipe";
import { PaginatorModule, SharedModule, TabViewModule } from "primeng/primeng"; import { PaginatorModule, SharedModule, TabViewModule } from "primeng/primeng";
import { IdentityService } from "../services"; import { IdentityService, SearchService } from "../services";
import { AuthGuard } from "../auth/auth.guard"; import { AuthGuard } from "../auth/auth.guard";
@ -43,6 +43,7 @@ const routes: Routes = [
], ],
providers: [ providers: [
IdentityService, IdentityService,
SearchService,
], ],
}) })

@ -207,7 +207,7 @@
<issue-report [movie]="true" [visible]="issuesBarVisible" (visibleChange)="issuesBarVisible = $event;" [title]="issueRequest?.title" <issue-report [movie]="true" [visible]="issuesBarVisible" (visibleChange)="issuesBarVisible = $event;" [title]="issueRequest?.title"
[issueCategory]="issueCategorySelected" [id]="issueRequest?.id" [providerId]=""></issue-report> [issueCategory]="issueCategorySelected" [id]="issueRequest?.id" [providerId]="issueProviderId"></issue-report>
<p-sidebar [(visible)]="filterDisplay" styleClass="ui-sidebar-md side-back side-small"> <p-sidebar [(visible)]="filterDisplay" styleClass="ui-sidebar-md side-back side-small">

@ -105,4 +105,4 @@
<issue-report [movie]="false" [visible]="issuesBarVisible" [title]="issueRequest?.title" <issue-report [movie]="false" [visible]="issuesBarVisible" [title]="issueRequest?.title"
[issueCategory]="issueCategorySelected" [id]="issueRequest?.id" (visibleChange)="issuesBarVisible = $event;"></issue-report> [issueCategory]="issueCategorySelected" [id]="issueRequest?.id" [providerId]="issueProviderId" (visibleChange)="issuesBarVisible = $event;"></issue-report>

@ -105,6 +105,7 @@ export class TvRequestChildrenComponent {
this.issueRequest = req; this.issueRequest = req;
this.issueCategorySelected = catId; this.issueCategorySelected = catId;
this.issuesBarVisible = true; this.issuesBarVisible = true;
this.issueProviderId = req.id.toString();
} }
private removeRequestFromUi(key: IChildRequests) { private removeRequestFromUi(key: IChildRequests) {

@ -90,12 +90,6 @@ export class TvRequestsComponent implements OnInit {
} }
public ngOnInit() { public ngOnInit() {
const profile = <ISonarrProfile>{name:"test",id:1 };
const folder = <ISonarrRootFolder>{path:"testpath", id:1};
this.sonarrProfiles.push(profile);
this.sonarrRootFolders.push(folder);
this.amountToLoad = 1000; this.amountToLoad = 1000;
this.currentlyLoaded = 1000; this.currentlyLoaded = 1000;
this.tvRequests = []; this.tvRequests = [];
@ -204,7 +198,7 @@ export class TvRequestsComponent implements OnInit {
this.loadInit(); this.loadInit();
} }
private loadBackdrop(val: TreeNode): void { private loadBackdrop(val: TreeNode): void {
this.imageService.getTvBanner(val.data.id).subscribe(x => { this.imageService.getTvBanner(val.data.tvDbId).subscribe(x => {
val.data.background = this.sanitizer.bypassSecurityTrustStyle val.data.background = this.sanitizer.bypassSecurityTrustStyle
("url(" + x + ")"); ("url(" + x + ")");
}); });

@ -5,7 +5,7 @@ import { NotificationService } from "../services";
import { RequestService } from "../services"; import { RequestService } from "../services";
import { SearchService } from "../services"; import { SearchService } from "../services";
import { INewSeasonRequests, IRequestEngineResult } from "../interfaces"; import { INewSeasonRequests, IRequestEngineResult, ISeasonsViewModel, ITvRequestViewModel } from "../interfaces";
import { IEpisodesRequests } from "../interfaces"; import { IEpisodesRequests } from "../interfaces";
import { ISearchTvResult } from "../interfaces"; import { ISearchTvResult } from "../interfaces";
@ -46,7 +46,22 @@ export class SeriesInformationComponent implements OnInit {
this.series.requested = true; this.series.requested = true;
this.requestService.requestTv(this.series) const viewModel = <ITvRequestViewModel>{ firstSeason: this.series.firstSeason, latestSeason: this.series.latestSeason, requestAll: this.series.requestAll, tvDbId: this.series.id};
viewModel.seasons = [];
this.series.seasonRequests.forEach((season) => {
const seasonsViewModel = <ISeasonsViewModel>{seasonNumber: season.seasonNumber, episodes: []};
season.episodes.forEach(ep => {
if(!this.series.latestSeason || !this.series.requestAll || !this.series.firstSeason) {
if(ep.requested) {
seasonsViewModel.episodes.push({episodeNumber: ep.episodeNumber});
}
}
});
viewModel.seasons.push(seasonsViewModel);
});
this.requestService.requestTv(viewModel)
.subscribe(x => { .subscribe(x => {
this.result = x as IRequestEngineResult; this.result = x as IRequestEngineResult;
if (this.result.result) { if (this.result.result) {

@ -7,7 +7,7 @@ import { ImageService, NotificationService, RequestService, SearchService} from
import { TreeNode } from "primeng/primeng"; import { TreeNode } from "primeng/primeng";
import { IRequestEngineResult } from "../interfaces"; import { IRequestEngineResult } from "../interfaces";
import { IIssueCategory, ISearchTvResult } from "../interfaces"; import { IIssueCategory, ISearchTvResult, ISeasonsViewModel, ITvRequestViewModel } from "../interfaces";
@Component({ @Component({
selector: "tv-search", selector: "tv-search",
@ -154,7 +154,23 @@ export class TvSearchComponent implements OnInit {
if (this.authService.hasRole("admin") || this.authService.hasRole("AutoApproveMovie")) { if (this.authService.hasRole("admin") || this.authService.hasRole("AutoApproveMovie")) {
searchResult.approved = true; searchResult.approved = true;
} }
this.requestService.requestTv(searchResult)
const viewModel = <ITvRequestViewModel>{ firstSeason: searchResult.firstSeason, latestSeason: searchResult.latestSeason, requestAll: searchResult.requestAll, tvDbId: searchResult.id};
viewModel.seasons = [];
searchResult.seasonRequests.forEach((season) => {
const seasonsViewModel = <ISeasonsViewModel>{seasonNumber: season.seasonNumber, episodes: []};
season.episodes.forEach(ep => {
if(!searchResult.latestSeason || !searchResult.requestAll || !searchResult.firstSeason) {
if(ep.requested) {
seasonsViewModel.episodes.push({episodeNumber: ep.episodeNumber});
}
}
});
viewModel.seasons.push(seasonsViewModel);
});
this.requestService.requestTv(viewModel)
.subscribe(x => { .subscribe(x => {
this.result = x; this.result = x;
if (this.result.result) { if (this.result.result) {

@ -20,10 +20,21 @@ export class ImageService extends ServiceHelpers {
public getTvBanner(tvdbid: number): Observable<string> { public getTvBanner(tvdbid: number): Observable<string> {
return this.http.get<string>(`${this.url}tv/${tvdbid}`, {headers: this.headers}); return this.http.get<string>(`${this.url}tv/${tvdbid}`, {headers: this.headers});
} }
public getMoviePoster(themoviedbid: string): Observable<string> {
return this.http.get<string>(`${this.url}poster/movie/${themoviedbid}`, {headers: this.headers}); public getMoviePoster(movieDbId: string): Observable<string> {
return this.http.get<string>(`${this.url}poster/movie/${movieDbId}`, { headers: this.headers });
} }
public getTvPoster(tvdbid: number): Observable<string> { public getTvPoster(tvdbid: number): Observable<string> {
return this.http.get<string>(`${this.url}poster/tv/${tvdbid}`, {headers: this.headers}); return this.http.get<string>(`${this.url}poster/tv/${tvdbid}`, { headers: this.headers });
}
public getMovieBackground(movieDbId: string): Observable<string> {
return this.http.get<string>(`${this.url}background/movie/${movieDbId}`, { headers: this.headers });
} }
public getTvBackground(tvdbid: number): Observable<string> {
return this.http.get<string>(`${this.url}background/tv/${tvdbid}`, { headers: this.headers });
}
} }

@ -7,7 +7,7 @@ import { Observable } from "rxjs/Rx";
import { TreeNode } from "primeng/primeng"; import { TreeNode } from "primeng/primeng";
import { IRequestEngineResult } from "../interfaces"; import { IRequestEngineResult } from "../interfaces";
import { IChildRequests, IFilter, IMovieRequestModel, IMovieRequests, IMovieUpdateModel, ITvRequests, ITvUpdateModel } from "../interfaces"; import { IChildRequests, IFilter, IMovieRequestModel, IMovieRequests, IMovieUpdateModel, ITvRequests, ITvUpdateModel } from "../interfaces";
import { ISearchTvResult } from "../interfaces"; import { ITvRequestViewModel } from "../interfaces";
import { ServiceHelpers } from "./service.helpers"; import { ServiceHelpers } from "./service.helpers";
@Injectable() @Injectable()
@ -20,7 +20,7 @@ export class RequestService extends ServiceHelpers {
return this.http.post<IRequestEngineResult>(`${this.url}Movie/`, JSON.stringify(movie), {headers: this.headers}); return this.http.post<IRequestEngineResult>(`${this.url}Movie/`, JSON.stringify(movie), {headers: this.headers});
} }
public requestTv(tv: ISearchTvResult): Observable<IRequestEngineResult> { public requestTv(tv: ITvRequestViewModel): Observable<IRequestEngineResult> {
return this.http.post<IRequestEngineResult>(`${this.url}TV/`, JSON.stringify(tv), {headers: this.headers}); return this.http.post<IRequestEngineResult>(`${this.url}TV/`, JSON.stringify(tv), {headers: this.headers});
} }

@ -33,12 +33,12 @@
</div> </div>
</div> </div>
<div class="form-group"> <!-- <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<input type="checkbox" id="enable" [(ngModel)]="settings.recentlyAddedPage" [checked]="settings.recentlyAddedPage"> <input type="checkbox" id="enable" [(ngModel)]="settings.recentlyAddedPage" [checked]="settings.recentlyAddedPage">
<label for="enable">Enable Recently Added Page</label> <label for="enable">Enable Recently Added Page</label>
</div> </div>
</div> </div> -->
<div class="form-group"> <div class="form-group">
<label for="logo" class="control-label">Custom Logo</label> <label for="logo" class="control-label">Custom Logo</label>

@ -1,6 +1,6 @@
<settings-menu></settings-menu> <settings-menu></settings-menu>
<wiki [url]="'https://github.com/tidusjar/Ombi/wiki/Newsletter'"></wiki> <wiki [url]="'https://github.com/tidusjar/Ombi/wiki/Newsletter-Settings'"></wiki>
<div *ngIf="settings"> <div *ngIf="settings">
<fieldset> <fieldset>
<legend>Newsletter</legend> <legend>Newsletter</legend>

@ -15,6 +15,8 @@ export class IssuesReportComponent {
@Input() public issueCategory: IIssueCategory; @Input() public issueCategory: IIssueCategory;
@Input() public movie: boolean; @Input() public movie: boolean;
@Input() public providerId: string; @Input() public providerId: string;
@Input() public background: string;
@Input() public posterPath: string;
@Output() public visibleChange = new EventEmitter<boolean>(); @Output() public visibleChange = new EventEmitter<boolean>();

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Ombi.Api.FanartTv; using Ombi.Api.FanartTv;
using Ombi.Store.Repository; using Ombi.Store.Repository;
using System; using System;
@ -71,7 +71,7 @@ namespace Ombi.Controllers
if (images.movieposter?.Any() ?? false) if (images.movieposter?.Any() ?? false)
{ {
var enImage = images.movieposter.Where(x => x.lang == "en").OrderByDescending(x => x.likes).Select(x => x.url).FirstOrDefault(); var enImage = images.movieposter.Where(x => x.lang == "en").OrderByDescending(x => x.likes).Select(x => x.url).FirstOrDefault();
if (enImage == null) if (enImage == null)
{ {
return images.movieposter.OrderByDescending(x => x.likes).Select(x => x.url).FirstOrDefault(); return images.movieposter.OrderByDescending(x => x.likes).Select(x => x.url).FirstOrDefault();
@ -117,6 +117,56 @@ namespace Ombi.Controllers
return string.Empty; return string.Empty;
} }
[HttpGet("background/movie/{movieDbId}")]
public async Task<string> GetMovieBackground(string movieDbId)
{
var key = await _cache.GetOrAdd(CacheKeys.FanartTv, async () => await Config.Get(Store.Entities.ConfigurationTypes.FanartTv), DateTime.Now.AddDays(1));
var images = await FanartTvApi.GetMovieImages(movieDbId, key.Value);
if (images == null)
{
return string.Empty;
}
if (images.moviebackground?.Any() ?? false)
{
var enImage = images.moviebackground.Where(x => x.lang == "en").OrderByDescending(x => x.likes).Select(x => x.url).FirstOrDefault();
if (enImage == null)
{
return images.moviebackground.OrderByDescending(x => x.likes).Select(x => x.url).FirstOrDefault();
}
return enImage;
}
return string.Empty;
}
[HttpGet("background/tv/{tvdbid}")]
public async Task<string> GetTvBackground(int tvdbid)
{
var key = await _cache.GetOrAdd(CacheKeys.FanartTv, async () => await Config.Get(Store.Entities.ConfigurationTypes.FanartTv), DateTime.Now.AddDays(1));
var images = await FanartTvApi.GetTvImages(tvdbid, key.Value);
if (images == null)
{
return string.Empty;
}
if (images.showbackground?.Any() ?? false)
{
var enImage = images.showbackground.Where(x => x.lang == "en").OrderByDescending(x => x.likes).Select(x => x.url).FirstOrDefault();
if (enImage == null)
{
return images.showbackground.OrderByDescending(x => x.likes).Select(x => x.url).FirstOrDefault();
}
return enImage;
}
return string.Empty;
}
[HttpGet("background")] [HttpGet("background")]
public async Task<object> GetBackgroundImage() public async Task<object> GetBackgroundImage()
{ {
@ -141,7 +191,7 @@ namespace Ombi.Controllers
movieUrl = result.moviebackground[0].url; movieUrl = result.moviebackground[0].url;
} }
if(tvArray.Any()) if (tvArray.Any())
{ {
var item = rand.Next(tvArray.Length); var item = rand.Next(tvArray.Length);
var result = await FanartTvApi.GetTvImages(tvArray[item], key.Value); var result = await FanartTvApi.GetTvImages(tvArray[item], key.Value);

@ -174,7 +174,7 @@ namespace Ombi.Controllers
/// <param name="tv">The tv.</param> /// <param name="tv">The tv.</param>
/// <returns></returns> /// <returns></returns>
[HttpPost("tv")] [HttpPost("tv")]
public async Task<RequestEngineResult> RequestTv([FromBody] SearchTvShowViewModel tv) public async Task<RequestEngineResult> RequestTv([FromBody] TvRequestViewModel tv)
{ {
return await TvRequestEngine.RequestTvShow(tv); return await TvRequestEngine.RequestTvShow(tv);
} }

@ -64,26 +64,16 @@ O:::::::OOO:::::::Om::::m m::::m m::::mb:::::bbbbbb::::::bi::::::i
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>@appName</title> <title>@appName</title>
<base href="/@baseUrl"/> <base href="/@baseUrl"/>
<link rel="SHORTCUT ICON" href="~/images/favicon/favicon.ico"/>
<link rel="icon" href="~/images/favicon/favicon.ico" type="image/ico"/> <link rel="apple-touch-icon" sizes="180x180" href="~/images/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="~/images/favicon/favicon-32x32.png">
<link rel="apple-touch-icon" sizes="57x57" href="~/images/favicon/apple-icon-57x57.png"/> <link rel="icon" type="image/png" sizes="16x16" href="~/images/favicon/favicon-16x16.png">
<link rel="apple-touch-icon" sizes="60x60" href="~/images/favicon/apple-icon-60x60.png"/> <link rel="manifest" href="~/images/favicon/site.webmanifest">
<link rel="apple-touch-icon" sizes="72x72" href="~/images/favicon/apple-icon-72x72.png"/> <link rel="mask-icon" href="~/images/favicon/safari-pinned-tab.svg" color="#df691a">
<link rel="apple-touch-icon" sizes="76x76" href="~/images/favicon/apple-icon-76x76.png"/> <link rel="shortcut icon" href="~/images/favicon/favicon.ico">
<link rel="apple-touch-icon" sizes="114x114" href="~/images/favicon/apple-icon-114x114.png"/> <meta name="msapplication-TileColor" content="#df691a">
<link rel="apple-touch-icon" sizes="120x120" href="~/images/favicon/apple-icon-120x120.png"/> <meta name="msapplication-config" content="~/images/favicon/browserconfig.xml">
<link rel="apple-touch-icon" sizes="144x144" href="~/images/favicon/apple-icon-144x144.png"/> <meta name="theme-color" content="#df691a">
<link rel="apple-touch-icon" sizes="152x152" href="~/images/favicon/apple-icon-152x152.png"/>
<link rel="apple-touch-icon" sizes="180x180" href="~/images/favicon/apple-icon-180x180.png"/>
<link rel="icon" type="image/png" sizes="192x192" href="~/images/favicon/android-icon-192x192.png"/>
<link rel="icon" type="image/png" sizes="32x32" href="~/images/favicon/favicon-32x32.png"/>
<link rel="icon" type="image/png" sizes="96x96" href="~/images/favicon/favicon-96x96.png"/>
<link rel="icon" type="image/png" sizes="16x16" href="~/images/favicon/favicon-16x16.png"/>
<link rel="manifest" href="~/images/favicon/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff"/>
<meta name="msapplication-TileImage" content="~/images/favicon/ms-icon-144x144.png"/>
<meta name="theme-color" content="#ffffff"/>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -1,2 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig> <browserconfig>
<msapplication>
<tile>
<square150x150logo src="/images/favicon/mstile-150x150.png"/>
<TileColor>#df691a</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 738 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 15 KiB

@ -1,41 +0,0 @@
{
"name": "App",
"icons": [
{
"src": "\/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "\/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "\/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "\/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "\/android-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "\/android-icon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1 @@
<svg version="1" xmlns="http://www.w3.org/2000/svg" width="1365.333" height="1365.333" viewBox="0 0 1024.000000 1024.000000"><path d="M487.2 71c-140.4 9.8-263.6 80.9-341 196.7-39.5 59-63.1 124.3-71.8 198.8-2.5 21.1-2.5 71.9 0 93 6.5 56 19.9 101.7 43.7 150 21.9 44.5 48.5 81.2 85.3 118 47.8 47.7 105.8 83.8 168.9 105 160.4 53.9 335.7 13.4 454.3-105 36.8-36.8 63.4-73.5 85.3-118 23.8-48.3 37.2-94 43.7-150 2.5-21.1 2.5-71.9 0-93-5.2-44.4-15.1-83.6-30.9-122.2-32.3-78.8-88.7-148.4-159.2-196.6-61.7-42.1-130.7-67.4-205.5-75.2-17.9-1.9-56.3-2.7-72.8-1.5zM391 283c11.3 3.2 21.2 8.6 31.4 17.1 5 4.2 28.7 27.4 52.6 51.5 46.2 46.6 48 48.8 54.4 64.8 4.3 10.7 5.9 19.7 5.8 33.1 0 13.9-1.9 22.6-7.8 35.4l-3.7 8.2 3.8 3.9 3.8 3.9 4.6-2.3c19-9.6 43.2-12.1 63.1-6.6 9.7 2.7 22.1 8.7 29.6 14.4 3.2 2.4 27.1 25.7 52.9 51.8 41.7 42 47.5 48.2 51.4 55.1 8.8 15.7 12.8 32.1 11.7 48.7-.7 10.7-3.4 23-6.7 30.5-6.2 14.2-20 30.1-34.1 39.2-25.2 16.3-57.3 17.7-85.8 3.7l-11.5-5.7-50.1-50.1c-54.7-54.7-56.7-57.1-62.5-73.9-4-11.5-5.2-21.2-4.6-34.7.7-13.3 2.3-20.2 7.7-31.7l3.8-8.1-3.7-3.6-3.7-3.7-7.9 3.6c-27.5 12.6-61.6 10-86.2-6.5-6.6-4.5-98.4-96-103.9-103.7-10.6-14.6-15.7-31.5-15.7-51.3 0-14.6 1.6-22.4 7.3-34.6 12.5-27.1 34.7-44.6 63.5-50.3 8.4-1.7 31.9-.6 40.5 1.9zm179 66c1.9 1.9 2 3.3 2 35.2 0 32.7 0 33.2-2.2 35.5-3.1 3.3-7.7 3.1-11.1-.5l-2.7-2.8V384c0-31.9 0-32.4 2.2-34.7 2.8-3 8.9-3.2 11.8-.3zm74.1 30.9c5.6 5.6 5.3 6.1-20.3 31.8-25.6 25.6-26.9 26.5-31.9 21.7-2.1-2-2.9-3.7-2.9-6.3 0-3.4 1.5-5.1 22.3-25.7 12.2-12.2 23.1-22.7 24.2-23.3 3.3-1.7 5.5-1.3 8.6 1.8zm29.9 73.6c2.1 1.1 3.1 2.5 3.6 5.2.6 3.3.3 4-2.5 6.5L672 468h-64.4l-2.8-2.7c-3.6-3.4-3.7-7.5-.3-10.8l2.4-2.5H639c26.6 0 32.6.3 35 1.5z"/><path d="M358.4 324.5c-28.1 6.1-42.3 34.4-30.7 61 2.4 5.4 7.3 10.6 48.1 51.7 48.4 48.6 50 50 62.7 53.3 7.1 1.8 14.4 1.9 18 .3 2.5-1.1 2.3-1.4-20.8-24.7-21.3-21.5-23.5-24-24.6-28.3-3.3-13.7 6.7-26.8 20.6-26.8 2.9 0 7 .7 9.1 1.6 2.4.9 11.8 9.5 26.3 24l22.6 22.5 1.3-3.1c2.8-6.6-.7-22.3-6.9-31.6-2.3-3.4-22.5-24.3-47.8-49.4-47-46.8-48.3-47.8-61-50.5-8.1-1.7-9-1.7-16.9 0zM567.8 533.7c-1 .2-1.8.7-1.8 1.1 0 .3 13.2 13.9 29.4 30.2 28.2 28.2 29.5 29.7 31 35.1 2 6.8 1.1 12.1-3 18-2.9 4.2-8.8 8.2-13.8 9.3-1.5.4-5.4.2-8.5-.5l-5.6-1.1-30-29.9c-16.6-16.4-30.3-29.9-30.7-29.9-.3 0-1.1 1.9-1.7 4.3-1.9 7.6 1.2 20.8 6.9 29.7 4.2 6.4 90.5 92.1 96 95.2 16.7 9.5 36.8 7 51.4-6.3 13.7-12.5 17.2-33 8.5-50.8-2.8-6-7.6-11.1-48.8-52.2-48.8-48.7-49.3-49.1-61.9-51.8-5.2-1.1-14-1.3-17.4-.4z"/></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@ -0,0 +1,19 @@
{
"name": "Ombi",
"short_name": "Ombi",
"icons": [
{
"src": "/images/favicon/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/images/favicon/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#df691a",
"background_color": "#df691a",
"display": "standalone"
}
Loading…
Cancel
Save