docker support and more, redesign the episodes

pull/1425/head
tidusjar 8 years ago
parent b829b10b14
commit fa5efb6c66

@ -52,27 +52,47 @@ namespace Ombi.Core.Engine
RequestAll = tv.RequestAll
};
var episodes = await TvApi.EpisodeLookup(showInfo.id);
foreach (var e in episodes)
{
var season = model.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == e.season);
season?.Episodes.Add(new EpisodesRequested
{
Url = e.url,
Title = e.name,
AirDate = DateTime.Parse(e.airstamp),
EpisodeNumber = e.number,
});
}
if (tv.LatestSeason)
{
var latest = showInfo.Season.OrderBy(x => x).FirstOrDefault();
model.SeasonRequests = showInfo.Season.Any()
? new List<SeasonRequestModel> {new SeasonRequestModel
foreach (var modelSeasonRequest in model.SeasonRequests)
{
if (modelSeasonRequest.SeasonNumber == latest.SeasonNumber)
{
SeasonNumber = latest.SeasonNumber,
Episodes = latest.EpisodeNumber
}}
: new List<SeasonRequestModel>();
foreach (var episodesRequested in modelSeasonRequest.Episodes)
{
episodesRequested.Requested = true;
}
}
}
}
if (tv.FirstSeason)
{
var first = showInfo.Season.OrderByDescending(x => x).FirstOrDefault();
model.SeasonRequests = showInfo.Season.Any()
? new List<SeasonRequestModel> {new SeasonRequestModel
foreach (var modelSeasonRequest in model.SeasonRequests)
{
if (modelSeasonRequest.SeasonNumber == first.SeasonNumber)
{
SeasonNumber = first.SeasonNumber,
Episodes = first.EpisodeNumber
}}
: new List<SeasonRequestModel>();
foreach (var episodesRequested in modelSeasonRequest.Episodes)
{
episodesRequested.Requested = true;
}
}
}
}

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Threading.Tasks;
@ -48,10 +49,30 @@ namespace Ombi.Core.Engine
return null;
}
public async Task<SearchTvShowViewModel> GetShowInformation(int tvdbId)
public async Task<SearchTvShowViewModel> GetShowInformation(int tvmazeId)
{
var show = await TvMazeApi.ShowLookupByTheTvDbId(tvdbId);
return Mapper.Map<SearchTvShowViewModel>(show);
var show = await TvMazeApi.ShowLookup(tvmazeId);
var episodes = await TvMazeApi.EpisodeLookup(show.id);
var mapped = Mapper.Map<SearchTvShowViewModel>(show);
foreach (var e in episodes)
{
var season = mapped.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == e.season);
season?.Episodes.Add(new EpisodesRequested
{
Url = e.url,
Title = e.name,
AirDate = DateTime.Parse(e.airstamp),
EpisodeNumber = e.number,
});
}
var existingRequests = await GetTvRequests();
var plexSettings = await PlexSettings.GetSettingsAsync();
var embySettings = await EmbySettings.GetSettingsAsync();
return ProcessResult(mapped, existingRequests, plexSettings, embySettings);
}
//public async Task<IEnumerable<SearchTvShowViewModel>> Popular()
@ -118,11 +139,28 @@ namespace Ombi.Core.Engine
var tvdbid = item.Id;
if (existingRequests.ContainsKey(tvdbid))
{
var dbt = existingRequests[tvdbid];
var existingRequest = existingRequests[tvdbid];
item.Requested = true;
item.SeasonRequests = dbt.SeasonRequests.ToList();
item.Approved = dbt.Approved;
item.Approved = existingRequest.Approved;
// Let's modify the seasonsrequested to reflect what we have requested...
foreach (var season in item.SeasonRequests)
{
// Find the existing request season
var existingSeason =
existingRequest.SeasonRequests.FirstOrDefault(x => x.SeasonNumber == season.SeasonNumber);
foreach (var ep in existingSeason.Episodes)
{
// Find the episode from what we are searching
var episodeSearching = season.Episodes.FirstOrDefault(x => x.EpisodeNumber == ep.EpisodeNumber);
episodeSearching.Requested = ep.Requested;
episodeSearching.Available = ep.Available;
episodeSearching.Approved = ep.Approved;
}
}
}
//if (sonarrCached.Select(x => x.TvdbId).Contains(tvdbid) || sickRageCache.Contains(tvdbid))
// // compare to the sonarr/sickrage db

@ -59,7 +59,19 @@ namespace Ombi.Core.Models.Requests
public class SeasonRequestModel
{
public int SeasonNumber { get; set; }
public List<int> Episodes { get; set; }
public List<EpisodesRequested> Episodes { get; set; } = new List<EpisodesRequested>();
}
public class EpisodesRequested
{
public int EpisodeNumber { get; set; }
public string Title { get; set; }
public DateTime AirDate { get; set; }
public string Url { get; set; }
public bool Requested { get; set; }
public string Status { get; set; }
public bool Available { get; set; }
public bool Approved { get; set; }
}
}

@ -6,7 +6,7 @@ namespace Ombi.Core.Models.Search
public class SearchTvShowViewModel : SearchViewModel
{
public int Id { get; set; }
public string SeriesName { get; set; }
public string Title { get; set; }
public List<string> Aliases { get; set; }
public string Banner { get; set; }
public int SeriesId { get; set; }
@ -48,5 +48,6 @@ namespace Ombi.Core.Models.Search
public bool FirstSeason { get; set; }
public bool LatestSeason { get; set; }
}
}

@ -24,12 +24,10 @@ namespace Ombi.Mapping.Profiles
.ForMember(dest => dest.Rating, opts => opts.MapFrom(src => src.score.ToString(CultureInfo.CurrentUICulture)))
.ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.show.runtime.ToString()))
.ForMember(dest => dest.SeriesId, opts => opts.MapFrom(src => src.show.id))
.ForMember(dest => dest.SeriesName, opts => opts.MapFrom(src => src.show.name))
.ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.show.name))
.ForMember(dest => dest.Banner, opts => opts.MapFrom(src => !string.IsNullOrEmpty(src.show.image.medium) ? src.show.image.medium.Replace("http", "https") : string.Empty))
.ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.show.status));
CreateMap<TvMazeCustomSeason, SeasonRequestModel>()
.ConstructUsing(x => new SeasonRequestModel { Episodes = x.EpisodeNumber, SeasonNumber = x.SeasonNumber });
CreateMap<TvMazeShow, SearchTvShowViewModel>()
.ForMember(dest => dest.Id, opts => opts.MapFrom(src => src.externals.thetvdb))
.ForMember(dest => dest.FirstAired, opts => opts.MapFrom(src => src.premiered))
@ -40,7 +38,7 @@ namespace Ombi.Mapping.Profiles
.ForMember(dest => dest.Rating, opts => opts.MapFrom(src => src.rating.ToString()))
.ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.runtime.ToString(CultureInfo.CurrentUICulture)))
.ForMember(dest => dest.SeriesId, opts => opts.MapFrom(src => src.id))
.ForMember(dest => dest.SeriesName, opts => opts.MapFrom(src => src.name))
.ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.name))
.ForMember(dest => dest.Banner, opts => opts.MapFrom(src => !string.IsNullOrEmpty(src.image.medium) ? src.image.medium.Replace("http", "https") : string.Empty))
.ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.status))
.ForMember(dest => dest.SeasonRequests, opts => opts.MapFrom(src => src.Season));
@ -53,7 +51,7 @@ namespace Ombi.Mapping.Profiles
// .ForMember(dest => dest.Overview, opts => opts.MapFrom(src => src.Overview.RemoveHtml()))
// .ForMember(dest => dest.Rating, opts => opts.MapFrom(src => src.Rating.ToString()))
// .ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.Runtime.ToString()))
// .ForMember(dest => dest.SeriesName, opts => opts.MapFrom(src => src.Title))
// .ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.Title))
// .ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.Status.DisplayName))
// .ForMember(dest => dest.Trailer, opts => opts.MapFrom(src => src.Trailer))
// .ForMember(dest => dest.Homepage, opts => opts.MapFrom(src => src.Homepage));
@ -66,7 +64,7 @@ namespace Ombi.Mapping.Profiles
// .ForMember(dest => dest.Overview, opts => opts.MapFrom(src => src.Show.Overview.RemoveHtml()))
// .ForMember(dest => dest.Rating, opts => opts.MapFrom(src => src.Show.Rating.ToString()))
// .ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.Show.Runtime.ToString()))
// .ForMember(dest => dest.SeriesName, opts => opts.MapFrom(src => src.Show.Title))
// .ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.Show.Title))
// .ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.Show.Status.DisplayName))
// .ForMember(dest => dest.Trailer, opts => opts.MapFrom(src => src.Show.Trailer))
// .ForMember(dest => dest.Homepage, opts => opts.MapFrom(src => src.Show.Homepage));
@ -79,7 +77,7 @@ namespace Ombi.Mapping.Profiles
// .ForMember(dest => dest.Overview, opts => opts.MapFrom(src => src.Show.Overview.RemoveHtml()))
// .ForMember(dest => dest.Rating, opts => opts.MapFrom(src => src.Show.Rating.ToString()))
// .ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.Show.Runtime.ToString()))
// .ForMember(dest => dest.SeriesName, opts => opts.MapFrom(src => src.Show.Title))
// .ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.Show.Title))
// .ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.Show.Status.DisplayName))
// .ForMember(dest => dest.Trailer, opts => opts.MapFrom(src => src.Show.Trailer))
// .ForMember(dest => dest.Homepage, opts => opts.MapFrom(src => src.Show.Homepage));
@ -92,7 +90,7 @@ namespace Ombi.Mapping.Profiles
// .ForMember(dest => dest.Overview, opts => opts.MapFrom(src => src.Show.Overview.RemoveHtml()))
// .ForMember(dest => dest.Rating, opts => opts.MapFrom(src => src.Show.Rating.ToString()))
// .ForMember(dest => dest.Runtime, opts => opts.MapFrom(src => src.Show.Runtime.ToString()))
// .ForMember(dest => dest.SeriesName, opts => opts.MapFrom(src => src.Show.Title))
// .ForMember(dest => dest.Title, opts => opts.MapFrom(src => src.Show.Title))
// .ForMember(dest => dest.Status, opts => opts.MapFrom(src => src.Show.Status.DisplayName))
// .ForMember(dest => dest.Trailer, opts => opts.MapFrom(src => src.Show.Trailer))
// .ForMember(dest => dest.Homepage, opts => opts.MapFrom(src => src.Show.Homepage));

@ -15,7 +15,9 @@ namespace Ombi.Store.Context
Database.EnsureCreated();
Database.Migrate();
// Run Script
Database.ExecuteSqlCommand(Sql.SqlTables, 0);
}
public DbSet<RequestBlobs> Requests { get; set; }

@ -8,7 +8,6 @@ namespace Ombi.Store.Entities
public int ProviderId { get; set; }
public byte[] Content { get; set; }
public RequestType Type { get; set; }
public string MusicId { get; set; }
}
public enum RequestType

@ -17,4 +17,17 @@
<ItemGroup>
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Sql.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Sql.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Sql.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Sql.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

@ -0,0 +1,89 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Ombi.Store {
using System;
using System.Reflection;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Sql {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Sql() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Ombi.Store.Sql", typeof(Sql).GetTypeInfo().Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to CREATE TABLE IF NOT EXISTS GlobalSettings
///(
/// Id INTEGER PRIMARY KEY AUTOINCREMENT,
/// SettingsName varchar(50) NOT NULL,
/// Content BLOB NOT NULL
///);
///
///CREATE TABLE IF NOT EXISTS PlexContent
///(
/// Id INTEGER PRIMARY KEY AUTOINCREMENT,
/// Title varchar(50) NOT NULL,
/// ProviderId varchar(50) NOT NULL,
/// Url varchar(100) NOT NULL,
/// Key varchar(50) NOT NULL,
/// AddedAt varchar(50) NOT NULL,
/// Type INTEGER NOT NULL,
/// Relea [rest of string was truncated]&quot;;.
/// </summary>
internal static string SqlTables {
get {
return ResourceManager.GetString("SqlTables", resourceCulture);
}
}
}
}

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="SqlTables" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>SqlTables.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
</root>

@ -0,0 +1,49 @@
CREATE TABLE IF NOT EXISTS GlobalSettings
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
SettingsName varchar(50) NOT NULL,
Content BLOB NOT NULL
);
CREATE TABLE IF NOT EXISTS PlexContent
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Title varchar(50) NOT NULL,
ProviderId varchar(50) NOT NULL,
Url varchar(100) NOT NULL,
Key varchar(50) NOT NULL,
AddedAt varchar(50) NOT NULL,
Type INTEGER NOT NULL,
ReleaseYear varchar(100) NOT NULL
);
CREATE TABLE IF NOT EXISTS SeasonsContent
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
SeasonNumber INTEGER NOT NULL,
SeasonKey INTEGER NOT NULL,
ParentKey INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS RequestBlobs
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
ProviderId INTEGER NOT NULL,
Content BLOB NOT NULL,
Type INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS Users
(
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Username VARCHAR(100) NOT NULL,
Alias VARCHAR(100) NULL,
ClaimsSerialized BLOB NOT NULL,
EmailAddress VARCHAR(100) NULL,
Password VARCHAR(100) NULL,
Salt BLOB NULL,
UserType INTEGER NOT NULL
);

@ -0,0 +1,39 @@
{
"version": "0.2.0",
"configurations": [
{
"name":".NET Core Docker Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "composeForDebug",
"cwd": "/app",
"program": "/app/Ombi.dll",
"sourceFileMap": {
"/app": "${workspaceRoot}"
},
"launchBrowser": {
"enabled": true,
"args": "${auto-detect-url}",
"windows": {
"command": "cmd.exe",
"args": "/C start ${auto-detect-url}"
},
"osx": {
"command": "open"
}
},
"pipeTransport": {
"pipeProgram": "/bin/bash",
"pipeCwd": "${workspaceRoot}",
"pipeArgs": [ "-c", "./dockerTask.sh startDebugging" ],
"windows": {
"pipeProgram": "${env.windir}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
"pipeCwd": "${workspaceRoot}",
"pipeArgs": [ ".\\dockerTask.ps1", "-StartDebugging" ]
}
}
}
]
}

@ -0,0 +1,5 @@
{
"files.associations": {
"dockerfile.*": "dockerfile"
}
}

@ -0,0 +1,66 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "0.1.0",
"windows": {
"command": "powershell",
"options": {
"cwd": "${workspaceRoot}"
},
"tasks": [
{
"taskName": "build",
"suppressTaskName": true,
"args": ["-ExecutionPolicy", "RemoteSigned", ".\\dockerTask.ps1", "-Build", "-Environment", "debug" ],
"isBuildCommand": true,
"showOutput": "always",
"echoCommand": true
},
{
"taskName": "compose",
"suppressTaskName": true,
"args": ["-ExecutionPolicy", "RemoteSigned", ".\\dockerTask.ps1", "-Compose", "-Environment", "debug" ],
"isBuildCommand": false,
"showOutput": "always",
"echoCommand": true
},
{
"taskName": "composeForDebug",
"suppressTaskName": true,
"args": ["-ExecutionPolicy", "RemoteSigned", ".\\dockerTask.ps1", "-ComposeForDebug", "-Environment", "debug" ],
"isBuildCommand": false,
"showOutput": "always",
"echoCommand": true
}
]
},
"osx": {
"command": "/bin/bash",
"options": {
"cwd": "${workspaceRoot}"
},
"tasks": [
{
"taskName": "build",
"suppressTaskName": true,
"args": [ "-c", "./dockerTask.sh build debug" ],
"isBuildCommand": true,
"showOutput": "always"
},
{
"taskName": "compose",
"suppressTaskName": true,
"args": [ "-c", "./dockerTask.sh compose debug" ],
"isBuildCommand": false,
"showOutput": "always"
},
{
"taskName": "composeForDebug",
"suppressTaskName": true,
"args": [ "-c", "./dockerTask.sh composeForDebug debug" ],
"isBuildCommand": false,
"showOutput": "always"
}
]
}
}

@ -66,10 +66,10 @@ namespace Ombi.Controllers
return await TvEngine.Search(searchTerm);
}
[HttpGet("tv/seasons/{tvdbId}")]
public async Task<IEnumerable<int>> GetSeasons(int tvdbId)
[HttpGet("tv/info/{tvdbId}")]
public async Task<SearchTvShowViewModel> GetShowInfo(int tvdbId)
{
return await TvEngine.GetSeasons(tvdbId);
return await TvEngine.GetShowInformation(tvdbId);
}
//[HttpGet("tv/popular")]

@ -0,0 +1,8 @@
FROM microsoft/dotnet:1.1-sdk-msbuild
ARG source=./bin/Release/netcoreapp1.1/publish
WORKDIR /app
COPY $source .
ENV ASPNETCORE_URLS http://*:5000
EXPOSE 5000
ENTRYPOINT ["dotnet", "Ombi.dll"]
COPY . /app

@ -89,6 +89,15 @@
<Content Update="wwwroot\app\search\moviesearch - Copy.component.ts">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="wwwroot\app\search\tvsearch - Copy.component.js">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="wwwroot\app\search\tvsearch - Copy.component.js.map">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="wwwroot\app\search\tvsearch - Copy.component.ts">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="wwwroot\app\usermanagement\request.component.js">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>

@ -0,0 +1,21 @@
SERVICE="Ombi"
# change directory to location of project.json
pushd ./
# run dotnet publish, specify release build
dotnet publish -c Release
# equivalent to cd .. (go back to previous directory)
#popd
# Create a docker image tagged with the name of the project:latest
docker build -t "$SERVICE":latest .
# Check to see if this container exists.
CONTAINER=`docker ps --all | grep "$SERVICE"`
# if it doesn't, then just run this.
if [ -z "$CONTAINER" ]; then
docker run -i -p 8000:5000 --name $SERVICE -t $SERVICE:latest
# if it does exist; nuke it and then run the new one
else
docker rm $SERVICE
docker run -i -p 8000:5000 --name $SERVICE -t $SERVICE:latest
fi
read -p "Press enter to continue"

@ -15,6 +15,7 @@ import { InfiniteScrollModule } from 'ngx-infinite-scroll'
import { SearchComponent } from './search/search.component';
import { MovieSearchComponent } from './search/moviesearch.component';
import { TvSearchComponent } from './search/tvsearch.component';
import { SeriesInformationComponent } from './search/seriesinformation.component';
// Request
import { RequestComponent } from './requests/request.component';
@ -50,6 +51,7 @@ const routes: Routes = [
{ path: '*', component: PageNotFoundComponent },
{ path: '', redirectTo: '/search', pathMatch: 'full' },
{ path: 'search', component: SearchComponent, canActivate: [AuthGuard] },
{ path: 'search/show/:id', component: SeriesInformationComponent, canActivate: [AuthGuard] },
{ path: 'requests', component: RequestComponent, canActivate: [AuthGuard] },
{ path: 'login', component: LoginComponent },
{ path: 'landingpage', component: LandingPageComponent },
@ -84,7 +86,8 @@ const routes: Routes = [
LandingPageComponent,
UserManagementComponent,
MovieRequestsComponent,
TvRequestsComponent
TvRequestsComponent,
SeriesInformationComponent
],
providers: [
SearchService,

@ -39,7 +39,17 @@ export interface ITvRequestModel extends IMediaBase {
export interface ISeasonRequests
{
seasonNumber: number,
episodes:number[],
episodes: IEpisodesRequested[],
}
export interface IEpisodesRequested {
episodeNumber: number,
title: string,
airDate: Date,
url: string,
requested: boolean,
status: string,
available:boolean
}

@ -0,0 +1,64 @@
<div *ngIf="series">
<div *ngFor="let season of series.seasonsRequested">
<h2>Season: {{season.seasonNumber}}</h2>
<table class="table table-striped table-hover table-responsive table-condensed">
<thead>
<tr>
<th>
<a>
#
</a>
</th>
<th>
<a>
Title
</a>
</th>
<th>
<a>
Air Date
</a>
</th>
<th>
<a>
Status
</a>
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let ep of season.episodes">
<td>
{{ep.episodeNumber}}
</td>
<td>
{{ep.title}}
</td>
<td>
{{ep.airDate | date: 'dd/MM/yyyy' }}
</td>
<td>
<span *ngIf="result.available" class="label label-success">Available</span>
<span *ngIf="result.approved && !result.available" class="label label-info">Processing Request</span>
<div *ngIf="result.requested && !result.available; then requested else notRequested"></div>
<template #requested>
<span *ngIf="!result.available" class="label label-warning">Pending Approval</span>
</template>
<template #notRequested>
<span *ngIf="!result.available" class="label label-danger">Not Yet Requested</span>
</template>
</td>
<td>
<a (click)="edit(u)" class="btn btn-sm btn-info-outline">Details/Edit</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>

@ -0,0 +1,67 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subject } from 'rxjs/Subject';
import "rxjs/add/operator/takeUntil";
import { SearchService } from '../services/search.service';
import { RequestService } from '../services/request.service';
import { NotificationService } from '../services/notification.service';
import { ISearchTvResult } from '../interfaces/ISearchTvResult';
import { IRequestEngineResult } from '../interfaces/IRequestEngineResult';
@Component({
selector: 'ombi',
moduleId: module.id,
templateUrl: './seriesinformation.component.html'
})
export class SeriesInformationComponent implements OnInit, OnDestroy {
constructor(private searchService: SearchService, private route: ActivatedRoute,
private requestService: RequestService, private notificationService: NotificationService) {
this.route.params
.takeUntil(this.subscriptions)
.subscribe(params => {
this.seriesId = +params['id']; // (+) converts string 'id' to a number
});
}
private subscriptions = new Subject<void>();
result : IRequestEngineResult;
seriesId: number;
series: ISearchTvResult;
ngOnInit(): void {
this.searchService.getShowInformation(this.seriesId)
.takeUntil(this.subscriptions)
.subscribe(x => {
this.series = x;
});
}
request() {
this.series.requested = true;
this.requestService.requestTv(this.series)
.takeUntil(this.subscriptions)
.subscribe(x => {
this.result = x;
if (this.result.requestAdded) {
this.notificationService.success("Request Added",
`Request for ${this.series.seriesName} has been added successfully`);
} else {
this.notificationService.warning("Request Added", this.result.message);
}
});
}
ngOnDestroy(): void {
this.subscriptions.next();
this.subscriptions.complete();
}
}

@ -121,8 +121,7 @@
<li><a (click)="allSeasons(result)">All Seasons</a></li>
<li><a (click)="firstSeason(result)">First Season</a></li>
<li><a (click)="latestSeason(result)">Latest Season</a></li>
<li><a>Select Season...</a></li>
<li><a>Select Episode...</a></li>
<li><a (click)="selectSeason(result)">Select ...</a></li>
</ul>
</div>

@ -1,4 +1,5 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import {Router} from '@angular/router';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
@ -26,7 +27,8 @@ export class TvSearchComponent implements OnInit, OnDestroy {
result: IRequestEngineResult;
searchApplied = false;
constructor(private searchService: SearchService, private requestService: RequestService, private notificationService: NotificationService) {
constructor(private searchService: SearchService, private requestService: RequestService,
private notificationService: NotificationService, private route : Router) {
this.searchChanged
.debounceTime(600) // Wait Xms afterthe last event before emitting last event
.distinctUntilChanged() // only emit if value is different from previous value
@ -128,6 +130,10 @@ export class TvSearchComponent implements OnInit, OnDestroy {
this.request(searchResult);
}
selectSeason(searchResult: ISearchTvResult) {
this.route.navigate(['/search/show', searchResult.seriesId]);
}
private clearResults() {
this.tvResults = [];

@ -37,6 +37,11 @@ export class SearchService extends ServiceAuthHelpers {
searchTv(searchTerm: string): Observable<ISearchTvResult[]> {
return this.http.get(`${this.url}/Tv/` + searchTerm).map(this.extractData);
}
getShowInformation(theTvDbId: number): Observable<ISearchTvResult> {
return this.http.get(`${this.url}/Tv/info/${theTvDbId}`).map(this.extractData);
}
popularTv(): Observable<ISearchTvResult[]> {
return this.http.get(`${this.url}/Tv/popular`).map(this.extractData);
}

Loading…
Cancel
Save