mirror of https://github.com/ErsatzTV/ErsatzTV.git
8 changed files with 269 additions and 2 deletions
@ -0,0 +1,19 @@ |
|||||||
|
using ErsatzTV.Core.Domain; |
||||||
|
|
||||||
|
namespace ErsatzTV.Application.MediaItems; |
||||||
|
|
||||||
|
public record MediaItemInfo( |
||||||
|
int Id, |
||||||
|
string Kind, |
||||||
|
string LibraryKind, |
||||||
|
string ServerName, |
||||||
|
string LibraryName, |
||||||
|
MediaItemState State, |
||||||
|
TimeSpan Duration, |
||||||
|
string SampleAspectRatio, |
||||||
|
string DisplayAspectRatio, |
||||||
|
string RFrameRate, |
||||||
|
VideoScanKind VideoScanKind, |
||||||
|
int Width, |
||||||
|
int Height, |
||||||
|
List<MediaItemInfoStream> Streams); |
@ -0,0 +1,23 @@ |
|||||||
|
using ErsatzTV.Core.Domain; |
||||||
|
|
||||||
|
namespace ErsatzTV.Application.MediaItems; |
||||||
|
|
||||||
|
public record MediaItemInfoStream( |
||||||
|
int Index, |
||||||
|
MediaStreamKind Kind, |
||||||
|
string Title, |
||||||
|
string Codec, |
||||||
|
string Profile, |
||||||
|
string Language, |
||||||
|
int? Channels, |
||||||
|
bool Default, |
||||||
|
bool Forced, |
||||||
|
bool AttachedPic, |
||||||
|
string PixelFormat, |
||||||
|
string ColorRange, |
||||||
|
string ColorSpace, |
||||||
|
string ColorTransfer, |
||||||
|
string ColorPrimaries, |
||||||
|
int BitsPerRawSample, |
||||||
|
string FileName, |
||||||
|
string MimeType); |
@ -0,0 +1,5 @@ |
|||||||
|
using ErsatzTV.Core; |
||||||
|
|
||||||
|
namespace ErsatzTV.Application.MediaItems; |
||||||
|
|
||||||
|
public record GetMediaItemInfo(int Id) : IRequest<Either<BaseError, MediaItemInfo>>; |
@ -0,0 +1,97 @@ |
|||||||
|
using ErsatzTV.Core; |
||||||
|
using ErsatzTV.Core.Domain; |
||||||
|
using ErsatzTV.Core.Extensions; |
||||||
|
using ErsatzTV.Infrastructure.Data; |
||||||
|
using ErsatzTV.Infrastructure.Extensions; |
||||||
|
using Microsoft.EntityFrameworkCore; |
||||||
|
|
||||||
|
namespace ErsatzTV.Application.MediaItems; |
||||||
|
|
||||||
|
public class GetMediaItemInfoHandler : IRequestHandler<GetMediaItemInfo, Either<BaseError, MediaItemInfo>> |
||||||
|
{ |
||||||
|
private readonly IDbContextFactory<TvContext> _dbContextFactory; |
||||||
|
|
||||||
|
public GetMediaItemInfoHandler(IDbContextFactory<TvContext> dbContextFactory) |
||||||
|
{ |
||||||
|
_dbContextFactory = dbContextFactory; |
||||||
|
} |
||||||
|
|
||||||
|
public async Task<Either<BaseError, MediaItemInfo>> Handle( |
||||||
|
GetMediaItemInfo request, |
||||||
|
CancellationToken cancellationToken) |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); |
||||||
|
|
||||||
|
Option<MediaItemInfo> mediaItem = await dbContext.MediaItems |
||||||
|
.AsNoTracking() |
||||||
|
.Include(i => i.LibraryPath) |
||||||
|
.ThenInclude(lp => lp.Library) |
||||||
|
.ThenInclude(l => l.MediaSource) |
||||||
|
// TODO: support all media types here
|
||||||
|
.Include(i => (i as Movie).MediaVersions) |
||||||
|
.ThenInclude(mv => mv.Streams) |
||||||
|
.Include(i => (i as Episode).MediaVersions) |
||||||
|
.ThenInclude(mv => mv.Streams) |
||||||
|
.SelectOneAsync(i => i.Id, i => i.Id == request.Id) |
||||||
|
.MapT(Project); |
||||||
|
|
||||||
|
return mediaItem.ToEither(BaseError.New("Unable to locate media item")); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
return BaseError.New(ex.ToString()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static MediaItemInfo Project(MediaItem mediaItem) |
||||||
|
{ |
||||||
|
MediaVersion version = mediaItem.GetHeadVersion(); |
||||||
|
|
||||||
|
string serverName = mediaItem.LibraryPath.Library.MediaSource switch |
||||||
|
{ |
||||||
|
PlexMediaSource plexMediaSource => plexMediaSource.ServerName, |
||||||
|
EmbyMediaSource embyMediaSource => embyMediaSource.ServerName, |
||||||
|
JellyfinMediaSource jellyfinMediaSource => jellyfinMediaSource.ServerName, |
||||||
|
_ => null |
||||||
|
}; |
||||||
|
|
||||||
|
return new MediaItemInfo( |
||||||
|
mediaItem.Id, |
||||||
|
mediaItem.GetType().Name, |
||||||
|
mediaItem.LibraryPath.Library.GetType().Name, |
||||||
|
serverName, |
||||||
|
mediaItem.LibraryPath.Library.Name, |
||||||
|
mediaItem.State, |
||||||
|
version.Duration, |
||||||
|
version.SampleAspectRatio, |
||||||
|
version.DisplayAspectRatio, |
||||||
|
version.RFrameRate, |
||||||
|
version.VideoScanKind, |
||||||
|
version.Width, |
||||||
|
version.Height, |
||||||
|
version.Streams.OrderBy(s => s.Index).Map(Project).ToList()); |
||||||
|
} |
||||||
|
|
||||||
|
private static MediaItemInfoStream Project(MediaStream mediaStream) => |
||||||
|
new( |
||||||
|
mediaStream.Index, |
||||||
|
mediaStream.MediaStreamKind, |
||||||
|
mediaStream.Title, |
||||||
|
mediaStream.Codec, |
||||||
|
mediaStream.Profile, |
||||||
|
mediaStream.Language, |
||||||
|
mediaStream.Channels > 0 ? mediaStream.Channels : null, |
||||||
|
mediaStream.Default, |
||||||
|
mediaStream.Forced, |
||||||
|
mediaStream.AttachedPic, |
||||||
|
mediaStream.PixelFormat, |
||||||
|
mediaStream.ColorRange, |
||||||
|
mediaStream.ColorSpace, |
||||||
|
mediaStream.ColorTransfer, |
||||||
|
mediaStream.ColorPrimaries, |
||||||
|
mediaStream.BitsPerRawSample, |
||||||
|
mediaStream.FileName, |
||||||
|
mediaStream.MimeType); |
||||||
|
} |
@ -0,0 +1,62 @@ |
|||||||
|
@using ErsatzTV.Application.MediaItems |
||||||
|
@using System.Text.Json |
||||||
|
@using System.Text.Json.Serialization |
||||||
|
@inject IJSRuntime JsRuntime |
||||||
|
|
||||||
|
<div> |
||||||
|
<MudDialog> |
||||||
|
<DialogContent> |
||||||
|
<div class="overflow-y-scroll" style="max-height: 500px"> |
||||||
|
<pre> |
||||||
|
<code @ref="_infoView">@_info</code> |
||||||
|
</pre> |
||||||
|
</div> |
||||||
|
</DialogContent> |
||||||
|
<DialogActions> |
||||||
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="(() => CopyToClipboard(_infoView))"> |
||||||
|
Copy |
||||||
|
</MudButton> |
||||||
|
<MudButton Color="Color.Primary" OnClick="Close">Close</MudButton> |
||||||
|
</DialogActions> |
||||||
|
</MudDialog> |
||||||
|
</div> |
||||||
|
|
||||||
|
@code { |
||||||
|
|
||||||
|
[CascadingParameter] |
||||||
|
MudDialogInstance MudDialog { get; set; } |
||||||
|
|
||||||
|
[Parameter] |
||||||
|
public MediaItemInfo MediaItemInfo { get; set; } |
||||||
|
|
||||||
|
private string _info; |
||||||
|
private ElementReference _infoView; |
||||||
|
|
||||||
|
protected override Task OnParametersSetAsync() |
||||||
|
{ |
||||||
|
try |
||||||
|
{ |
||||||
|
_info = JsonSerializer.Serialize( |
||||||
|
MediaItemInfo, |
||||||
|
new JsonSerializerOptions |
||||||
|
{ |
||||||
|
Converters = { new JsonStringEnumConverter() }, |
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, |
||||||
|
WriteIndented = true |
||||||
|
}); |
||||||
|
} |
||||||
|
catch (Exception ex) |
||||||
|
{ |
||||||
|
_info = ex.ToString(); |
||||||
|
} |
||||||
|
|
||||||
|
return Task.CompletedTask; |
||||||
|
} |
||||||
|
|
||||||
|
private async Task CopyToClipboard(ElementReference view) |
||||||
|
{ |
||||||
|
await JsRuntime.InvokeVoidAsync("clipboardCopy.copyText", view); |
||||||
|
} |
||||||
|
|
||||||
|
private void Close() => MudDialog.Close(DialogResult.Ok(true)); |
||||||
|
} |
Loading…
Reference in new issue