mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-14 02:26:55 -07:00
docker support and more, redesign the episodes
This commit is contained in:
parent
b829b10b14
commit
fa5efb6c66
25 changed files with 689 additions and 42 deletions
|
@ -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));
|
||||
|
|
|
@ -14,8 +14,10 @@ namespace Ombi.Store.Context
|
|||
_created = true;
|
||||
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>
|
89
Ombi/Ombi.Store/Sql.Designer.cs
generated
Normal file
89
Ombi/Ombi.Store/Sql.Designer.cs
generated
Normal file
|
@ -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]";.
|
||||
/// </summary>
|
||||
internal static string SqlTables {
|
||||
get {
|
||||
return ResourceManager.GetString("SqlTables", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
124
Ombi/Ombi.Store/Sql.resx
Normal file
124
Ombi/Ombi.Store/Sql.resx
Normal file
|
@ -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>
|
49
Ombi/Ombi.Store/SqlTables.sql
Normal file
49
Ombi/Ombi.Store/SqlTables.sql
Normal file
|
@ -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
|
||||
|
||||
);
|
39
Ombi/Ombi/.vscode/launch.json
vendored
Normal file
39
Ombi/Ombi/.vscode/launch.json
vendored
Normal file
|
@ -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" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
5
Ombi/Ombi/.vscode/settings.json
vendored
Normal file
5
Ombi/Ombi/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"files.associations": {
|
||||
"dockerfile.*": "dockerfile"
|
||||
}
|
||||
}
|
66
Ombi/Ombi/.vscode/tasks.json
vendored
Normal file
66
Ombi/Ombi/.vscode/tasks.json
vendored
Normal file
|
@ -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")]
|
||||
|
|
8
Ombi/Ombi/Dockerfile
Normal file
8
Ombi/Ombi/Dockerfile
Normal file
|
@ -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>
|
||||
|
|
21
Ombi/Ombi/build.sh
Normal file
21
Ombi/Ombi/build.sh
Normal file
|
@ -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>
|
67
Ombi/Ombi/wwwroot/app/search/seriesinformation.component.ts
Normal file
67
Ombi/Ombi/wwwroot/app/search/seriesinformation.component.ts
Normal file
|
@ -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 = [];
|
||||
|
|
|
@ -36,7 +36,12 @@ export class SearchService extends ServiceAuthHelpers {
|
|||
// TV
|
||||
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…
Add table
Add a link
Reference in a new issue