Interface improvements (#34)
* fix plex library synchronization * add basic plex movie synchronization * proxy plex movie thumbnails (posters) * add plex path replacements * use transcoded plex artwork * remove unsynchronized plex movies on save; queue plex library scan on save * log plex path replacements * prefer buttons instead of menus * lock plex libraries before sync * add movie to collection from paged view * fix plex import memory use; quick add seasons/shows * quick add episode to collection * add favicon * add search page * disable plex for now
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
namespace ErsatzTV.Core.Domain
|
||||
{
|
||||
public class PlexMediaFile : MediaFile
|
||||
{
|
||||
public int PlexId { get; set; }
|
||||
public string Key { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace ErsatzTV.Core.Domain
|
||||
{
|
||||
public class PlexMediaItemPart
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int PlexId { get; set; }
|
||||
public string Key { get; set; }
|
||||
public int Duration { get; set; }
|
||||
public string File { get; set; }
|
||||
public int Size { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,5 @@
|
||||
public class PlexMovie : Movie
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public PlexMediaItemPart Part { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,5 +10,6 @@ namespace ErsatzTV.Core.Domain
|
||||
|
||||
// public bool IsOwned { get; set; }
|
||||
public List<PlexConnection> Connections { get; set; }
|
||||
public List<PlexPathReplacement> PathReplacements { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace ErsatzTV.Core.Domain
|
||||
{
|
||||
public class PlexPathReplacement
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string PlexPath { get; set; }
|
||||
public string LocalPath { get; set; }
|
||||
public int PlexMediaSourceId { get; set; }
|
||||
public PlexMediaSource PlexMediaSource { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -15,32 +15,25 @@ namespace ErsatzTV.Core.FFmpeg
|
||||
public Process ForPlayoutItem(
|
||||
string ffmpegPath,
|
||||
Channel channel,
|
||||
PlayoutItem item,
|
||||
MediaVersion version,
|
||||
string path,
|
||||
DateTimeOffset start,
|
||||
DateTimeOffset now)
|
||||
{
|
||||
MediaVersion version = item.MediaItem switch
|
||||
{
|
||||
Movie m => m.MediaVersions.Head(),
|
||||
Episode e => e.MediaVersions.Head(),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(item))
|
||||
};
|
||||
|
||||
FFmpegPlaybackSettings playbackSettings = _playbackSettingsCalculator.CalculateSettings(
|
||||
channel.StreamingMode,
|
||||
channel.FFmpegProfile,
|
||||
version,
|
||||
item.StartOffset,
|
||||
start,
|
||||
now);
|
||||
|
||||
MediaFile file = version.MediaFiles.Head();
|
||||
|
||||
FFmpegProcessBuilder builder = new FFmpegProcessBuilder(ffmpegPath)
|
||||
.WithThreads(playbackSettings.ThreadCount)
|
||||
.WithQuiet()
|
||||
.WithFormatFlags(playbackSettings.FormatFlags)
|
||||
.WithRealtimeOutput(playbackSettings.RealtimeOutput)
|
||||
.WithSeek(playbackSettings.StreamSeek)
|
||||
.WithInput(file.Path);
|
||||
.WithInput(path);
|
||||
|
||||
playbackSettings.ScaledSize.Match(
|
||||
scaledSize =>
|
||||
@@ -86,7 +79,7 @@ namespace ErsatzTV.Core.FFmpeg
|
||||
return builder.WithPlaybackArgs(playbackSettings)
|
||||
.WithMetadata(channel)
|
||||
.WithFormat("mpegts")
|
||||
.WithDuration(item.Start + version.Duration - now)
|
||||
.WithDuration(start + version.Duration - now)
|
||||
.WithPipe()
|
||||
.Build();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
using ErsatzTV.Core.Domain;
|
||||
using System;
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Core.Interfaces.Metadata
|
||||
{
|
||||
public interface IFallbackMetadataProvider
|
||||
{
|
||||
ShowMetadata GetFallbackMetadataForShow(string showFolder);
|
||||
Tuple<EpisodeMetadata, int> GetFallbackMetadata(Episode episode);
|
||||
MovieMetadata GetFallbackMetadata(Movie movie);
|
||||
string GetSortTitle(string title);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories
|
||||
Task<LibraryPath> Add(LibraryPath libraryPath);
|
||||
Task<Option<Library>> Get(int libraryId);
|
||||
Task<Option<LocalLibrary>> GetLocal(int libraryId);
|
||||
Task<List<LocalLibrary>> GetAllLocal();
|
||||
Task<List<Library>> GetAll();
|
||||
Task<Unit> UpdateLastScan(Library library);
|
||||
Task<List<LibraryPath>> GetLocalPaths(int libraryId);
|
||||
Task<Option<LibraryPath>> GetPath(int libraryPathId);
|
||||
|
||||
@@ -12,15 +12,18 @@ namespace ErsatzTV.Core.Interfaces.Repositories
|
||||
Task<List<MediaSource>> GetAll();
|
||||
Task<List<PlexMediaSource>> GetAllPlex();
|
||||
Task<List<PlexLibrary>> GetPlexLibraries(int plexMediaSourceId);
|
||||
Task<List<PlexPathReplacement>> GetPlexPathReplacements(int plexMediaSourceId);
|
||||
Task<Option<PlexLibrary>> GetPlexLibrary(int plexLibraryId);
|
||||
Task<Option<MediaSource>> Get(int id);
|
||||
Task<Option<PlexMediaSource>> GetPlex(int id);
|
||||
Task<Option<PlexMediaSource>> GetPlexByLibraryId(int plexLibraryId);
|
||||
Task<List<PlexPathReplacement>> GetPlexPathReplacementsByLibraryId(int plexLibraryPathId);
|
||||
Task<int> CountMediaItems(int id);
|
||||
Task Update(LocalMediaSource localMediaSource);
|
||||
Task Update(PlexMediaSource plexMediaSource);
|
||||
Task Update(PlexLibrary plexMediaSourceLibrary);
|
||||
Task Delete(int id);
|
||||
Task DisablePlexLibrarySync(IEnumerable<int> libraryIds);
|
||||
Task DisablePlexLibrarySync(List<int> libraryIds);
|
||||
Task EnablePlexLibrarySync(IEnumerable<int> libraryIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Core.Interfaces.Repositories
|
||||
{
|
||||
public interface ISearchRepository
|
||||
{
|
||||
public Task<List<MediaItem>> SearchMediaItems(string query);
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ namespace ErsatzTV.Core.Iptv
|
||||
xml.WriteAttributeString("system", "xmltv_ns");
|
||||
xml.WriteString($"{s - 1}.{e - 1}.0/1");
|
||||
xml.WriteEndElement(); // episode-num
|
||||
|
||||
|
||||
xml.WriteStartElement("episode-num");
|
||||
xml.WriteAttributeString("system", "onscreen");
|
||||
xml.WriteString($"S{s:00}E{e:00}");
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace ErsatzTV.Core.Metadata
|
||||
return GetTelevisionShowMetadata(fileName, metadata);
|
||||
}
|
||||
|
||||
public static Tuple<EpisodeMetadata, int> GetFallbackMetadata(Episode episode)
|
||||
public Tuple<EpisodeMetadata, int> GetFallbackMetadata(Episode episode)
|
||||
{
|
||||
string path = episode.MediaVersions.Head().MediaFiles.Head().Path;
|
||||
string fileName = Path.GetFileName(path);
|
||||
@@ -26,7 +26,7 @@ namespace ErsatzTV.Core.Metadata
|
||||
return fileName != null ? GetEpisodeMetadata(fileName, metadata) : Tuple(metadata, 0);
|
||||
}
|
||||
|
||||
public static MovieMetadata GetFallbackMetadata(Movie movie)
|
||||
public MovieMetadata GetFallbackMetadata(Movie movie)
|
||||
{
|
||||
string path = movie.MediaVersions.Head().MediaFiles.Head().Path;
|
||||
string fileName = Path.GetFileName(path);
|
||||
@@ -34,8 +34,28 @@ namespace ErsatzTV.Core.Metadata
|
||||
|
||||
return fileName != null ? GetMovieMetadata(fileName, metadata) : metadata;
|
||||
}
|
||||
|
||||
public string GetSortTitle(string title)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
return title;
|
||||
}
|
||||
|
||||
private static Tuple<EpisodeMetadata, int> GetEpisodeMetadata(string fileName, EpisodeMetadata metadata)
|
||||
if (title.StartsWith("the ", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return title.Substring(4);
|
||||
}
|
||||
|
||||
if (title.StartsWith("Æ"))
|
||||
{
|
||||
return title.Replace("Æ", "E");
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
private Tuple<EpisodeMetadata, int> GetEpisodeMetadata(string fileName, EpisodeMetadata metadata)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -55,7 +75,7 @@ namespace ErsatzTV.Core.Metadata
|
||||
return Tuple(metadata, 0);
|
||||
}
|
||||
|
||||
private static MovieMetadata GetMovieMetadata(string fileName, MovieMetadata metadata)
|
||||
private MovieMetadata GetMovieMetadata(string fileName, MovieMetadata metadata)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -76,9 +96,7 @@ namespace ErsatzTV.Core.Metadata
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private static ShowMetadata GetTelevisionShowMetadata(
|
||||
string fileName,
|
||||
ShowMetadata metadata)
|
||||
private ShowMetadata GetTelevisionShowMetadata(string fileName, ShowMetadata metadata)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace ErsatzTV.Core.Metadata
|
||||
() =>
|
||||
{
|
||||
ShowMetadata metadata = _fallbackMetadataProvider.GetFallbackMetadataForShow(showFolder);
|
||||
metadata.SortTitle = GetSortTitle(metadata.Title);
|
||||
metadata.SortTitle = _fallbackMetadataProvider.GetSortTitle(metadata.Title);
|
||||
return metadata;
|
||||
});
|
||||
}
|
||||
@@ -73,9 +73,9 @@ namespace ErsatzTV.Core.Metadata
|
||||
public Task<Unit> RefreshFallbackMetadata(MediaItem mediaItem) =>
|
||||
mediaItem switch
|
||||
{
|
||||
Episode e => ApplyMetadataUpdate(e, FallbackMetadataProvider.GetFallbackMetadata(e))
|
||||
Episode e => ApplyMetadataUpdate(e, _fallbackMetadataProvider.GetFallbackMetadata(e))
|
||||
.ToUnit(),
|
||||
Movie m => ApplyMetadataUpdate(m, FallbackMetadataProvider.GetFallbackMetadata(m)).ToUnit(),
|
||||
Movie m => ApplyMetadataUpdate(m, _fallbackMetadataProvider.GetFallbackMetadata(m)).ToUnit(),
|
||||
_ => Task.FromResult(Unit.Default)
|
||||
};
|
||||
|
||||
@@ -100,11 +100,11 @@ namespace ErsatzTV.Core.Metadata
|
||||
existing.OriginalTitle = metadata.OriginalTitle;
|
||||
existing.ReleaseDate = metadata.ReleaseDate;
|
||||
existing.Year = metadata.Year;
|
||||
existing.SortTitle = metadata.SortTitle ?? GetSortTitle(metadata.Title);
|
||||
existing.SortTitle = metadata.SortTitle ?? _fallbackMetadataProvider.GetSortTitle(metadata.Title);
|
||||
},
|
||||
() =>
|
||||
{
|
||||
metadata.SortTitle ??= GetSortTitle(metadata.Title);
|
||||
metadata.SortTitle ??= _fallbackMetadataProvider.GetSortTitle(metadata.Title);
|
||||
episode.EpisodeMetadata = new List<EpisodeMetadata> { metadata };
|
||||
});
|
||||
|
||||
@@ -126,11 +126,11 @@ namespace ErsatzTV.Core.Metadata
|
||||
existing.OriginalTitle = metadata.OriginalTitle;
|
||||
existing.ReleaseDate = metadata.ReleaseDate;
|
||||
existing.Year = metadata.Year;
|
||||
existing.SortTitle = metadata.SortTitle ?? GetSortTitle(metadata.Title);
|
||||
existing.SortTitle = metadata.SortTitle ?? _fallbackMetadataProvider.GetSortTitle(metadata.Title);
|
||||
},
|
||||
() =>
|
||||
{
|
||||
metadata.SortTitle ??= GetSortTitle(metadata.Title);
|
||||
metadata.SortTitle ??= _fallbackMetadataProvider.GetSortTitle(metadata.Title);
|
||||
movie.MovieMetadata = new List<MovieMetadata> { metadata };
|
||||
});
|
||||
|
||||
@@ -152,11 +152,11 @@ namespace ErsatzTV.Core.Metadata
|
||||
existing.OriginalTitle = metadata.OriginalTitle;
|
||||
existing.ReleaseDate = metadata.ReleaseDate;
|
||||
existing.Year = metadata.Year;
|
||||
existing.SortTitle = metadata.SortTitle ?? GetSortTitle(metadata.Title);
|
||||
existing.SortTitle = metadata.SortTitle ?? _fallbackMetadataProvider.GetSortTitle(metadata.Title);
|
||||
},
|
||||
() =>
|
||||
{
|
||||
metadata.SortTitle ??= GetSortTitle(metadata.Title);
|
||||
metadata.SortTitle ??= _fallbackMetadataProvider.GetSortTitle(metadata.Title);
|
||||
show.ShowMetadata = new List<ShowMetadata> { metadata };
|
||||
});
|
||||
|
||||
@@ -247,7 +247,7 @@ namespace ErsatzTV.Core.Metadata
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogInformation(ex, "Failed to read TV episode nfo metadata from {Path}", nfoFileName);
|
||||
return FallbackMetadataProvider.GetFallbackMetadata(episode);
|
||||
return _fallbackMetadataProvider.GetFallbackMetadata(episode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ namespace ErsatzTV.Core.Metadata
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogInformation(ex, "Failed to read Movie nfo metadata from {Path}", nfoFileName);
|
||||
return FallbackMetadataProvider.GetFallbackMetadata(mediaItem);
|
||||
return _fallbackMetadataProvider.GetFallbackMetadata(mediaItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,26 +293,6 @@ namespace ErsatzTV.Core.Metadata
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string GetSortTitle(string title)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
return title;
|
||||
}
|
||||
|
||||
if (title.StartsWith("the ", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return title.Substring(4);
|
||||
}
|
||||
|
||||
if (title.StartsWith("Æ"))
|
||||
{
|
||||
return title.Replace("Æ", "E");
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
[XmlRoot("movie")]
|
||||
public class MovieNfo
|
||||
{
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ErsatzTV.Core.Plex
|
||||
{
|
||||
public class PlexMediaEntry
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int Duration { get; set; }
|
||||
public int Bitrate { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
public double AspectRatio { get; set; }
|
||||
public int AudioChannels { get; set; }
|
||||
public string AudioCodec { get; set; }
|
||||
public string VideoCodec { get; set; }
|
||||
public string Container { get; set; }
|
||||
public string VideoFrameRate { get; set; }
|
||||
public List<PlexPartEntry> Part { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ErsatzTV.Core.Plex
|
||||
{
|
||||
public class PlexMetadataEntry
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Summary { get; set; }
|
||||
public int Year { get; set; }
|
||||
public string Tagline { get; set; }
|
||||
public string Thumb { get; set; }
|
||||
public string Art { get; set; }
|
||||
public string OriginallyAvailableAt { get; set; }
|
||||
public int AddedAt { get; set; }
|
||||
public int UpdatedAt { get; set; }
|
||||
public List<PlexMediaEntry> Media { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
namespace ErsatzTV.Core.Plex
|
||||
{
|
||||
public class PlexPartEntry
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Key { get; set; }
|
||||
public int Duration { get; set; }
|
||||
public string File { get; set; }
|
||||
public int Size { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user