public async Task <Option <Collection> > GetCollectionWithCollectionItemsUntracked(int id) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); return(await dbContext.Collections .Include(c => c.CollectionItems) .OrderBy(c => c.Id) .SingleOrDefaultAsync(c => c.Id == id) .Map(Optional)); }
public async Task <Either <BaseError, Unit> > Handle(CreateFillerPreset request, CancellationToken cancellationToken) { try { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); var fillerPreset = new FillerPreset { Name = request.Name, FillerKind = request.FillerKind, FillerMode = request.FillerMode, Duration = request.Duration, Count = request.Count, PadToNearestMinute = request.PadToNearestMinute, CollectionType = request.CollectionType, CollectionId = request.CollectionId, MediaItemId = request.MediaItemId, MultiCollectionId = request.MultiCollectionId, SmartCollectionId = request.SmartCollectionId }; await dbContext.FillerPresets.AddAsync(fillerPreset, cancellationToken); await dbContext.SaveChangesAsync(cancellationToken); return(Unit.Default); } catch (Exception ex) { _client.Notify(ex); return(BaseError.New(ex.Message)); } }
public async Task <Either <BaseError, Unit> > Handle(BuildPlayout request, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); Validation <BaseError, Playout> validation = await Validate(dbContext, request); return(await LanguageExtensions.Apply(validation, playout => ApplyUpdateRequest(dbContext, request, playout))); }
public async Task <Either <BaseError, Unit> > Handle( AddSeasonToCollection request, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); Validation <BaseError, Parameters> validation = await Validate(dbContext, request); return(await LanguageExtensions.Apply(validation, parameters => ApplyAddSeasonRequest(dbContext, parameters))); }
public async Task <Either <BaseError, Unit> > Handle( MoveLocalLibraryPath request, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); Validation <BaseError, Parameters> validation = await Validate(dbContext, request); return(await LanguageExtensions.Apply(validation, parameters => MovePath(dbContext, parameters))); }
public async Task <Either <BaseError, IEnumerable <ProgramScheduleItemViewModel> > > Handle( ReplaceProgramScheduleItems request, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); Validation <BaseError, ProgramSchedule> validation = await Validate(dbContext, request); return(await LanguageExtensions.Apply(validation, ps => PersistItems(dbContext, request, ps))); }
public async Task <Either <BaseError, UpdateProgramScheduleResult> > Handle( UpdateProgramSchedule request, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); Validation <BaseError, ProgramSchedule> validation = await Validate(dbContext, request); return(await LanguageExtensions.Apply(validation, ps => ApplyUpdateRequest(dbContext, ps, request))); }
public async Task <Either <BaseError, Unit> > Handle( UpdateCollectionCustomOrder request, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); Validation <BaseError, Collection> validation = await Validate(dbContext, request); return(await LanguageExtensions.Apply(validation, c => ApplyUpdateRequest(dbContext, c, request))); }
public async Task <Either <BaseError, Unit> > Handle( DeleteLocalLibrary request, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); Validation <BaseError, LocalLibrary> validation = await LocalLibraryMustExist(dbContext, request); return(await LanguageExtensions.Apply(validation, localLibrary => DoDeletion(dbContext, localLibrary))); }
public async Task <Either <BaseError, LocalLibraryViewModel> > Handle( CreateLocalLibrary request, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); Validation <BaseError, LocalLibrary> validation = await Validate(dbContext, request); return(await LanguageExtensions.Apply(validation, localLibrary => PersistLocalLibrary(dbContext, localLibrary))); }
public async Task <Either <BaseError, Unit> > Handle( ExtractEmbeddedSubtitles request, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); Validation <BaseError, string> validation = await FFmpegPathMustExist(dbContext); return(await validation.Match( ffmpegPath => ExtractAll(dbContext, request, ffmpegPath, cancellationToken), error => Task.FromResult <Either <BaseError, Unit> >(error.Join()))); }
public async Task <HealthCheckResult> Check(CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); IQueryable <MediaItem> mediaItems = dbContext.MediaItems .Filter(mi => mi.State == MediaItemState.Unavailable) .Include(mi => (mi as Episode).MediaVersions) .ThenInclude(mv => mv.MediaFiles) .Include(mi => (mi as Movie).MediaVersions) .ThenInclude(mv => mv.MediaFiles) .Include(mi => (mi as MusicVideo).MediaVersions) .ThenInclude(mv => mv.MediaFiles) .Include(mi => (mi as OtherVideo).MediaVersions) .ThenInclude(mv => mv.MediaFiles) .Include(mi => (mi as Song).MediaVersions) .ThenInclude(mv => mv.MediaFiles); List <MediaItem> five = await mediaItems // shows and seasons don't have paths to display .Filter(mi => !(mi is Show)) .Filter(mi => !(mi is Season)) .OrderBy(mi => mi.Id) .Take(5) .ToListAsync(cancellationToken); if (mediaItems.Any()) { var paths = new List <string>(); foreach (MediaItem mediaItem in five) { string path = await mediaItem.GetLocalPath( _plexPathReplacementService, _jellyfinPathReplacementService, _embyPathReplacementService, false); paths.Add(path); } var files = string.Join(", ", paths); int count = await mediaItems.CountAsync(cancellationToken); return(WarningResult( $"There are {count} items that are unavailable because ErsatzTV cannot find them on disk, including the following: {files}", "/search?query=state%3aUnavailable")); } return(OkResult()); }
public async Task <Either <BaseError, string> > Handle( GetHlsPlaylistByChannelNumber request, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); DateTimeOffset now = DateTimeOffset.Now; Validation <BaseError, Parameters> validation = await Validate(dbContext, request, now); return(await LanguageExtensions.Apply( validation, parameters => GetPlaylist(dbContext, request, parameters, now))); }
private async Task <Option <HealthCheckResult> > VerifyProfilesUseAcceleration( IEnumerable <HardwareAccelerationKind> accelerationKinds) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); List <Channel> badChannels = await dbContext.Channels .Filter(c => c.StreamingMode != StreamingMode.HttpLiveStreamingDirect) .Filter(c => !accelerationKinds.Contains(c.FFmpegProfile.HardwareAcceleration)) .ToListAsync(); if (badChannels.Any()) { var accel = string.Join(", ", accelerationKinds); var channels = string.Join(", ", badChannels.Map(c => $"{c.Number} - {c.Name}")); return(WarningResult( $"The following channels are transcoding without hardware acceleration ({accel}): {channels}")); } return(None); }
public async Task <Option <MovieViewModel> > Handle( GetMovieById request, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); Option <JellyfinMediaSource> maybeJellyfin = await _mediaSourceRepository.GetAllJellyfin() .Map(list => list.HeadOrNone()); Option <EmbyMediaSource> maybeEmby = await _mediaSourceRepository.GetAllEmby() .Map(list => list.HeadOrNone()); Option <Movie> maybeMovie = await _movieRepository.GetMovie(request.Id); Option <MediaVersion> maybeVersion = maybeMovie.Map(m => m.MediaVersions.HeadOrNone()).Flatten(); var languageCodes = new List <string>(); foreach (MediaVersion version in maybeVersion) { var mediaCodes = version.Streams .Filter(ms => ms.MediaStreamKind == MediaStreamKind.Audio) .Map(ms => ms.Language) .ToList(); languageCodes.AddRange(await dbContext.LanguageCodes.GetAllLanguageCodes(mediaCodes)); } foreach (Movie movie in maybeMovie) { string localPath = await movie.GetLocalPath( _plexPathReplacementService, _jellyfinPathReplacementService, _embyPathReplacementService, false); return(ProjectToViewModel(movie, localPath, languageCodes, maybeJellyfin, maybeEmby)); } return(None); }
public async Task <Either <BaseError, Unit> > Handle( MatchTraktListItems request, CancellationToken cancellationToken) { try { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); Validation <BaseError, TraktList> validation = await TraktListMustExist(dbContext, request.TraktListId); return(await validation.Match( async l => await MatchListItems(dbContext, l).MapT(_ => Unit.Default), error => Task.FromResult <Either <BaseError, Unit> >(error.Join()))); } finally { if (request.Unlock) { _entityLocker.UnlockTrakt(); } } }
public async Task <LibraryPath> Add(LibraryPath libraryPath) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); await dbContext.LibraryPaths.AddAsync(libraryPath); await dbContext.SaveChangesAsync(); return(libraryPath); }
public async Task <Option <int> > Handle(GetChannelFramerate request, CancellationToken cancellationToken) { // TODO: expand to check everything in collection rather than what's scheduled? _logger.LogDebug("Checking frame rates for channel {ChannelNumber}", request.ChannelNumber); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); List <Playout> playouts = await dbContext.Playouts .Include(p => p.Items) .ThenInclude(pi => pi.MediaItem) .ThenInclude(mi => (mi as Movie).MediaVersions) .Include(p => p.Items) .ThenInclude(pi => pi.MediaItem) .ThenInclude(mi => (mi as Episode).MediaVersions) .Include(p => p.Items) .ThenInclude(pi => pi.MediaItem) .ThenInclude(mi => (mi as Song).MediaVersions) .Include(p => p.Items) .ThenInclude(pi => pi.MediaItem) .ThenInclude(mi => (mi as MusicVideo).MediaVersions) .Include(p => p.Items) .ThenInclude(pi => pi.MediaItem) .ThenInclude(mi => (mi as OtherVideo).MediaVersions) .Filter(p => p.Channel.Number == request.ChannelNumber) .ToListAsync(cancellationToken); var frameRates = playouts.Map(p => p.Items.Map(i => i.MediaItem.GetHeadVersion())) .Flatten() .Map(mv => mv.RFrameRate) .ToList(); var distinct = frameRates.Distinct().ToList(); if (distinct.Count > 1) { // TODO: something more intelligent than minimum framerate? int result = frameRates.Map(ParseFrameRate).Min(); if (result < 24) { _logger.LogInformation( "Normalizing frame rate for channel {ChannelNumber} from {Distinct} to {FrameRate} instead of min value {MinFrameRate}", request.ChannelNumber, distinct, 24, result); return(24); } _logger.LogInformation( "Normalizing frame rate for channel {ChannelNumber} from {Distinct} to {FrameRate}", request.ChannelNumber, distinct, result); return(result); } _logger.LogInformation( "All content on channel {ChannelNumber} has the same frame rate of {FrameRate}; will not normalize", request.ChannelNumber, distinct[0]); return(None); }
public async Task <Either <BaseError, CollectionCardResultsViewModel> > Handle( GetCollectionCards request, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); Option <JellyfinMediaSource> maybeJellyfin = await _mediaSourceRepository.GetAllJellyfin() .Map(list => list.HeadOrNone()); Option <EmbyMediaSource> maybeEmby = await _mediaSourceRepository.GetAllEmby() .Map(list => list.HeadOrNone()); return(await dbContext.Collections .AsNoTracking() .Include(c => c.CollectionItems) .Include(c => c.MediaItems) .ThenInclude(i => i.LibraryPath) .Include(c => c.MediaItems) .ThenInclude(i => (i as Movie).MovieMetadata) .ThenInclude(mm => mm.Artwork) .Include(c => c.MediaItems) .ThenInclude(i => (i as Movie).MediaVersions) .ThenInclude(mv => mv.MediaFiles) .Include(c => c.MediaItems) .ThenInclude(i => (i as Artist).ArtistMetadata) .ThenInclude(mvm => mvm.Artwork) .Include(c => c.MediaItems) .ThenInclude(i => (i as MusicVideo).MusicVideoMetadata) .ThenInclude(mvm => mvm.Artwork) .Include(c => c.MediaItems) .ThenInclude(i => (i as MusicVideo).Artist) .ThenInclude(a => a.ArtistMetadata) .Include(c => c.MediaItems) .ThenInclude(i => (i as MusicVideo).MediaVersions) .ThenInclude(mv => mv.MediaFiles) .Include(c => c.MediaItems) .ThenInclude(i => (i as Show).ShowMetadata) .ThenInclude(sm => sm.Artwork) .Include(c => c.MediaItems) .ThenInclude(i => (i as Season).SeasonMetadata) .ThenInclude(sm => sm.Artwork) .Include(c => c.MediaItems) .ThenInclude(i => (i as Season).Show) .ThenInclude(s => s.ShowMetadata) .Include(c => c.MediaItems) .ThenInclude(i => (i as Episode).EpisodeMetadata) .ThenInclude(em => em.Artwork) .Include(c => c.MediaItems) .ThenInclude(i => (i as Episode).EpisodeMetadata) .ThenInclude(em => em.Directors) .Include(c => c.MediaItems) .ThenInclude(i => (i as Episode).EpisodeMetadata) .ThenInclude(em => em.Writers) .Include(c => c.MediaItems) .ThenInclude(i => (i as Episode).Season) .ThenInclude(s => s.Show) .ThenInclude(s => s.ShowMetadata) .Include(c => c.MediaItems) .ThenInclude(i => (i as Episode).Season) .ThenInclude(s => s.SeasonMetadata) .Include(c => c.MediaItems) .ThenInclude(i => (i as Episode).MediaVersions) .ThenInclude(mv => mv.MediaFiles) .Include(c => c.MediaItems) .ThenInclude(i => (i as OtherVideo).OtherVideoMetadata) .ThenInclude(ovm => ovm.Artwork) .Include(c => c.MediaItems) .ThenInclude(i => (i as OtherVideo).MediaVersions) .ThenInclude(mv => mv.MediaFiles) .Include(c => c.MediaItems) .ThenInclude(i => (i as Song).SongMetadata) .ThenInclude(ovm => ovm.Artwork) .Include(c => c.MediaItems) .ThenInclude(i => (i as Song).MediaVersions) .ThenInclude(mv => mv.MediaFiles) .SelectOneAsync(c => c.Id, c => c.Id == request.Id) .Map(c => c.ToEither(BaseError.New("Unable to load collection"))) .MapT(c => ProjectToViewModel(c, maybeJellyfin, maybeEmby))); }