Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db054ece24 | ||
|
|
c2d8a54a47 | ||
|
|
88b645af2d | ||
|
|
941f1a59ee | ||
|
|
a3e20826a5 | ||
|
|
ebff29d6cd | ||
|
|
5a29fc1cbb |
@@ -61,6 +61,7 @@ jobs:
|
||||
- name: Publish
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
prerelease: true
|
||||
files: |
|
||||
ErsatzTV*.zip
|
||||
ErsatzTV*.tar.gz
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using ErsatzTV.Core;
|
||||
using LanguageExt;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Commands
|
||||
{
|
||||
public record AddItemsToSimpleMediaCollection
|
||||
(int MediaCollectionId, List<int> ItemIds) : MediatR.IRequest<Either<BaseError, Unit>>;
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using static LanguageExt.Prelude;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Commands
|
||||
{
|
||||
public class
|
||||
AddItemsToSimpleMediaCollectionHandler : MediatR.IRequestHandler<AddItemsToSimpleMediaCollection,
|
||||
Either<BaseError, Unit>>
|
||||
{
|
||||
private readonly IMediaCollectionRepository _mediaCollectionRepository;
|
||||
private readonly IMediaItemRepository _mediaItemRepository;
|
||||
|
||||
public AddItemsToSimpleMediaCollectionHandler(
|
||||
IMediaCollectionRepository mediaCollectionRepository,
|
||||
IMediaItemRepository mediaItemRepository)
|
||||
{
|
||||
_mediaCollectionRepository = mediaCollectionRepository;
|
||||
_mediaItemRepository = mediaItemRepository;
|
||||
}
|
||||
|
||||
public Task<Either<BaseError, Unit>> Handle(
|
||||
AddItemsToSimpleMediaCollection request,
|
||||
CancellationToken cancellationToken) =>
|
||||
Validate(request)
|
||||
.MapT(ApplyAddItemsRequest)
|
||||
.Bind(v => v.ToEitherAsync());
|
||||
|
||||
private async Task<Unit> ApplyAddItemsRequest(RequestParameters parameters)
|
||||
{
|
||||
foreach (MediaItem item in parameters.ItemsToAdd.Where(
|
||||
item => parameters.Collection.Items.All(i => i.Id != item.Id)))
|
||||
{
|
||||
parameters.Collection.Items.Add(item);
|
||||
}
|
||||
|
||||
await _mediaCollectionRepository.Update(parameters.Collection);
|
||||
|
||||
return Unit.Default;
|
||||
}
|
||||
|
||||
private async Task<Validation<BaseError, RequestParameters>>
|
||||
Validate(AddItemsToSimpleMediaCollection request) =>
|
||||
(await SimpleMediaCollectionMustExist(request), await ValidateItems(request))
|
||||
.Apply(
|
||||
(simpleMediaCollectionToUpdate, itemsToAdd) =>
|
||||
new RequestParameters(simpleMediaCollectionToUpdate, itemsToAdd));
|
||||
|
||||
private Task<Validation<BaseError, SimpleMediaCollection>> SimpleMediaCollectionMustExist(
|
||||
AddItemsToSimpleMediaCollection updateSimpleMediaCollection) =>
|
||||
_mediaCollectionRepository.GetSimpleMediaCollection(updateSimpleMediaCollection.MediaCollectionId)
|
||||
.Map(v => v.ToValidation<BaseError>("SimpleMediaCollection does not exist."));
|
||||
|
||||
private Task<Validation<BaseError, List<MediaItem>>> ValidateItems(
|
||||
AddItemsToSimpleMediaCollection request) =>
|
||||
LoadAllMediaItems(request)
|
||||
.Map(v => v.ToValidation<BaseError>("MediaItem does not exist"));
|
||||
|
||||
private async Task<Option<List<MediaItem>>> LoadAllMediaItems(AddItemsToSimpleMediaCollection request)
|
||||
{
|
||||
var items = (await request.ItemIds.Map(async id => await _mediaItemRepository.Get(id)).Sequence())
|
||||
.ToList();
|
||||
if (items.Any(i => i.IsNone))
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
return items.Somes().ToList();
|
||||
}
|
||||
|
||||
private record RequestParameters(SimpleMediaCollection Collection, List<MediaItem> ItemsToAdd);
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ErsatzTV.Application.MediaItems;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Queries
|
||||
{
|
||||
public record GetSimpleMediaCollectionWithItemsById
|
||||
(int Id) : IRequest<Option<Tuple<MediaCollectionViewModel, List<MediaItemSearchResultViewModel>>>>;
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Application.MediaItems;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
using static LanguageExt.Prelude;
|
||||
using static ErsatzTV.Application.MediaCollections.Mapper;
|
||||
using static ErsatzTV.Application.MediaItems.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.MediaCollections.Queries
|
||||
{
|
||||
public class GetSimpleMediaCollectionWithItemsByIdHandler : IRequestHandler<GetSimpleMediaCollectionWithItemsById,
|
||||
Option<Tuple<MediaCollectionViewModel, List<MediaItemSearchResultViewModel>>>>
|
||||
{
|
||||
private readonly IMediaCollectionRepository _mediaCollectionRepository;
|
||||
|
||||
public GetSimpleMediaCollectionWithItemsByIdHandler(IMediaCollectionRepository mediaCollectionRepository) =>
|
||||
_mediaCollectionRepository = mediaCollectionRepository;
|
||||
|
||||
public async Task<Option<Tuple<MediaCollectionViewModel, List<MediaItemSearchResultViewModel>>>> Handle(
|
||||
GetSimpleMediaCollectionWithItemsById request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
Option<SimpleMediaCollection> maybeCollection =
|
||||
await _mediaCollectionRepository.GetSimpleMediaCollectionWithItems(request.Id);
|
||||
|
||||
return maybeCollection.Match<Option<Tuple<MediaCollectionViewModel, List<MediaItemSearchResultViewModel>>>>(
|
||||
c => Tuple(ProjectToViewModel(c), c.Items.Map(ProjectToSearchViewModel).ToList()),
|
||||
None);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using ErsatzTV.Core.Domain;
|
||||
using static LanguageExt.Prelude;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems
|
||||
{
|
||||
@@ -9,5 +10,26 @@ namespace ErsatzTV.Application.MediaItems
|
||||
mediaItem.Id,
|
||||
mediaItem.MediaSourceId,
|
||||
mediaItem.Path);
|
||||
|
||||
internal static MediaItemSearchResultViewModel ProjectToSearchViewModel(MediaItem mediaItem) =>
|
||||
new(
|
||||
mediaItem.Id,
|
||||
mediaItem.Source.Name,
|
||||
mediaItem.Metadata.MediaType.ToString(),
|
||||
GetDisplayTitle(mediaItem),
|
||||
GetDisplayDuration(mediaItem));
|
||||
|
||||
|
||||
private static string GetDisplayTitle(this MediaItem mediaItem) =>
|
||||
mediaItem.Metadata.MediaType == MediaType.TvShow &&
|
||||
Optional(mediaItem.Metadata.SeasonNumber).IsSome &&
|
||||
Optional(mediaItem.Metadata.EpisodeNumber).IsSome
|
||||
? $"{mediaItem.Metadata.Title} s{mediaItem.Metadata.SeasonNumber:00}e{mediaItem.Metadata.EpisodeNumber:00}"
|
||||
: mediaItem.Metadata.Title;
|
||||
|
||||
private static string GetDisplayDuration(MediaItem mediaItem) =>
|
||||
string.Format(
|
||||
mediaItem.Metadata.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}",
|
||||
mediaItem.Metadata.Duration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace ErsatzTV.Application.MediaItems
|
||||
{
|
||||
public record MediaItemSearchResultViewModel(
|
||||
int Id,
|
||||
string Source,
|
||||
string MediaType,
|
||||
string Title,
|
||||
string Duration);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
using static ErsatzTV.Application.MediaItems.Mapper;
|
||||
|
||||
@@ -15,9 +16,9 @@ namespace ErsatzTV.Application.MediaItems.Queries
|
||||
public GetAllMediaItemsHandler(IMediaItemRepository mediaItemRepository) =>
|
||||
_mediaItemRepository = mediaItemRepository;
|
||||
|
||||
public async Task<List<MediaItemViewModel>> Handle(
|
||||
public Task<List<MediaItemViewModel>> Handle(
|
||||
GetAllMediaItems request,
|
||||
CancellationToken cancellationToken) =>
|
||||
(await _mediaItemRepository.GetAll()).Map(ProjectToViewModel).ToList();
|
||||
_mediaItemRepository.GetAll().Map(list => list.Map(ProjectToViewModel).ToList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems.Queries
|
||||
{
|
||||
public record SearchAllMediaItems(string SearchString) : IRequest<List<MediaItemSearchResultViewModel>>;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
using static ErsatzTV.Application.MediaItems.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.MediaItems.Queries
|
||||
{
|
||||
public class SearchAllMediaItemsHandler : IRequestHandler<SearchAllMediaItems, List<MediaItemSearchResultViewModel>>
|
||||
{
|
||||
private readonly IMediaItemRepository _mediaItemRepository;
|
||||
|
||||
public SearchAllMediaItemsHandler(IMediaItemRepository mediaItemRepository) =>
|
||||
_mediaItemRepository = mediaItemRepository;
|
||||
|
||||
public Task<List<MediaItemSearchResultViewModel>>
|
||||
Handle(SearchAllMediaItems request, CancellationToken cancellationToken) =>
|
||||
_mediaItemRepository.Search(request.SearchString).Map(list => list.Map(ProjectToSearchViewModel).ToList());
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ namespace ErsatzTV.Application.Playouts
|
||||
? $"{mediaItem.Metadata.Title} s{mediaItem.Metadata.SeasonNumber:00}e{mediaItem.Metadata.EpisodeNumber:00}"
|
||||
: mediaItem.Metadata.Title;
|
||||
|
||||
public static string GetDisplayDuration(MediaItem mediaItem) =>
|
||||
private static string GetDisplayDuration(MediaItem mediaItem) =>
|
||||
string.Format(
|
||||
mediaItem.Metadata.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}",
|
||||
mediaItem.Metadata.Duration);
|
||||
|
||||
@@ -22,6 +22,9 @@ namespace ErsatzTV.Core.Tests.Fakes
|
||||
public Task<Option<SimpleMediaCollection>> GetSimpleMediaCollection(int id) =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
public Task<Option<SimpleMediaCollection>> GetSimpleMediaCollectionWithItems(int id) =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
public Task<Option<TelevisionMediaCollection>> GetTelevisionMediaCollection(int id) =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
|
||||
@@ -25,11 +25,20 @@ namespace ErsatzTV.Core.Tests.Metadata
|
||||
[TestCase("Awesome Show - S01E02 - Episode Title-720p.mkv", "Awesome Show", 1, 2)]
|
||||
[TestCase("Awesome Show - s1e2 - Episode Title-720p.mkv", "Awesome Show", 1, 2)]
|
||||
[TestCase("Awesome Show - S1E2 - Episode Title-720p.mkv", "Awesome Show", 1, 2)]
|
||||
[TestCase("Awesome Show (2021) - S01E02 - Description; More Description (1080p QUALITY codec GROUP).mkv", "Awesome Show (2021)", 1, 2)]
|
||||
[TestCase("Awesome.Show.S01E02.Description.more.Description.QUAlity.codec.CODEC-GROUP.mkv", "Awesome.Show", 1, 2)]
|
||||
[TestCase(
|
||||
"Awesome Show (2021) - S01E02 - Description; More Description (1080p QUALITY codec GROUP).mkv",
|
||||
"Awesome Show (2021)",
|
||||
1,
|
||||
2)]
|
||||
[TestCase(
|
||||
"Awesome.Show.S01E02.Description.more.Description.QUAlity.codec.CODEC-GROUP.mkv",
|
||||
"Awesome.Show",
|
||||
1,
|
||||
2)]
|
||||
public void GetFallbackMetadata_ShouldHandleVariousFormats(string path, string title, int season, int episode)
|
||||
{
|
||||
var metadata = FallbackMetadataProvider.GetFallbackMetadata(path);
|
||||
MediaMetadata metadata = FallbackMetadataProvider.GetFallbackMetadata(
|
||||
new MediaItem { Path = path, Source = new LocalMediaSource { MediaType = MediaType.TvShow } });
|
||||
|
||||
metadata.MediaType.Should().Be(MediaType.TvShow);
|
||||
metadata.Title.Should().Be(title);
|
||||
|
||||
@@ -19,6 +19,11 @@ namespace ErsatzTV.Core.Hdhr
|
||||
|
||||
public string GuideNumber => _channel.Number.ToString();
|
||||
public string GuideName => _channel.Name;
|
||||
public string URL => $"{_scheme}://{_host}/iptv/channel/{_channel.Number}";
|
||||
|
||||
public string URL => _channel.StreamingMode switch
|
||||
{
|
||||
StreamingMode.HttpLiveStreaming => $"{_scheme}://{_host}/iptv/channel/{_channel.Number}.m3u8",
|
||||
_ => $"{_scheme}://{_host}/iptv/channel/{_channel.Number}.ts"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories
|
||||
public Task<SimpleMediaCollection> Add(SimpleMediaCollection collection);
|
||||
public Task<Option<MediaCollection>> Get(int id);
|
||||
public Task<Option<SimpleMediaCollection>> GetSimpleMediaCollection(int id);
|
||||
public Task<Option<SimpleMediaCollection>> GetSimpleMediaCollectionWithItems(int id);
|
||||
public Task<Option<TelevisionMediaCollection>> GetTelevisionMediaCollection(int id);
|
||||
public Task<List<SimpleMediaCollection>> GetSimpleMediaCollections();
|
||||
public Task<List<MediaCollection>> GetAll();
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories
|
||||
public Task<int> Add(MediaItem mediaItem);
|
||||
public Task<Option<MediaItem>> Get(int id);
|
||||
public Task<List<MediaItem>> GetAll();
|
||||
public Task<List<MediaItem>> Search(string searchString);
|
||||
public Task<List<MediaItem>> GetAll(MediaType mediaType);
|
||||
public Task<List<MediaItem>> GetAllByMediaSourceId(int mediaSourceId);
|
||||
public Task Update(MediaItem mediaItem);
|
||||
|
||||
@@ -23,18 +23,18 @@ namespace ErsatzTV.Core.Iptv
|
||||
|
||||
public string ToXml()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
using var xml = XmlWriter.Create(sb);
|
||||
using var ms = new MemoryStream();
|
||||
using var xml = XmlWriter.Create(ms);
|
||||
xml.WriteStartDocument();
|
||||
|
||||
|
||||
xml.WriteStartElement("tv");
|
||||
xml.WriteAttributeString("generator-info-name", "ersatztv");
|
||||
|
||||
|
||||
foreach (Channel channel in _channels)
|
||||
{
|
||||
xml.WriteStartElement("channel");
|
||||
xml.WriteAttributeString("id", channel.Number.ToString());
|
||||
|
||||
|
||||
xml.WriteStartElement("display-name");
|
||||
xml.WriteAttributeString("lang", "en");
|
||||
xml.WriteString(channel.Name);
|
||||
@@ -72,7 +72,7 @@ namespace ErsatzTV.Core.Iptv
|
||||
xml.WriteAttributeString("lang", "en");
|
||||
xml.WriteString(metadata.Title);
|
||||
xml.WriteEndElement(); // title
|
||||
|
||||
|
||||
xml.WriteStartElement("previously-shown");
|
||||
xml.WriteEndElement(); // previously-shown
|
||||
|
||||
@@ -113,7 +113,8 @@ namespace ErsatzTV.Core.Iptv
|
||||
xml.WriteEndElement(); // tv
|
||||
xml.WriteEndDocument();
|
||||
|
||||
return sb.ToString();
|
||||
xml.Flush();
|
||||
return Encoding.UTF8.GetString(ms.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
@@ -6,12 +7,32 @@ namespace ErsatzTV.Core.Metadata
|
||||
{
|
||||
public static class FallbackMetadataProvider
|
||||
{
|
||||
public static MediaMetadata GetFallbackMetadata(string path)
|
||||
public static MediaMetadata GetFallbackMetadata(MediaItem mediaItem)
|
||||
{
|
||||
string fileName = Path.GetFileName(path);
|
||||
var metadata = new MediaMetadata { Title = fileName ?? path };
|
||||
string fileName = Path.GetFileName(mediaItem.Path);
|
||||
var metadata = new MediaMetadata { Title = fileName ?? mediaItem.Path };
|
||||
|
||||
if (fileName != null)
|
||||
{
|
||||
if (!(mediaItem.Source is LocalMediaSource localMediaSource))
|
||||
{
|
||||
return metadata;
|
||||
}
|
||||
|
||||
return localMediaSource.MediaType switch
|
||||
{
|
||||
MediaType.TvShow => GetTvShowMetadata(fileName, metadata),
|
||||
MediaType.Movie => GetMovieMetadata(fileName, metadata),
|
||||
_ => metadata
|
||||
};
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private static MediaMetadata GetTvShowMetadata(string fileName, MediaMetadata metadata)
|
||||
{
|
||||
try
|
||||
{
|
||||
const string PATTERN = @"^(.*?)[.\s-]+[sS](\d+)[eE](\d+).*\.\w+$";
|
||||
Match match = Regex.Match(fileName, PATTERN);
|
||||
@@ -23,6 +44,31 @@ namespace ErsatzTV.Core.Metadata
|
||||
metadata.EpisodeNumber = int.Parse(match.Groups[3].Value);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private static MediaMetadata GetMovieMetadata(string fileName, MediaMetadata metadata)
|
||||
{
|
||||
try
|
||||
{
|
||||
const string PATTERN = @"^(.*?)[.\(](\d{4})[.\)].*\.\w+$";
|
||||
Match match = Regex.Match(fileName, PATTERN);
|
||||
if (match.Success)
|
||||
{
|
||||
metadata.MediaType = MediaType.Movie;
|
||||
metadata.Title = match.Groups[1].Value;
|
||||
metadata.Aired = new DateTime(int.Parse(match.Groups[2].Value), 1, 1);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace ErsatzTV.Core.Metadata
|
||||
var knownExtensions = new List<string>
|
||||
{
|
||||
".mpg", ".mp2", ".mpeg", ".mpe", ".mpv", ".ogg", ".mp4", ".m4p", ".m4v",
|
||||
".avi", ".wmv", ".mov", ".mkv"
|
||||
".avi", ".wmv", ".mov", ".mkv", ".ts"
|
||||
};
|
||||
|
||||
var allFiles = Directory.GetFiles(localMediaSource.Folder, "*", SearchOption.AllDirectories)
|
||||
|
||||
@@ -1,28 +1,35 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Metadata;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using static LanguageExt.Prelude;
|
||||
|
||||
namespace ErsatzTV.Core.Metadata
|
||||
{
|
||||
public class LocalMetadataProvider : ILocalMetadataProvider
|
||||
{
|
||||
private static readonly XmlSerializer MovieSerializer = new(typeof(MovieNfo));
|
||||
private static readonly XmlSerializer TvShowSerializer = new(typeof(TvShowEpisodeNfo));
|
||||
private readonly ILogger<LocalMetadataProvider> _logger;
|
||||
|
||||
private readonly IMediaItemRepository _mediaItemRepository;
|
||||
|
||||
public LocalMetadataProvider(IMediaItemRepository mediaItemRepository) =>
|
||||
public LocalMetadataProvider(IMediaItemRepository mediaItemRepository, ILogger<LocalMetadataProvider> logger)
|
||||
{
|
||||
_mediaItemRepository = mediaItemRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task RefreshMetadata(MediaItem mediaItem)
|
||||
{
|
||||
Option<MediaMetadata> maybeMetadata = await LoadMetadata(mediaItem);
|
||||
MediaMetadata metadata =
|
||||
maybeMetadata.IfNone(() => FallbackMetadataProvider.GetFallbackMetadata(mediaItem.Path));
|
||||
maybeMetadata.IfNone(() => FallbackMetadataProvider.GetFallbackMetadata(mediaItem));
|
||||
await ApplyMetadataUpdate(mediaItem, metadata);
|
||||
}
|
||||
|
||||
@@ -50,54 +57,72 @@ namespace ErsatzTV.Core.Metadata
|
||||
string nfoFileName = Path.ChangeExtension(mediaItem.Path, "nfo");
|
||||
if (nfoFileName == null || !File.Exists(nfoFileName))
|
||||
{
|
||||
_logger.LogDebug("NFO file does not exist at {Path}", nfoFileName);
|
||||
return None;
|
||||
}
|
||||
|
||||
var tvShowSerializer = new XmlSerializer(typeof(TvShowEpisodeNfo));
|
||||
var movieSerializer = new XmlSerializer(typeof(MovieNfo));
|
||||
if (!(mediaItem.Source is LocalMediaSource localMediaSource))
|
||||
{
|
||||
_logger.LogDebug("Media source {Name} is not a local media source", mediaItem.Source.Name);
|
||||
return None;
|
||||
}
|
||||
|
||||
TryAsync<object> tvShowAttempt = TryAsync(
|
||||
async () =>
|
||||
{
|
||||
await using FileStream fileStream = File.Open(nfoFileName, FileMode.Open);
|
||||
return tvShowSerializer.Deserialize(fileStream);
|
||||
});
|
||||
TryAsync<object> movieAttempt = TryAsync(
|
||||
async () =>
|
||||
{
|
||||
await using FileStream fileStream = File.Open(nfoFileName, FileMode.Open);
|
||||
return movieSerializer.Deserialize(fileStream);
|
||||
});
|
||||
return await choice(tvShowAttempt, movieAttempt).Match<object, Option<MediaMetadata>>(
|
||||
result =>
|
||||
{
|
||||
switch (result)
|
||||
return localMediaSource.MediaType switch
|
||||
{
|
||||
MediaType.Movie => await LoadMovieMetadata(nfoFileName),
|
||||
MediaType.TvShow => await LoadTvShowMetadata(nfoFileName),
|
||||
_ => None
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<Option<MediaMetadata>> LoadTvShowMetadata(string nfoFileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using FileStream fileStream = File.Open(nfoFileName, FileMode.Open, FileAccess.Read);
|
||||
Option<TvShowEpisodeNfo> maybeNfo = TvShowSerializer.Deserialize(fileStream) as TvShowEpisodeNfo;
|
||||
return maybeNfo.Match<Option<MediaMetadata>>(
|
||||
nfo => new MediaMetadata
|
||||
{
|
||||
case TvShowEpisodeNfo nfo:
|
||||
return new MediaMetadata
|
||||
{
|
||||
MediaType = MediaType.TvShow,
|
||||
Title = nfo.ShowTitle,
|
||||
Subtitle = nfo.Title,
|
||||
Description = nfo.Outline,
|
||||
EpisodeNumber = nfo.Episode,
|
||||
SeasonNumber = nfo.Season,
|
||||
Aired = GetAired(nfo.Aired)
|
||||
};
|
||||
case MovieNfo nfo:
|
||||
return new MediaMetadata
|
||||
{
|
||||
MediaType = MediaType.Movie,
|
||||
Title = nfo.Title,
|
||||
Description = nfo.Outline,
|
||||
ContentRating = nfo.ContentRating,
|
||||
Aired = GetAired(nfo.Premiered)
|
||||
};
|
||||
default:
|
||||
return None;
|
||||
}
|
||||
},
|
||||
None);
|
||||
MediaType = MediaType.TvShow,
|
||||
Title = nfo.ShowTitle,
|
||||
Subtitle = nfo.Title,
|
||||
Description = nfo.Outline,
|
||||
EpisodeNumber = nfo.Episode,
|
||||
SeasonNumber = nfo.Season,
|
||||
Aired = GetAired(nfo.Aired)
|
||||
},
|
||||
None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogDebug(ex, "Failed to read TV nfo metadata from {Path}", nfoFileName);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Option<MediaMetadata>> LoadMovieMetadata(string nfoFileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using FileStream fileStream = File.Open(nfoFileName, FileMode.Open, FileAccess.Read);
|
||||
Option<MovieNfo> maybeNfo = MovieSerializer.Deserialize(fileStream) as MovieNfo;
|
||||
return maybeNfo.Match<Option<MediaMetadata>>(
|
||||
nfo => new MediaMetadata
|
||||
{
|
||||
MediaType = MediaType.Movie,
|
||||
Title = nfo.Title,
|
||||
Description = nfo.Outline,
|
||||
ContentRating = nfo.ContentRating,
|
||||
Aired = GetAired(nfo.Premiered)
|
||||
},
|
||||
None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogDebug(ex, "Failed to read Movie nfo metadata from {Path}", nfoFileName);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
private static DateTime? GetAired(string aired)
|
||||
|
||||
@@ -7,6 +7,7 @@ using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Metadata;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using static LanguageExt.Prelude;
|
||||
|
||||
@@ -14,16 +15,29 @@ namespace ErsatzTV.Core.Metadata
|
||||
{
|
||||
public class LocalStatisticsProvider : ILocalStatisticsProvider
|
||||
{
|
||||
private readonly ILogger<LocalStatisticsProvider> _logger;
|
||||
private readonly IMediaItemRepository _mediaItemRepository;
|
||||
|
||||
public LocalStatisticsProvider(IMediaItemRepository mediaItemRepository) =>
|
||||
public LocalStatisticsProvider(
|
||||
IMediaItemRepository mediaItemRepository,
|
||||
ILogger<LocalStatisticsProvider> logger)
|
||||
{
|
||||
_mediaItemRepository = mediaItemRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task RefreshStatistics(string ffprobePath, MediaItem mediaItem)
|
||||
{
|
||||
FFprobe ffprobe = await GetProbeOutput(ffprobePath, mediaItem);
|
||||
MediaMetadata metadata = ProjectToMediaMetadata(ffprobe);
|
||||
await ApplyStatisticsUpdate(mediaItem, metadata);
|
||||
try
|
||||
{
|
||||
FFprobe ffprobe = await GetProbeOutput(ffprobePath, mediaItem);
|
||||
MediaMetadata metadata = ProjectToMediaMetadata(ffprobe);
|
||||
await ApplyStatisticsUpdate(mediaItem, metadata);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to refresh statistics for media item at {Path}", mediaItem.Path);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ApplyStatisticsUpdate(
|
||||
@@ -82,7 +96,7 @@ namespace ErsatzTV.Core.Metadata
|
||||
|
||||
private MediaMetadata ProjectToMediaMetadata(FFprobe probeOutput) =>
|
||||
Optional(probeOutput)
|
||||
.Filter(json => json != null)
|
||||
.Filter(json => json?.format != null && json.streams != null)
|
||||
.ToValidation<BaseError>("Unable to parse ffprobe output")
|
||||
.ToEither<FFprobe>()
|
||||
.Match(
|
||||
|
||||
@@ -29,6 +29,13 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
public Task<Option<SimpleMediaCollection>> GetSimpleMediaCollection(int id) =>
|
||||
Get(id).Map(c => c.OfType<SimpleMediaCollection>().HeadOrNone());
|
||||
|
||||
public Task<Option<SimpleMediaCollection>> GetSimpleMediaCollectionWithItems(int id) =>
|
||||
_dbContext.SimpleMediaCollections
|
||||
.Include(s => s.Items)
|
||||
.ThenInclude(i => i.Source)
|
||||
.SingleOrDefaultAsync(c => c.Id == id)
|
||||
.Map(Optional);
|
||||
|
||||
public Task<Option<TelevisionMediaCollection>> GetTelevisionMediaCollection(int id) =>
|
||||
Get(id).Map(c => c.OfType<TelevisionMediaCollection>().HeadOrNone());
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
@@ -26,6 +27,19 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
|
||||
public Task<List<MediaItem>> GetAll() => _dbContext.MediaItems.ToListAsync();
|
||||
|
||||
public Task<List<MediaItem>> Search(string searchString)
|
||||
{
|
||||
IQueryable<MediaItem> data = from c in _dbContext.MediaItems.Include(c => c.Source) select c;
|
||||
|
||||
if (!string.IsNullOrEmpty(searchString))
|
||||
{
|
||||
data = data.Where(c => EF.Functions.Like(c.Metadata.Title, $"%{searchString}%"));
|
||||
}
|
||||
|
||||
return data.ToListAsync();
|
||||
}
|
||||
|
||||
|
||||
public Task<List<MediaItem>> GetAll(MediaType mediaType) =>
|
||||
_dbContext.MediaItems
|
||||
.Include(i => i.Source)
|
||||
|
||||
@@ -2,11 +2,16 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.3" />
|
||||
<PackageReference Include="Refit" Version="6.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -0,0 +1,876 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(TvContext))]
|
||||
[Migration("20210212105010_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "5.0.3");
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.AggregateModels.GenericIntegerId", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.ToTable("GenericIntegerIds");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.AggregateModels.MediaCollectionSummary", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsSimple")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ItemCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.ToTable("MediaCollectionSummaries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("FFmpegProfileId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Logo")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Number")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("StreamingMode")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("UniqueId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("FFmpegProfileId");
|
||||
|
||||
b.HasIndex("Number")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Channels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.ConfigElement", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Key")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ConfigElements");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AudioBitrate")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AudioBufferSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AudioChannels")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AudioCodec")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("AudioSampleRate")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AudioVolume")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("NormalizeAudio")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("NormalizeAudioCodec")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("NormalizeResolution")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("NormalizeVideoCodec")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ResolutionId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ThreadCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Transcode")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("VideoBitrate")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("VideoBufferSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("VideoCodec")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ResolutionId");
|
||||
|
||||
b.ToTable("FFmpegProfiles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaCollection", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MediaCollections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("LastWriteTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("MediaSourceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Path")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MediaSourceId");
|
||||
|
||||
b.ToTable("MediaItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaSource", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SourceType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MediaSources");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ChannelId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ProgramScheduleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ProgramSchedulePlayoutType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ChannelId");
|
||||
|
||||
b.HasIndex("ProgramScheduleId");
|
||||
|
||||
b.ToTable("Playouts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Finish")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("MediaItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("PlayoutId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Start")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MediaItemId");
|
||||
|
||||
b.HasIndex("PlayoutId");
|
||||
|
||||
b.ToTable("PlayoutItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b =>
|
||||
{
|
||||
b.Property<int>("PlayoutId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ProgramScheduleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MediaCollectionId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("PlayoutId", "ProgramScheduleId", "MediaCollectionId");
|
||||
|
||||
b.HasIndex("MediaCollectionId");
|
||||
|
||||
b.HasIndex("ProgramScheduleId");
|
||||
|
||||
b.ToTable("PlayoutProgramScheduleItemAnchors");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSourceConnection", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("PlexMediaSourceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Uri")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PlexMediaSourceId");
|
||||
|
||||
b.ToTable("PlexMediaSourceConnections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSourceLibrary", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("MediaType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("PlexMediaSourceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PlexMediaSourceId");
|
||||
|
||||
b.ToTable("PlexMediaSourceLibraries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MediaCollectionPlaybackOrder")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ProgramSchedules");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Index")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MediaCollectionId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ProgramScheduleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<TimeSpan?>("StartTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MediaCollectionId");
|
||||
|
||||
b.HasIndex("ProgramScheduleId");
|
||||
|
||||
b.ToTable("ProgramScheduleItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Resolution", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Height")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Width")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Resolutions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MediaItemSimpleMediaCollection", b =>
|
||||
{
|
||||
b.Property<int>("ItemsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SimpleMediaCollectionsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("ItemsId", "SimpleMediaCollectionsId");
|
||||
|
||||
b.HasIndex("SimpleMediaCollectionsId");
|
||||
|
||||
b.ToTable("MediaItemSimpleMediaCollection");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b =>
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection");
|
||||
|
||||
b.ToTable("SimpleMediaCollections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionMediaCollection", b =>
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection");
|
||||
|
||||
b.Property<int?>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ShowTitle")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasIndex("ShowTitle", "SeasonNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("TelevisionMediaCollections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b =>
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaSource");
|
||||
|
||||
b.Property<string>("Folder")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("MediaType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.ToTable("LocalMediaSources");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b =>
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaSource");
|
||||
|
||||
b.Property<string>("ClientIdentifier")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProductVersion")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.ToTable("PlexMediaSources");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b =>
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
|
||||
|
||||
b.Property<bool>("OfflineTail")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<TimeSpan>("PlayoutDuration")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.ToTable("ProgramScheduleDurationItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b =>
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
|
||||
|
||||
b.ToTable("ProgramScheduleFloodItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b =>
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
|
||||
|
||||
b.Property<int>("Count")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.ToTable("ProgramScheduleMultipleItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b =>
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
|
||||
|
||||
b.ToTable("ProgramScheduleOneItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile")
|
||||
.WithMany()
|
||||
.HasForeignKey("FFmpegProfileId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("FFmpegProfile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.FFmpegProfile", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution")
|
||||
.WithMany()
|
||||
.HasForeignKey("ResolutionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Resolution");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.MediaItem", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaSource", "Source")
|
||||
.WithMany()
|
||||
.HasForeignKey("MediaSourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.OwnsOne("ErsatzTV.Core.Domain.MediaMetadata", "Metadata", b1 =>
|
||||
{
|
||||
b1.Property<int>("MediaItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<DateTime?>("Aired")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<string>("AudioCodec")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<string>("ContentRating")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<string>("DisplayAspectRatio")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<TimeSpan>("Duration")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<int?>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("Height")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("MediaType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<string>("SampleAspectRatio")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<int?>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<string>("Subtitle")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<string>("VideoCodec")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<int>("VideoScanType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("Width")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.HasKey("MediaItemId");
|
||||
|
||||
b1.ToTable("MediaItems");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("MediaItemId");
|
||||
});
|
||||
|
||||
b.Navigation("Metadata");
|
||||
|
||||
b.Navigation("Source");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel")
|
||||
.WithMany("Playouts")
|
||||
.HasForeignKey("ChannelId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule")
|
||||
.WithMany("Playouts")
|
||||
.HasForeignKey("ProgramScheduleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.OwnsOne("ErsatzTV.Core.Domain.PlayoutAnchor", "Anchor", b1 =>
|
||||
{
|
||||
b1.Property<int>("PlayoutId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("NextScheduleItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<DateTimeOffset>("NextStart")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.HasKey("PlayoutId");
|
||||
|
||||
b1.HasIndex("NextScheduleItemId");
|
||||
|
||||
b1.ToTable("Playouts");
|
||||
|
||||
b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("NextScheduleItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("PlayoutId");
|
||||
|
||||
b1.Navigation("NextScheduleItem");
|
||||
});
|
||||
|
||||
b.Navigation("Anchor");
|
||||
|
||||
b.Navigation("Channel");
|
||||
|
||||
b.Navigation("ProgramSchedule");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutItem", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("MediaItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout")
|
||||
.WithMany("Items")
|
||||
.HasForeignKey("PlayoutId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("MediaItem");
|
||||
|
||||
b.Navigation("Playout");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection")
|
||||
.WithMany()
|
||||
.HasForeignKey("MediaCollectionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout")
|
||||
.WithMany("ProgramScheduleAnchors")
|
||||
.HasForeignKey("PlayoutId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProgramScheduleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.OwnsOne("ErsatzTV.Core.Domain.MediaCollectionEnumeratorState", "EnumeratorState", b1 =>
|
||||
{
|
||||
b1.Property<int>("PlayoutProgramScheduleAnchorPlayoutId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("PlayoutProgramScheduleAnchorProgramScheduleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("PlayoutProgramScheduleAnchorMediaCollectionId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("Index")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("Seed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.HasKey("PlayoutProgramScheduleAnchorPlayoutId", "PlayoutProgramScheduleAnchorProgramScheduleId", "PlayoutProgramScheduleAnchorMediaCollectionId");
|
||||
|
||||
b1.ToTable("PlayoutProgramScheduleItemAnchors");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("PlayoutProgramScheduleAnchorPlayoutId", "PlayoutProgramScheduleAnchorProgramScheduleId", "PlayoutProgramScheduleAnchorMediaCollectionId");
|
||||
});
|
||||
|
||||
b.Navigation("EnumeratorState");
|
||||
|
||||
b.Navigation("MediaCollection");
|
||||
|
||||
b.Navigation("Playout");
|
||||
|
||||
b.Navigation("ProgramSchedule");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSourceConnection", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", null)
|
||||
.WithMany("Connections")
|
||||
.HasForeignKey("PlexMediaSourceId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSourceLibrary", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", null)
|
||||
.WithMany("Libraries")
|
||||
.HasForeignKey("PlexMediaSourceId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItem", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection")
|
||||
.WithMany()
|
||||
.HasForeignKey("MediaCollectionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule")
|
||||
.WithMany("Items")
|
||||
.HasForeignKey("ProgramScheduleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("MediaCollection");
|
||||
|
||||
b.Navigation("ProgramSchedule");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MediaItemSimpleMediaCollection", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ItemsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("SimpleMediaCollectionsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.SimpleMediaCollection", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("ErsatzTV.Core.Domain.SimpleMediaCollection", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.TelevisionMediaCollection", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("ErsatzTV.Core.Domain.TelevisionMediaCollection", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.LocalMediaSource", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaSource", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaSource", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramScheduleItemOne", b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b =>
|
||||
{
|
||||
b.Navigation("Playouts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Playout", b =>
|
||||
{
|
||||
b.Navigation("Items");
|
||||
|
||||
b.Navigation("ProgramScheduleAnchors");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.ProgramSchedule", b =>
|
||||
{
|
||||
b.Navigation("Items");
|
||||
|
||||
b.Navigation("Playouts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.PlexMediaSource", b =>
|
||||
{
|
||||
b.Navigation("Connections");
|
||||
|
||||
b.Navigation("Libraries");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,718 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Migrations
|
||||
{
|
||||
public partial class Initial : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
"ConfigElements",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Key = table.Column<string>("TEXT", nullable: true),
|
||||
Value = table.Column<string>("TEXT", nullable: true)
|
||||
},
|
||||
constraints: table => { table.PrimaryKey("PK_ConfigElements", x => x.Id); });
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"GenericIntegerIds",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table => { });
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"MediaCollections",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Name = table.Column<string>("TEXT", nullable: true)
|
||||
},
|
||||
constraints: table => { table.PrimaryKey("PK_MediaCollections", x => x.Id); });
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"MediaCollectionSummaries",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false),
|
||||
Name = table.Column<string>("TEXT", nullable: true),
|
||||
ItemCount = table.Column<int>("INTEGER", nullable: false),
|
||||
IsSimple = table.Column<bool>("INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table => { });
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"MediaSources",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
SourceType = table.Column<int>("INTEGER", nullable: false),
|
||||
Name = table.Column<string>("TEXT", nullable: true)
|
||||
},
|
||||
constraints: table => { table.PrimaryKey("PK_MediaSources", x => x.Id); });
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"ProgramSchedules",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Name = table.Column<string>("TEXT", nullable: true),
|
||||
MediaCollectionPlaybackOrder = table.Column<int>("INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table => { table.PrimaryKey("PK_ProgramSchedules", x => x.Id); });
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"Resolutions",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Name = table.Column<string>("TEXT", nullable: true),
|
||||
Height = table.Column<int>("INTEGER", nullable: false),
|
||||
Width = table.Column<int>("INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table => { table.PrimaryKey("PK_Resolutions", x => x.Id); });
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"SimpleMediaCollections",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SimpleMediaCollections", x => x.Id);
|
||||
table.ForeignKey(
|
||||
"FK_SimpleMediaCollections_MediaCollections_Id",
|
||||
x => x.Id,
|
||||
"MediaCollections",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"TelevisionMediaCollections",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
ShowTitle = table.Column<string>("TEXT", nullable: true),
|
||||
SeasonNumber = table.Column<int>("INTEGER", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_TelevisionMediaCollections", x => x.Id);
|
||||
table.ForeignKey(
|
||||
"FK_TelevisionMediaCollections_MediaCollections_Id",
|
||||
x => x.Id,
|
||||
"MediaCollections",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"LocalMediaSources",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
MediaType = table.Column<int>("INTEGER", nullable: false),
|
||||
Folder = table.Column<string>("TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_LocalMediaSources", x => x.Id);
|
||||
table.ForeignKey(
|
||||
"FK_LocalMediaSources_MediaSources_Id",
|
||||
x => x.Id,
|
||||
"MediaSources",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"MediaItems",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
MediaSourceId = table.Column<int>("INTEGER", nullable: false),
|
||||
Path = table.Column<string>("TEXT", nullable: true),
|
||||
Metadata_Duration = table.Column<TimeSpan>("TEXT", nullable: true),
|
||||
Metadata_SampleAspectRatio = table.Column<string>("TEXT", nullable: true),
|
||||
Metadata_DisplayAspectRatio = table.Column<string>("TEXT", nullable: true),
|
||||
Metadata_VideoCodec = table.Column<string>("TEXT", nullable: true),
|
||||
Metadata_AudioCodec = table.Column<string>("TEXT", nullable: true),
|
||||
Metadata_MediaType = table.Column<int>("INTEGER", nullable: true),
|
||||
Metadata_Title = table.Column<string>("TEXT", nullable: true),
|
||||
Metadata_Subtitle = table.Column<string>("TEXT", nullable: true),
|
||||
Metadata_Description = table.Column<string>("TEXT", nullable: true),
|
||||
Metadata_SeasonNumber = table.Column<int>("INTEGER", nullable: true),
|
||||
Metadata_EpisodeNumber = table.Column<int>("INTEGER", nullable: true),
|
||||
Metadata_ContentRating = table.Column<string>("TEXT", nullable: true),
|
||||
Metadata_Aired = table.Column<DateTime>("TEXT", nullable: true),
|
||||
Metadata_VideoScanType = table.Column<int>("INTEGER", nullable: true),
|
||||
Metadata_Width = table.Column<int>("INTEGER", nullable: true),
|
||||
Metadata_Height = table.Column<int>("INTEGER", nullable: true),
|
||||
LastWriteTime = table.Column<DateTime>("TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_MediaItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
"FK_MediaItems_MediaSources_MediaSourceId",
|
||||
x => x.MediaSourceId,
|
||||
"MediaSources",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"PlexMediaSources",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
ProductVersion = table.Column<string>("TEXT", nullable: true),
|
||||
ClientIdentifier = table.Column<string>("TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PlexMediaSources", x => x.Id);
|
||||
table.ForeignKey(
|
||||
"FK_PlexMediaSources_MediaSources_Id",
|
||||
x => x.Id,
|
||||
"MediaSources",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"ProgramScheduleItems",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Index = table.Column<int>("INTEGER", nullable: false),
|
||||
StartTime = table.Column<TimeSpan>("TEXT", nullable: true),
|
||||
MediaCollectionId = table.Column<int>("INTEGER", nullable: false),
|
||||
ProgramScheduleId = table.Column<int>("INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ProgramScheduleItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
"FK_ProgramScheduleItems_MediaCollections_MediaCollectionId",
|
||||
x => x.MediaCollectionId,
|
||||
"MediaCollections",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
"FK_ProgramScheduleItems_ProgramSchedules_ProgramScheduleId",
|
||||
x => x.ProgramScheduleId,
|
||||
"ProgramSchedules",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"FFmpegProfiles",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Name = table.Column<string>("TEXT", nullable: true),
|
||||
ThreadCount = table.Column<int>("INTEGER", nullable: false),
|
||||
Transcode = table.Column<bool>("INTEGER", nullable: false),
|
||||
ResolutionId = table.Column<int>("INTEGER", nullable: false),
|
||||
NormalizeResolution = table.Column<bool>("INTEGER", nullable: false),
|
||||
VideoCodec = table.Column<string>("TEXT", nullable: true),
|
||||
NormalizeVideoCodec = table.Column<bool>("INTEGER", nullable: false),
|
||||
VideoBitrate = table.Column<int>("INTEGER", nullable: false),
|
||||
VideoBufferSize = table.Column<int>("INTEGER", nullable: false),
|
||||
AudioCodec = table.Column<string>("TEXT", nullable: true),
|
||||
NormalizeAudioCodec = table.Column<bool>("INTEGER", nullable: false),
|
||||
AudioBitrate = table.Column<int>("INTEGER", nullable: false),
|
||||
AudioBufferSize = table.Column<int>("INTEGER", nullable: false),
|
||||
AudioVolume = table.Column<int>("INTEGER", nullable: false),
|
||||
AudioChannels = table.Column<int>("INTEGER", nullable: false),
|
||||
AudioSampleRate = table.Column<int>("INTEGER", nullable: false),
|
||||
NormalizeAudio = table.Column<bool>("INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_FFmpegProfiles", x => x.Id);
|
||||
table.ForeignKey(
|
||||
"FK_FFmpegProfiles_Resolutions_ResolutionId",
|
||||
x => x.ResolutionId,
|
||||
"Resolutions",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"MediaItemSimpleMediaCollection",
|
||||
table => new
|
||||
{
|
||||
ItemsId = table.Column<int>("INTEGER", nullable: false),
|
||||
SimpleMediaCollectionsId = table.Column<int>("INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey(
|
||||
"PK_MediaItemSimpleMediaCollection",
|
||||
x => new { x.ItemsId, x.SimpleMediaCollectionsId });
|
||||
table.ForeignKey(
|
||||
"FK_MediaItemSimpleMediaCollection_MediaItems_ItemsId",
|
||||
x => x.ItemsId,
|
||||
"MediaItems",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
"FK_MediaItemSimpleMediaCollection_SimpleMediaCollections_SimpleMediaCollectionsId",
|
||||
x => x.SimpleMediaCollectionsId,
|
||||
"SimpleMediaCollections",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"PlexMediaSourceConnections",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
IsActive = table.Column<bool>("INTEGER", nullable: false),
|
||||
Uri = table.Column<string>("TEXT", nullable: true),
|
||||
PlexMediaSourceId = table.Column<int>("INTEGER", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PlexMediaSourceConnections", x => x.Id);
|
||||
table.ForeignKey(
|
||||
"FK_PlexMediaSourceConnections_PlexMediaSources_PlexMediaSourceId",
|
||||
x => x.PlexMediaSourceId,
|
||||
"PlexMediaSources",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"PlexMediaSourceLibraries",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Key = table.Column<string>("TEXT", nullable: true),
|
||||
Name = table.Column<string>("TEXT", nullable: true),
|
||||
MediaType = table.Column<int>("INTEGER", nullable: false),
|
||||
PlexMediaSourceId = table.Column<int>("INTEGER", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PlexMediaSourceLibraries", x => x.Id);
|
||||
table.ForeignKey(
|
||||
"FK_PlexMediaSourceLibraries_PlexMediaSources_PlexMediaSourceId",
|
||||
x => x.PlexMediaSourceId,
|
||||
"PlexMediaSources",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"ProgramScheduleDurationItems",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
PlayoutDuration = table.Column<TimeSpan>("TEXT", nullable: false),
|
||||
OfflineTail = table.Column<bool>("INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ProgramScheduleDurationItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
"FK_ProgramScheduleDurationItems_ProgramScheduleItems_Id",
|
||||
x => x.Id,
|
||||
"ProgramScheduleItems",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"ProgramScheduleFloodItems",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ProgramScheduleFloodItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
"FK_ProgramScheduleFloodItems_ProgramScheduleItems_Id",
|
||||
x => x.Id,
|
||||
"ProgramScheduleItems",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"ProgramScheduleMultipleItems",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Count = table.Column<int>("INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ProgramScheduleMultipleItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
"FK_ProgramScheduleMultipleItems_ProgramScheduleItems_Id",
|
||||
x => x.Id,
|
||||
"ProgramScheduleItems",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"ProgramScheduleOneItems",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ProgramScheduleOneItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
"FK_ProgramScheduleOneItems_ProgramScheduleItems_Id",
|
||||
x => x.Id,
|
||||
"ProgramScheduleItems",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"Channels",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
UniqueId = table.Column<Guid>("TEXT", nullable: false),
|
||||
Number = table.Column<int>("INTEGER", nullable: false),
|
||||
Name = table.Column<string>("TEXT", nullable: true),
|
||||
Logo = table.Column<string>("TEXT", nullable: true),
|
||||
FFmpegProfileId = table.Column<int>("INTEGER", nullable: false),
|
||||
StreamingMode = table.Column<int>("INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Channels", x => x.Id);
|
||||
table.ForeignKey(
|
||||
"FK_Channels_FFmpegProfiles_FFmpegProfileId",
|
||||
x => x.FFmpegProfileId,
|
||||
"FFmpegProfiles",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"Playouts",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
ChannelId = table.Column<int>("INTEGER", nullable: false),
|
||||
ProgramScheduleId = table.Column<int>("INTEGER", nullable: false),
|
||||
ProgramSchedulePlayoutType = table.Column<int>("INTEGER", nullable: false),
|
||||
Anchor_NextScheduleItemId = table.Column<int>("INTEGER", nullable: true),
|
||||
Anchor_NextStart = table.Column<DateTimeOffset>("TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Playouts", x => x.Id);
|
||||
table.ForeignKey(
|
||||
"FK_Playouts_Channels_ChannelId",
|
||||
x => x.ChannelId,
|
||||
"Channels",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
"FK_Playouts_ProgramScheduleItems_Anchor_NextScheduleItemId",
|
||||
x => x.Anchor_NextScheduleItemId,
|
||||
"ProgramScheduleItems",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
"FK_Playouts_ProgramSchedules_ProgramScheduleId",
|
||||
x => x.ProgramScheduleId,
|
||||
"ProgramSchedules",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"PlayoutItems",
|
||||
table => new
|
||||
{
|
||||
Id = table.Column<int>("INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
MediaItemId = table.Column<int>("INTEGER", nullable: false),
|
||||
Start = table.Column<DateTimeOffset>("TEXT", nullable: false),
|
||||
Finish = table.Column<DateTimeOffset>("TEXT", nullable: false),
|
||||
PlayoutId = table.Column<int>("INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PlayoutItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
"FK_PlayoutItems_MediaItems_MediaItemId",
|
||||
x => x.MediaItemId,
|
||||
"MediaItems",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
"FK_PlayoutItems_Playouts_PlayoutId",
|
||||
x => x.PlayoutId,
|
||||
"Playouts",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"PlayoutProgramScheduleItemAnchors",
|
||||
table => new
|
||||
{
|
||||
PlayoutId = table.Column<int>("INTEGER", nullable: false),
|
||||
ProgramScheduleId = table.Column<int>("INTEGER", nullable: false),
|
||||
MediaCollectionId = table.Column<int>("INTEGER", nullable: false),
|
||||
EnumeratorState_Seed = table.Column<int>("INTEGER", nullable: true),
|
||||
EnumeratorState_Index = table.Column<int>("INTEGER", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey(
|
||||
"PK_PlayoutProgramScheduleItemAnchors",
|
||||
x => new { x.PlayoutId, x.ProgramScheduleId, x.MediaCollectionId });
|
||||
table.ForeignKey(
|
||||
"FK_PlayoutProgramScheduleItemAnchors_MediaCollections_MediaCollectionId",
|
||||
x => x.MediaCollectionId,
|
||||
"MediaCollections",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
"FK_PlayoutProgramScheduleItemAnchors_Playouts_PlayoutId",
|
||||
x => x.PlayoutId,
|
||||
"Playouts",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
"FK_PlayoutProgramScheduleItemAnchors_ProgramSchedules_ProgramScheduleId",
|
||||
x => x.ProgramScheduleId,
|
||||
"ProgramSchedules",
|
||||
"Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_Channels_FFmpegProfileId",
|
||||
"Channels",
|
||||
"FFmpegProfileId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_Channels_Number",
|
||||
"Channels",
|
||||
"Number",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_ConfigElements_Key",
|
||||
"ConfigElements",
|
||||
"Key",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_FFmpegProfiles_ResolutionId",
|
||||
"FFmpegProfiles",
|
||||
"ResolutionId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_MediaCollections_Name",
|
||||
"MediaCollections",
|
||||
"Name",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_MediaItems_MediaSourceId",
|
||||
"MediaItems",
|
||||
"MediaSourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_MediaItemSimpleMediaCollection_SimpleMediaCollectionsId",
|
||||
"MediaItemSimpleMediaCollection",
|
||||
"SimpleMediaCollectionsId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_MediaSources_Name",
|
||||
"MediaSources",
|
||||
"Name",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_PlayoutItems_MediaItemId",
|
||||
"PlayoutItems",
|
||||
"MediaItemId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_PlayoutItems_PlayoutId",
|
||||
"PlayoutItems",
|
||||
"PlayoutId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_PlayoutProgramScheduleItemAnchors_MediaCollectionId",
|
||||
"PlayoutProgramScheduleItemAnchors",
|
||||
"MediaCollectionId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_PlayoutProgramScheduleItemAnchors_ProgramScheduleId",
|
||||
"PlayoutProgramScheduleItemAnchors",
|
||||
"ProgramScheduleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_Playouts_Anchor_NextScheduleItemId",
|
||||
"Playouts",
|
||||
"Anchor_NextScheduleItemId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_Playouts_ChannelId",
|
||||
"Playouts",
|
||||
"ChannelId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_Playouts_ProgramScheduleId",
|
||||
"Playouts",
|
||||
"ProgramScheduleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_PlexMediaSourceConnections_PlexMediaSourceId",
|
||||
"PlexMediaSourceConnections",
|
||||
"PlexMediaSourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_PlexMediaSourceLibraries_PlexMediaSourceId",
|
||||
"PlexMediaSourceLibraries",
|
||||
"PlexMediaSourceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_ProgramScheduleItems_MediaCollectionId",
|
||||
"ProgramScheduleItems",
|
||||
"MediaCollectionId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_ProgramScheduleItems_ProgramScheduleId",
|
||||
"ProgramScheduleItems",
|
||||
"ProgramScheduleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_ProgramSchedules_Name",
|
||||
"ProgramSchedules",
|
||||
"Name",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_TelevisionMediaCollections_ShowTitle_SeasonNumber",
|
||||
"TelevisionMediaCollections",
|
||||
new[] { "ShowTitle", "SeasonNumber" },
|
||||
unique: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
"ConfigElements");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"GenericIntegerIds");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"LocalMediaSources");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"MediaCollectionSummaries");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"MediaItemSimpleMediaCollection");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"PlayoutItems");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"PlayoutProgramScheduleItemAnchors");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"PlexMediaSourceConnections");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"PlexMediaSourceLibraries");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"ProgramScheduleDurationItems");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"ProgramScheduleFloodItems");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"ProgramScheduleMultipleItems");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"ProgramScheduleOneItems");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"TelevisionMediaCollections");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"SimpleMediaCollections");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"MediaItems");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"Playouts");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"PlexMediaSources");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"Channels");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"ProgramScheduleItems");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"MediaSources");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"FFmpegProfiles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"MediaCollections");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"ProgramSchedules");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"Resolutions");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,978 @@
|
||||
// <auto-generated />
|
||||
|
||||
using System;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(TvContext))]
|
||||
internal class TvContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "5.0.3");
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.AggregateModels.GenericIntegerId",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.ToTable("GenericIntegerIds");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.AggregateModels.MediaCollectionSummary",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsSimple")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ItemCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.ToTable("MediaCollectionSummaries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.Channel",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("FFmpegProfileId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Logo")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Number")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("StreamingMode")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("UniqueId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("FFmpegProfileId");
|
||||
|
||||
b.HasIndex("Number")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Channels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.ConfigElement",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Key")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ConfigElements");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.FFmpegProfile",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AudioBitrate")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AudioBufferSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AudioChannels")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AudioCodec")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("AudioSampleRate")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AudioVolume")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("NormalizeAudio")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("NormalizeAudioCodec")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("NormalizeResolution")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("NormalizeVideoCodec")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ResolutionId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ThreadCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Transcode")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("VideoBitrate")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("VideoBufferSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("VideoCodec")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ResolutionId");
|
||||
|
||||
b.ToTable("FFmpegProfiles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.MediaCollection",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MediaCollections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.MediaItem",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("LastWriteTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("MediaSourceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Path")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MediaSourceId");
|
||||
|
||||
b.ToTable("MediaItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.MediaSource",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SourceType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("MediaSources");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.Playout",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ChannelId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ProgramScheduleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ProgramSchedulePlayoutType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ChannelId");
|
||||
|
||||
b.HasIndex("ProgramScheduleId");
|
||||
|
||||
b.ToTable("Playouts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.PlayoutItem",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Finish")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("MediaItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("PlayoutId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Start")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MediaItemId");
|
||||
|
||||
b.HasIndex("PlayoutId");
|
||||
|
||||
b.ToTable("PlayoutItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("PlayoutId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ProgramScheduleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MediaCollectionId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("PlayoutId", "ProgramScheduleId", "MediaCollectionId");
|
||||
|
||||
b.HasIndex("MediaCollectionId");
|
||||
|
||||
b.HasIndex("ProgramScheduleId");
|
||||
|
||||
b.ToTable("PlayoutProgramScheduleItemAnchors");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.PlexMediaSourceConnection",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("PlexMediaSourceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Uri")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PlexMediaSourceId");
|
||||
|
||||
b.ToTable("PlexMediaSourceConnections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.PlexMediaSourceLibrary",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("MediaType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("PlexMediaSourceId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PlexMediaSourceId");
|
||||
|
||||
b.ToTable("PlexMediaSourceLibraries");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.ProgramSchedule",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MediaCollectionPlaybackOrder")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ProgramSchedules");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.ProgramScheduleItem",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Index")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MediaCollectionId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ProgramScheduleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<TimeSpan?>("StartTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MediaCollectionId");
|
||||
|
||||
b.HasIndex("ProgramScheduleId");
|
||||
|
||||
b.ToTable("ProgramScheduleItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.Resolution",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Height")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Width")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Resolutions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"MediaItemSimpleMediaCollection",
|
||||
b =>
|
||||
{
|
||||
b.Property<int>("ItemsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SimpleMediaCollectionsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("ItemsId", "SimpleMediaCollectionsId");
|
||||
|
||||
b.HasIndex("SimpleMediaCollectionsId");
|
||||
|
||||
b.ToTable("MediaItemSimpleMediaCollection");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.SimpleMediaCollection",
|
||||
b =>
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection");
|
||||
|
||||
b.ToTable("SimpleMediaCollections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.TelevisionMediaCollection",
|
||||
b =>
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaCollection");
|
||||
|
||||
b.Property<int?>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ShowTitle")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasIndex("ShowTitle", "SeasonNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("TelevisionMediaCollections");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.LocalMediaSource",
|
||||
b =>
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaSource");
|
||||
|
||||
b.Property<string>("Folder")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("MediaType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.ToTable("LocalMediaSources");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.PlexMediaSource",
|
||||
b =>
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaSource");
|
||||
|
||||
b.Property<string>("ClientIdentifier")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProductVersion")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.ToTable("PlexMediaSources");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.ProgramScheduleItemDuration",
|
||||
b =>
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
|
||||
|
||||
b.Property<bool>("OfflineTail")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<TimeSpan>("PlayoutDuration")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.ToTable("ProgramScheduleDurationItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.ProgramScheduleItemFlood",
|
||||
b =>
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
|
||||
|
||||
b.ToTable("ProgramScheduleFloodItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.ProgramScheduleItemMultiple",
|
||||
b =>
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
|
||||
|
||||
b.Property<int>("Count")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.ToTable("ProgramScheduleMultipleItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.ProgramScheduleItemOne",
|
||||
b =>
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.ProgramScheduleItem");
|
||||
|
||||
b.ToTable("ProgramScheduleOneItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.Channel",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.FFmpegProfile", "FFmpegProfile")
|
||||
.WithMany()
|
||||
.HasForeignKey("FFmpegProfileId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("FFmpegProfile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.FFmpegProfile",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.Resolution", "Resolution")
|
||||
.WithMany()
|
||||
.HasForeignKey("ResolutionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Resolution");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.MediaItem",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaSource", "Source")
|
||||
.WithMany()
|
||||
.HasForeignKey("MediaSourceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.OwnsOne(
|
||||
"ErsatzTV.Core.Domain.MediaMetadata",
|
||||
"Metadata",
|
||||
b1 =>
|
||||
{
|
||||
b1.Property<int>("MediaItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<DateTime?>("Aired")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<string>("AudioCodec")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<string>("ContentRating")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<string>("Description")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<string>("DisplayAspectRatio")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<TimeSpan>("Duration")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<int?>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("Height")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("MediaType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<string>("SampleAspectRatio")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<int?>("SeasonNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<string>("Subtitle")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<string>("Title")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<string>("VideoCodec")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.Property<int>("VideoScanType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("Width")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.HasKey("MediaItemId");
|
||||
|
||||
b1.ToTable("MediaItems");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("MediaItemId");
|
||||
});
|
||||
|
||||
b.Navigation("Metadata");
|
||||
|
||||
b.Navigation("Source");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.Playout",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.Channel", "Channel")
|
||||
.WithMany("Playouts")
|
||||
.HasForeignKey("ChannelId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule")
|
||||
.WithMany("Playouts")
|
||||
.HasForeignKey("ProgramScheduleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.OwnsOne(
|
||||
"ErsatzTV.Core.Domain.PlayoutAnchor",
|
||||
"Anchor",
|
||||
b1 =>
|
||||
{
|
||||
b1.Property<int>("PlayoutId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("NextScheduleItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<DateTimeOffset>("NextStart")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b1.HasKey("PlayoutId");
|
||||
|
||||
b1.HasIndex("NextScheduleItemId");
|
||||
|
||||
b1.ToTable("Playouts");
|
||||
|
||||
b1.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", "NextScheduleItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("NextScheduleItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("PlayoutId");
|
||||
|
||||
b1.Navigation("NextScheduleItem");
|
||||
});
|
||||
|
||||
b.Navigation("Anchor");
|
||||
|
||||
b.Navigation("Channel");
|
||||
|
||||
b.Navigation("ProgramSchedule");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.PlayoutItem",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaItem", "MediaItem")
|
||||
.WithMany()
|
||||
.HasForeignKey("MediaItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout")
|
||||
.WithMany("Items")
|
||||
.HasForeignKey("PlayoutId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("MediaItem");
|
||||
|
||||
b.Navigation("Playout");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.PlayoutProgramScheduleAnchor",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection")
|
||||
.WithMany()
|
||||
.HasForeignKey("MediaCollectionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("ErsatzTV.Core.Domain.Playout", "Playout")
|
||||
.WithMany("ProgramScheduleAnchors")
|
||||
.HasForeignKey("PlayoutId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProgramScheduleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.OwnsOne(
|
||||
"ErsatzTV.Core.Domain.MediaCollectionEnumeratorState",
|
||||
"EnumeratorState",
|
||||
b1 =>
|
||||
{
|
||||
b1.Property<int>("PlayoutProgramScheduleAnchorPlayoutId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("PlayoutProgramScheduleAnchorProgramScheduleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("PlayoutProgramScheduleAnchorMediaCollectionId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("Index")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.Property<int>("Seed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b1.HasKey(
|
||||
"PlayoutProgramScheduleAnchorPlayoutId",
|
||||
"PlayoutProgramScheduleAnchorProgramScheduleId",
|
||||
"PlayoutProgramScheduleAnchorMediaCollectionId");
|
||||
|
||||
b1.ToTable("PlayoutProgramScheduleItemAnchors");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey(
|
||||
"PlayoutProgramScheduleAnchorPlayoutId",
|
||||
"PlayoutProgramScheduleAnchorProgramScheduleId",
|
||||
"PlayoutProgramScheduleAnchorMediaCollectionId");
|
||||
});
|
||||
|
||||
b.Navigation("EnumeratorState");
|
||||
|
||||
b.Navigation("MediaCollection");
|
||||
|
||||
b.Navigation("Playout");
|
||||
|
||||
b.Navigation("ProgramSchedule");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.PlexMediaSourceConnection",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", null)
|
||||
.WithMany("Connections")
|
||||
.HasForeignKey("PlexMediaSourceId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.PlexMediaSourceLibrary",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.PlexMediaSource", null)
|
||||
.WithMany("Libraries")
|
||||
.HasForeignKey("PlexMediaSourceId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.ProgramScheduleItem",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaCollection", "MediaCollection")
|
||||
.WithMany()
|
||||
.HasForeignKey("MediaCollectionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("ErsatzTV.Core.Domain.ProgramSchedule", "ProgramSchedule")
|
||||
.WithMany("Items")
|
||||
.HasForeignKey("ProgramScheduleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("MediaCollection");
|
||||
|
||||
b.Navigation("ProgramSchedule");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"MediaItemSimpleMediaCollection",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaItem", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ItemsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("ErsatzTV.Core.Domain.SimpleMediaCollection", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("SimpleMediaCollectionsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.SimpleMediaCollection",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("ErsatzTV.Core.Domain.SimpleMediaCollection", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.TelevisionMediaCollection",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaCollection", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("ErsatzTV.Core.Domain.TelevisionMediaCollection", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.LocalMediaSource",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaSource", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("ErsatzTV.Core.Domain.LocalMediaSource", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.PlexMediaSource",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.MediaSource", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("ErsatzTV.Core.Domain.PlexMediaSource", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.ProgramScheduleItemDuration",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemDuration", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.ProgramScheduleItemFlood",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemFlood", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.ProgramScheduleItemMultiple",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemMultiple", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.ProgramScheduleItemOne",
|
||||
b =>
|
||||
{
|
||||
b.HasOne("ErsatzTV.Core.Domain.ProgramScheduleItem", null)
|
||||
.WithOne()
|
||||
.HasForeignKey("ErsatzTV.Core.Domain.ProgramScheduleItemOne", "Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Channel", b => { b.Navigation("Playouts"); });
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.Playout",
|
||||
b =>
|
||||
{
|
||||
b.Navigation("Items");
|
||||
|
||||
b.Navigation("ProgramScheduleAnchors");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.ProgramSchedule",
|
||||
b =>
|
||||
{
|
||||
b.Navigation("Items");
|
||||
|
||||
b.Navigation("Playouts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity(
|
||||
"ErsatzTV.Core.Domain.PlexMediaSource",
|
||||
b =>
|
||||
{
|
||||
b.Navigation("Connections");
|
||||
|
||||
b.Navigation("Libraries");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,10 @@
|
||||
<PackageReference Include="LanguageExt.Core" Version="3.4.15" />
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MudBlazor" Version="5.0.0" />
|
||||
<PackageReference Include="Refit.HttpClientFactory" Version="6.0.1" />
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
using LanguageExt;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -28,7 +29,7 @@ namespace ErsatzTV.Extensions
|
||||
|
||||
private static TvContext Migrate(TvContext context)
|
||||
{
|
||||
context.Database.EnsureCreated();
|
||||
context.Database.Migrate();
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
using ErsatzTV.Core.Domain;
|
||||
using static LanguageExt.Prelude;
|
||||
|
||||
namespace ErsatzTV.Models.UI
|
||||
{
|
||||
public static class MediaItemExtensions
|
||||
{
|
||||
public static string GetDisplayDuration(this MediaItem mediaItem) =>
|
||||
string.Format(
|
||||
mediaItem.Metadata.Duration.TotalHours >= 1 ? @"{0:h\:mm\:ss}" : @"{0:mm\:ss}",
|
||||
mediaItem.Metadata.Duration);
|
||||
|
||||
public static string GetDisplayTitle(this MediaItem mediaItem) =>
|
||||
mediaItem.Metadata.MediaType == MediaType.TvShow &&
|
||||
Optional(mediaItem.Metadata.SeasonNumber).IsSome &&
|
||||
Optional(mediaItem.Metadata.EpisodeNumber).IsSome
|
||||
? $"{mediaItem.Metadata.Title} s{mediaItem.Metadata.SeasonNumber:00}e{mediaItem.Metadata.EpisodeNumber:00}"
|
||||
: mediaItem.Metadata.Title;
|
||||
|
||||
public static string GetDisplayMediaType(this MediaItem mediaItem) =>
|
||||
mediaItem.Metadata.MediaType == MediaType.TvShow
|
||||
? "TV Show"
|
||||
: mediaItem.Metadata.MediaType.ToString();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,16 @@
|
||||
@page "/media/collections/{Id:int}/items"
|
||||
@inject IDbContextFactory<TvContext> DbFactory
|
||||
@using ErsatzTV.Application.MediaCollections
|
||||
@using ErsatzTV.Application.MediaCollections.Commands
|
||||
@using ErsatzTV.Application.MediaCollections.Queries
|
||||
@using ErsatzTV.Application.MediaItems
|
||||
@using ErsatzTV.Application.MediaItems.Queries
|
||||
@using Unit = LanguageExt.Unit
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IMediator Mediator
|
||||
@inject ILogger<MediaCollectionItemsEditor> Logger
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
<MudTable Hover="true" Items="_mediaCollection.Items">
|
||||
<MudTable Hover="true" Items="_collectionItems">
|
||||
<ToolBarContent>
|
||||
<MudText Typo="Typo.h6">@_mediaCollection.Name Media Items</MudText>
|
||||
</ToolBarContent>
|
||||
@@ -13,20 +21,20 @@
|
||||
<MudTh>Duration</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Source">@context.Source.Name</MudTd>
|
||||
<MudTd DataLabel="Type">@context.GetDisplayMediaType()</MudTd>
|
||||
<MudTd DataLabel="Title">@context.GetDisplayTitle()</MudTd>
|
||||
<MudTd DataLabel="Duration">@context.Metadata.Duration.ToString(@"hh\:mm\:ss\.fff")</MudTd>
|
||||
<MudTd DataLabel="Source">@context.Source</MudTd>
|
||||
<MudTd DataLabel="Type">@context.MediaType</MudTd>
|
||||
<MudTd DataLabel="Title">@context.Title</MudTd>
|
||||
<MudTd DataLabel="Duration">@context.Duration</MudTd>
|
||||
</RowTemplate>
|
||||
<PagerContent>
|
||||
@if (_mediaCollection.Items.Count > 0)
|
||||
@if (_collectionItems.Any())
|
||||
{
|
||||
<MudTablePager/>
|
||||
}
|
||||
</PagerContent>
|
||||
</MudTable>
|
||||
|
||||
<MudTable @ref="_table" Hover="true" ServerData="@(new Func<TableState, Task<TableData<MediaItem>>>(ServerReload))" Class="mt-8">
|
||||
<MudTable @ref="_table" Hover="true" ServerData="@(new Func<TableState, Task<TableData<MediaItemSearchResultViewModel>>>(ServerReload))" Class="mt-8">
|
||||
<ToolBarContent>
|
||||
<MudText Typo="Typo.h6">All Media Items</MudText>
|
||||
<MudToolBarSpacer/>
|
||||
@@ -41,10 +49,10 @@
|
||||
<MudTh>Duration</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Source">@context.Source.Name</MudTd>
|
||||
<MudTd DataLabel="Type">@context.GetDisplayMediaType()</MudTd>
|
||||
<MudTd DataLabel="Title">@context.GetDisplayTitle()</MudTd>
|
||||
<MudTd DataLabel="Duration">@context.GetDisplayDuration()</MudTd>
|
||||
<MudTd DataLabel="Source">@context.Source</MudTd>
|
||||
<MudTd DataLabel="Type">@context.MediaType</MudTd>
|
||||
<MudTd DataLabel="Title">@context.Title</MudTd>
|
||||
<MudTd DataLabel="Duration">@context.Duration</MudTd>
|
||||
</RowTemplate>
|
||||
<PagerContent>
|
||||
<MudTablePager/>
|
||||
@@ -59,32 +67,27 @@
|
||||
[Parameter]
|
||||
public int Id { get; set; }
|
||||
|
||||
private SimpleMediaCollection _mediaCollection;
|
||||
private MediaCollectionViewModel _mediaCollection;
|
||||
private IEnumerable<MediaItemSearchResultViewModel> _collectionItems;
|
||||
|
||||
protected override async Task OnParametersSetAsync() => await LoadMediaCollectionAsync();
|
||||
|
||||
private List<int> _mediaItemIds;
|
||||
private IEnumerable<MediaItem> _pagedData;
|
||||
private MudTable<MediaItem> _table;
|
||||
private IEnumerable<MediaItemSearchResultViewModel> _pagedData;
|
||||
private MudTable<MediaItemSearchResultViewModel> _table;
|
||||
|
||||
private int _totalItems;
|
||||
private string _searchString;
|
||||
|
||||
private async Task<TableData<MediaItem>> ServerReload(TableState state)
|
||||
private async Task<TableData<MediaItemSearchResultViewModel>> ServerReload(TableState state)
|
||||
{
|
||||
await using TvContext context = DbFactory.CreateDbContext();
|
||||
IQueryable<MediaItem> data = from c in context.MediaItems.Include(c => c.Source) select c;
|
||||
|
||||
if (!string.IsNullOrEmpty(_searchString))
|
||||
{
|
||||
data = data.Where(c => EF.Functions.Like(c.Metadata.Title, $"%{_searchString}%"));
|
||||
}
|
||||
List<MediaItemSearchResultViewModel> data = await Mediator.Send(new SearchAllMediaItems(_searchString));
|
||||
|
||||
_mediaItemIds = data.Map(c => c.Id).ToList();
|
||||
_totalItems = data.Count();
|
||||
_totalItems = data.Count;
|
||||
|
||||
_pagedData = data.OrderBy(c => c.Id).Skip(state.Page * state.PageSize).Take(state.PageSize).ToArray();
|
||||
return new TableData<MediaItem> { TotalItems = _totalItems, Items = _pagedData };
|
||||
return new TableData<MediaItemSearchResultViewModel> { TotalItems = _totalItems, Items = _pagedData };
|
||||
}
|
||||
|
||||
private async Task OnSearch(string text)
|
||||
@@ -95,32 +98,24 @@
|
||||
|
||||
private async Task AddResultsAsync()
|
||||
{
|
||||
await using TvContext context = DbFactory.CreateDbContext();
|
||||
|
||||
SimpleMediaCollection mediaCollection = await context.SimpleMediaCollections.FindAsync(_mediaCollection.Id);
|
||||
await context.Entry(mediaCollection).Collection(cg => cg.Items).LoadAsync();
|
||||
IEnumerable<int> existingMediaItems = _mediaCollection.Items.Select(c => c.Id);
|
||||
IQueryable<MediaItem> mediaItemsToAdd = from c in context.MediaItems
|
||||
where _mediaItemIds.Contains(c.Id) && !existingMediaItems.Contains(c.Id)
|
||||
select c;
|
||||
foreach (MediaItem mediaItem in mediaItemsToAdd)
|
||||
{
|
||||
mediaCollection.Items.Add(mediaItem);
|
||||
}
|
||||
context.MediaCollections.Update(mediaCollection);
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
await LoadMediaCollectionAsync();
|
||||
Either<BaseError, Unit> result = await Mediator.Send(new AddItemsToSimpleMediaCollection(Id, _mediaItemIds));
|
||||
await result.Match(
|
||||
async _ => await LoadMediaCollectionAsync(),
|
||||
error =>
|
||||
{
|
||||
Snackbar.Add(error.Value, Severity.Error);
|
||||
Logger.LogError("Error adding items to media collection: {Error}", error.Value);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
|
||||
private async Task LoadMediaCollectionAsync()
|
||||
{
|
||||
await using TvContext context = DbFactory.CreateDbContext();
|
||||
_mediaCollection = await context.SimpleMediaCollections
|
||||
.AsNoTracking()
|
||||
.Include(cg => cg.Items)
|
||||
.ThenInclude(c => c.Source)
|
||||
.FirstAsync(cg => cg.Id == Id);
|
||||
Option<Tuple<MediaCollectionViewModel, List<MediaItemSearchResultViewModel>>> maybeResult =
|
||||
await Mediator.Send(new GetSimpleMediaCollectionWithItemsById(Id));
|
||||
maybeResult.Match(
|
||||
result => (_mediaCollection, _collectionItems) = result,
|
||||
() => NavigationManager.NavigateTo("404"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
@page "/media/items"
|
||||
@inject IDbContextFactory<TvContext> DbFactory
|
||||
|
||||
<MudTabs Elevation="1">
|
||||
<MudTabPanel Text="TV Shows">
|
||||
|
||||
+1
-1
@@ -54,4 +54,4 @@ namespace ErsatzTV
|
||||
.UseKestrel(options => options.AddServerHeader = false))
|
||||
.UseSerilog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,21 +9,16 @@
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="@(_ => DrawerToggle())"/>
|
||||
<MudText Typo="Typo.h5" Class="ml-3">ErsatzTV</MudText>
|
||||
<MudAppBarSpacer/>
|
||||
<MudTooltip Text="Channels M3U">
|
||||
<MudIconButton Icon="@Icons.Custom.FileFormats.FileVideo" Color="Color.Inherit" Link="/iptv/channels.m3u" Target="_blank"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="EPG XMLTV">
|
||||
<MudIconButton Icon="@Icons.Custom.FileFormats.FileCode" Color="Color.Inherit" Link="/iptv/xmltv.xml" Target="_blank"/>
|
||||
</MudTooltip>
|
||||
<MudLink Style="@($"color:{Colors.Shades.White}")" Color="Color.Inherit" Href="/iptv/channels.m3u" Target="_blank" Underline="Underline.None">M3U</MudLink>
|
||||
<MudLink Style="@($"color:{Colors.Shades.White}")" Color="Color.Inherit" Href="/iptv/xmltv.xml" Target="_blank" Class="mx-4" Underline="Underline.None">XMLTV</MudLink>
|
||||
<MudLink Style="@($"color:{Colors.Shades.White}")" Color="Color.Inherit" Href="/swagger" Target="_blank" Class="mr-4" Underline="Underline.None">API</MudLink>
|
||||
<MudDivider Vertical="true" FlexItem="true" DividerType="DividerType.Middle" Class="mx-4 my-5"/>
|
||||
<MudTooltip Text="Discord">
|
||||
<MudIconButton Icon="fab fa-discord" Color="Color.Inherit" Link="https://discord.gg/hHaJm3yGy6" Target="_blank"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="GitHub">
|
||||
<MudIconButton Icon="@Icons.Custom.Brands.GitHub" Color="Color.Inherit" Link="https://github.com/jasongdove/ErsatzTV" Target="_blank"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="OpenAPI">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Api" Color="Color.Inherit" Link="/swagger" Target="_blank"/>
|
||||
</MudTooltip>
|
||||
</MudAppBar>
|
||||
<MudDrawer @bind-Open="_drawerOpen" Elevation="2">
|
||||
<MudNavMenu>
|
||||
|
||||
+5
-15
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Channels;
|
||||
using ErsatzTV.Application;
|
||||
@@ -100,23 +99,14 @@ namespace ErsatzTV
|
||||
// string xmltvPath = Path.Combine(appDataFolder, "xmltv.xml");
|
||||
// Log.Logger.Information("XMLTV is at {XmltvPath}", xmltvPath);
|
||||
|
||||
services.AddDbContextFactory<TvContext>(
|
||||
options =>
|
||||
{
|
||||
options.UseSqlite(
|
||||
$"Data Source={FileSystemLayout.DatabasePath}",
|
||||
o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));
|
||||
|
||||
if (Debugger.IsAttached)
|
||||
{
|
||||
options.EnableSensitiveDataLogging();
|
||||
}
|
||||
});
|
||||
|
||||
services.AddDbContext<TvContext>(
|
||||
options => options.UseSqlite(
|
||||
$"Data Source={FileSystemLayout.DatabasePath}",
|
||||
o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)));
|
||||
o =>
|
||||
{
|
||||
o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery);
|
||||
o.MigrationsAssembly("ErsatzTV.Infrastructure");
|
||||
}));
|
||||
|
||||
services.AddMediatR(typeof(GetAllChannels).Assembly);
|
||||
|
||||
|
||||
@@ -20,7 +20,5 @@
|
||||
@using ErsatzTV.Core
|
||||
@using ErsatzTV.Core.Domain
|
||||
@using ErsatzTV.Infrastructure.Data
|
||||
@using ErsatzTV.Models
|
||||
@using ErsatzTV.Models.UI
|
||||
@using ErsatzTV.Shared
|
||||
@using ErsatzTV.ViewModels
|
||||
+1
-1
@@ -5,7 +5,7 @@ services:
|
||||
build:
|
||||
context: .
|
||||
ports:
|
||||
- "8409:80"
|
||||
- "8409:8409"
|
||||
volumes:
|
||||
- ersatztv:/root/.local/share/ersatztv
|
||||
#- /media/shared:/media/shared:ro
|
||||
|
||||
Reference in New Issue
Block a user