Browse Source

add preferred audio title feature (#943)

* use consistent edit/delete icons

* add preferred audio title feature

* update dependencies
pull/944/head
Jason Dove 3 years ago committed by GitHub
parent
commit
46331ed2c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      CHANGELOG.md
  2. 1
      ErsatzTV.Application/Channels/ChannelViewModel.cs
  3. 1
      ErsatzTV.Application/Channels/Commands/CreateChannel.cs
  4. 1
      ErsatzTV.Application/Channels/Commands/CreateChannelHandler.cs
  5. 1
      ErsatzTV.Application/Channels/Commands/UpdateChannel.cs
  6. 3
      ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs
  7. 1
      ErsatzTV.Application/Channels/Mapper.cs
  8. 1
      ErsatzTV.Application/ProgramSchedules/Commands/AddProgramScheduleItem.cs
  9. 1
      ErsatzTV.Application/ProgramSchedules/Commands/IProgramScheduleItemRequest.cs
  10. 4
      ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs
  11. 1
      ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItems.cs
  12. 4
      ErsatzTV.Application/ProgramSchedules/Mapper.cs
  13. 2
      ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemDurationViewModel.cs
  14. 2
      ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemFloodViewModel.cs
  15. 2
      ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemMultipleViewModel.cs
  16. 2
      ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemOneViewModel.cs
  17. 1
      ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemViewModel.cs
  18. 1
      ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs
  19. 2
      ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj
  20. 4
      ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs
  21. 1
      ErsatzTV.Core/Domain/Channel.cs
  22. 1
      ErsatzTV.Core/Domain/PlayoutItem.cs
  23. 1
      ErsatzTV.Core/Domain/ProgramScheduleItem.cs
  24. 2
      ErsatzTV.Core/ErsatzTV.Core.csproj
  25. 4
      ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
  26. 45
      ErsatzTV.Core/FFmpeg/FFmpegStreamSelector.cs
  27. 1
      ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs
  28. 3
      ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegStreamSelector.cs
  29. 1
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerDuration.cs
  30. 1
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerFlood.cs
  31. 1
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerMultiple.cs
  32. 1
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerOne.cs
  33. 2
      ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj
  34. 2
      ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj
  35. 4303
      ErsatzTV.Infrastructure/Migrations/20220830202251_Add_PreferredAudioTitle.Designer.cs
  36. 45
      ErsatzTV.Infrastructure/Migrations/20220830202251_Add_PreferredAudioTitle.cs
  37. 11
      ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs
  38. 2
      ErsatzTV/ErsatzTV.csproj
  39. 2
      ErsatzTV/Pages/Artist.razor
  40. 2
      ErsatzTV/Pages/ChannelEditor.razor
  41. 3
      ErsatzTV/Pages/ScheduleItemsEditor.razor
  42. 2
      ErsatzTV/Pages/TelevisionEpisodeList.razor
  43. 2
      ErsatzTV/Pages/TelevisionSeasonList.razor
  44. 3
      ErsatzTV/ViewModels/ChannelEditViewModel.cs
  45. 1
      ErsatzTV/ViewModels/ProgramScheduleItemEditViewModel.cs
  46. 11
      ErsatzTV/client-app/src/views/ChannelsPage.vue

7
CHANGELOG.md

@ -10,6 +10,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -10,6 +10,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix scanner crash caused by invalid mtime
- `VAAPI`: Downgrade libva from 2.15 to 2.14
### Added
- Add `Preferred Audio Title` feature
- Preference can be configured in channel settings and overridden on schedule items
- When a title is specified, audio streams that contain that title (case-insensitive search) will be prioritized
- This can be helpful for creating channels that use commentary tracks
- External tooling exists to easily update title/name metadata if your audio streams don't already have this metadata
## [0.6.6-beta] - 2022-08-17
### Fixed
- Use MIME Type `application/x-mpegurl` for all playlists instead of `application/vnd.apple.mpegurl`

1
ErsatzTV.Application/Channels/ChannelViewModel.cs

@ -11,6 +11,7 @@ public record ChannelViewModel( @@ -11,6 +11,7 @@ public record ChannelViewModel(
int FFmpegProfileId,
string Logo,
string PreferredAudioLanguageCode,
string PreferredAudioTitle,
StreamingMode StreamingMode,
int? WatermarkId,
int? FallbackFillerId,

1
ErsatzTV.Application/Channels/Commands/CreateChannel.cs

@ -12,6 +12,7 @@ public record CreateChannel @@ -12,6 +12,7 @@ public record CreateChannel
int FFmpegProfileId,
string Logo,
string PreferredAudioLanguageCode,
string PreferredAudioTitle,
StreamingMode StreamingMode,
int? WatermarkId,
int? FallbackFillerId,

1
ErsatzTV.Application/Channels/Commands/CreateChannelHandler.cs

@ -71,6 +71,7 @@ public class CreateChannelHandler : IRequestHandler<CreateChannel, Either<BaseEr @@ -71,6 +71,7 @@ public class CreateChannelHandler : IRequestHandler<CreateChannel, Either<BaseEr
StreamingMode = request.StreamingMode,
Artwork = artwork,
PreferredAudioLanguageCode = preferredAudioLanguageCode,
PreferredAudioTitle = request.PreferredAudioTitle,
PreferredSubtitleLanguageCode = preferredSubtitleLanguageCode,
SubtitleMode = request.SubtitleMode,
MusicVideoCreditsMode = request.MusicVideoCreditsMode

1
ErsatzTV.Application/Channels/Commands/UpdateChannel.cs

@ -13,6 +13,7 @@ public record UpdateChannel @@ -13,6 +13,7 @@ public record UpdateChannel
int FFmpegProfileId,
string Logo,
string PreferredAudioLanguageCode,
string PreferredAudioTitle,
StreamingMode StreamingMode,
int? WatermarkId,
int? FallbackFillerId,

3
ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs

@ -31,7 +31,7 @@ public class UpdateChannelHandler : IRequestHandler<UpdateChannel, Either<BaseEr @@ -31,7 +31,7 @@ public class UpdateChannelHandler : IRequestHandler<UpdateChannel, Either<BaseEr
{
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
Validation<BaseError, Channel> validation = await Validate(dbContext, request);
return await LanguageExtensions.Apply(validation, c => ApplyUpdateRequest(dbContext, c, request));
return await validation.Apply(c => ApplyUpdateRequest(dbContext, c, request));
}
private async Task<ChannelViewModel> ApplyUpdateRequest(TvContext dbContext, Channel c, UpdateChannel update)
@ -42,6 +42,7 @@ public class UpdateChannelHandler : IRequestHandler<UpdateChannel, Either<BaseEr @@ -42,6 +42,7 @@ public class UpdateChannelHandler : IRequestHandler<UpdateChannel, Either<BaseEr
c.Categories = update.Categories;
c.FFmpegProfileId = update.FFmpegProfileId;
c.PreferredAudioLanguageCode = update.PreferredAudioLanguageCode;
c.PreferredAudioTitle = update.PreferredAudioTitle;
c.PreferredSubtitleLanguageCode = update.PreferredSubtitleLanguageCode;
c.SubtitleMode = update.SubtitleMode;
c.MusicVideoCreditsMode = update.MusicVideoCreditsMode;

1
ErsatzTV.Application/Channels/Mapper.cs

@ -15,6 +15,7 @@ internal static class Mapper @@ -15,6 +15,7 @@ internal static class Mapper
channel.FFmpegProfileId,
GetLogo(channel),
channel.PreferredAudioLanguageCode,
channel.PreferredAudioTitle,
channel.StreamingMode,
channel.WatermarkId,
channel.FallbackFillerId,

1
ErsatzTV.Application/ProgramSchedules/Commands/AddProgramScheduleItem.cs

@ -26,6 +26,7 @@ public record AddProgramScheduleItem( @@ -26,6 +26,7 @@ public record AddProgramScheduleItem(
int? FallbackFillerId,
int? WatermarkId,
string PreferredAudioLanguageCode,
string PreferredAudioTitle,
string PreferredSubtitleLanguageCode,
ChannelSubtitleMode? SubtitleMode) : IRequest<Either<BaseError, ProgramScheduleItemViewModel>>,
IProgramScheduleItemRequest;

1
ErsatzTV.Application/ProgramSchedules/Commands/IProgramScheduleItemRequest.cs

@ -24,6 +24,7 @@ public interface IProgramScheduleItemRequest @@ -24,6 +24,7 @@ public interface IProgramScheduleItemRequest
int? FallbackFillerId { get; }
int? WatermarkId { get; }
string PreferredAudioLanguageCode { get; }
string PreferredAudioTitle { get; }
string PreferredSubtitleLanguageCode { get; }
ChannelSubtitleMode? SubtitleMode { get; }
}

4
ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs

@ -180,6 +180,7 @@ public abstract class ProgramScheduleItemCommandBase @@ -180,6 +180,7 @@ public abstract class ProgramScheduleItemCommandBase
FallbackFillerId = item.FallbackFillerId,
WatermarkId = item.WatermarkId,
PreferredAudioLanguageCode = item.PreferredAudioLanguageCode,
PreferredAudioTitle = item.PreferredAudioTitle,
PreferredSubtitleLanguageCode = item.PreferredSubtitleLanguageCode,
SubtitleMode = item.SubtitleMode
},
@ -203,6 +204,7 @@ public abstract class ProgramScheduleItemCommandBase @@ -203,6 +204,7 @@ public abstract class ProgramScheduleItemCommandBase
FallbackFillerId = item.FallbackFillerId,
WatermarkId = item.WatermarkId,
PreferredAudioLanguageCode = item.PreferredAudioLanguageCode,
PreferredAudioTitle = item.PreferredAudioTitle,
PreferredSubtitleLanguageCode = item.PreferredSubtitleLanguageCode,
SubtitleMode = item.SubtitleMode
},
@ -227,6 +229,7 @@ public abstract class ProgramScheduleItemCommandBase @@ -227,6 +229,7 @@ public abstract class ProgramScheduleItemCommandBase
FallbackFillerId = item.FallbackFillerId,
WatermarkId = item.WatermarkId,
PreferredAudioLanguageCode = item.PreferredAudioLanguageCode,
PreferredAudioTitle = item.PreferredAudioTitle,
PreferredSubtitleLanguageCode = item.PreferredSubtitleLanguageCode,
SubtitleMode = item.SubtitleMode
},
@ -252,6 +255,7 @@ public abstract class ProgramScheduleItemCommandBase @@ -252,6 +255,7 @@ public abstract class ProgramScheduleItemCommandBase
FallbackFillerId = item.FallbackFillerId,
WatermarkId = item.WatermarkId,
PreferredAudioLanguageCode = item.PreferredAudioLanguageCode,
PreferredAudioTitle = item.PreferredAudioTitle,
PreferredSubtitleLanguageCode = item.PreferredSubtitleLanguageCode,
SubtitleMode = item.SubtitleMode
},

1
ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItems.cs

@ -26,6 +26,7 @@ public record ReplaceProgramScheduleItem( @@ -26,6 +26,7 @@ public record ReplaceProgramScheduleItem(
int? FallbackFillerId,
int? WatermarkId,
string PreferredAudioLanguageCode,
string PreferredAudioTitle,
string PreferredSubtitleLanguageCode,
ChannelSubtitleMode? SubtitleMode) : IProgramScheduleItemRequest;

4
ErsatzTV.Application/ProgramSchedules/Mapper.cs

@ -63,6 +63,7 @@ internal static class Mapper @@ -63,6 +63,7 @@ internal static class Mapper
? Watermarks.Mapper.ProjectToViewModel(duration.Watermark)
: null,
duration.PreferredAudioLanguageCode,
duration.PreferredAudioTitle,
duration.PreferredSubtitleLanguageCode,
duration.SubtitleMode),
ProgramScheduleItemFlood flood =>
@ -110,6 +111,7 @@ internal static class Mapper @@ -110,6 +111,7 @@ internal static class Mapper
? Watermarks.Mapper.ProjectToViewModel(flood.Watermark)
: null,
flood.PreferredAudioLanguageCode,
flood.PreferredAudioTitle,
flood.PreferredSubtitleLanguageCode,
flood.SubtitleMode),
ProgramScheduleItemMultiple multiple =>
@ -158,6 +160,7 @@ internal static class Mapper @@ -158,6 +160,7 @@ internal static class Mapper
? Watermarks.Mapper.ProjectToViewModel(multiple.Watermark)
: null,
multiple.PreferredAudioLanguageCode,
multiple.PreferredAudioTitle,
multiple.PreferredSubtitleLanguageCode,
multiple.SubtitleMode),
ProgramScheduleItemOne one =>
@ -205,6 +208,7 @@ internal static class Mapper @@ -205,6 +208,7 @@ internal static class Mapper
? Watermarks.Mapper.ProjectToViewModel(one.Watermark)
: null,
one.PreferredAudioLanguageCode,
one.PreferredAudioTitle,
one.PreferredSubtitleLanguageCode,
one.SubtitleMode),
_ => throw new NotSupportedException(

2
ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemDurationViewModel.cs

@ -30,6 +30,7 @@ public record ProgramScheduleItemDurationViewModel : ProgramScheduleItemViewMode @@ -30,6 +30,7 @@ public record ProgramScheduleItemDurationViewModel : ProgramScheduleItemViewMode
FillerPresetViewModel fallbackFiller,
WatermarkViewModel watermark,
string preferredAudioLanguageCode,
string preferredAudioTitle,
string preferredSubtitleLanguageCode,
ChannelSubtitleMode? subtitleMode) : base(
id,
@ -52,6 +53,7 @@ public record ProgramScheduleItemDurationViewModel : ProgramScheduleItemViewMode @@ -52,6 +53,7 @@ public record ProgramScheduleItemDurationViewModel : ProgramScheduleItemViewMode
fallbackFiller,
watermark,
preferredAudioLanguageCode,
preferredAudioTitle,
preferredSubtitleLanguageCode,
subtitleMode)
{

2
ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemFloodViewModel.cs

@ -28,6 +28,7 @@ public record ProgramScheduleItemFloodViewModel : ProgramScheduleItemViewModel @@ -28,6 +28,7 @@ public record ProgramScheduleItemFloodViewModel : ProgramScheduleItemViewModel
FillerPresetViewModel fallbackFiller,
WatermarkViewModel watermark,
string preferredAudioLanguageCode,
string preferredAudioTitle,
string preferredSubtitleLanguageCode,
ChannelSubtitleMode? subtitleMode) : base(
id,
@ -50,6 +51,7 @@ public record ProgramScheduleItemFloodViewModel : ProgramScheduleItemViewModel @@ -50,6 +51,7 @@ public record ProgramScheduleItemFloodViewModel : ProgramScheduleItemViewModel
fallbackFiller,
watermark,
preferredAudioLanguageCode,
preferredAudioTitle,
preferredSubtitleLanguageCode,
subtitleMode)
{

2
ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemMultipleViewModel.cs

@ -29,6 +29,7 @@ public record ProgramScheduleItemMultipleViewModel : ProgramScheduleItemViewMode @@ -29,6 +29,7 @@ public record ProgramScheduleItemMultipleViewModel : ProgramScheduleItemViewMode
FillerPresetViewModel fallbackFiller,
WatermarkViewModel watermark,
string preferredAudioLanguageCode,
string preferredAudioTitle,
string preferredSubtitleLanguageCode,
ChannelSubtitleMode? subtitleMode) : base(
id,
@ -51,6 +52,7 @@ public record ProgramScheduleItemMultipleViewModel : ProgramScheduleItemViewMode @@ -51,6 +52,7 @@ public record ProgramScheduleItemMultipleViewModel : ProgramScheduleItemViewMode
fallbackFiller,
watermark,
preferredAudioLanguageCode,
preferredAudioTitle,
preferredSubtitleLanguageCode,
subtitleMode) =>
Count = count;

2
ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemOneViewModel.cs

@ -28,6 +28,7 @@ public record ProgramScheduleItemOneViewModel : ProgramScheduleItemViewModel @@ -28,6 +28,7 @@ public record ProgramScheduleItemOneViewModel : ProgramScheduleItemViewModel
FillerPresetViewModel fallbackFiller,
WatermarkViewModel watermark,
string preferredAudioLanguageCode,
string preferredAudioTitle,
string preferredSubtitleLanguageCode,
ChannelSubtitleMode? subtitleMode) : base(
id,
@ -50,6 +51,7 @@ public record ProgramScheduleItemOneViewModel : ProgramScheduleItemViewModel @@ -50,6 +51,7 @@ public record ProgramScheduleItemOneViewModel : ProgramScheduleItemViewModel
fallbackFiller,
watermark,
preferredAudioLanguageCode,
preferredAudioTitle,
preferredSubtitleLanguageCode,
subtitleMode)
{

1
ErsatzTV.Application/ProgramSchedules/ProgramScheduleItemViewModel.cs

@ -27,6 +27,7 @@ public abstract record ProgramScheduleItemViewModel( @@ -27,6 +27,7 @@ public abstract record ProgramScheduleItemViewModel(
FillerPresetViewModel FallbackFiller,
WatermarkViewModel Watermark,
string PreferredAudioLanguageCode,
string PreferredAudioTitle,
string PreferredSubtitleLanguageCode,
ChannelSubtitleMode? SubtitleMode)
{

1
ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs

@ -174,6 +174,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< @@ -174,6 +174,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler<
audioPath,
subtitles,
playoutItemWithPath.PlayoutItem.PreferredAudioLanguageCode ?? channel.PreferredAudioLanguageCode,
playoutItemWithPath.PlayoutItem.PreferredAudioTitle ?? channel.PreferredAudioTitle,
playoutItemWithPath.PlayoutItem.PreferredSubtitleLanguageCode ?? channel.PreferredSubtitleLanguageCode,
playoutItemWithPath.PlayoutItem.SubtitleMode ?? channel.SubtitleMode,
playoutItemWithPath.PlayoutItem.StartOffset,

2
ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj

@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.3.44">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

4
ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs

@ -483,6 +483,7 @@ public class TranscodingTests @@ -483,6 +483,7 @@ public class TranscodingTests
subtitles,
string.Empty,
string.Empty,
string.Empty,
subtitleMode,
now,
now + TimeSpan.FromSeconds(5),
@ -569,7 +570,8 @@ public class TranscodingTests @@ -569,7 +570,8 @@ public class TranscodingTests
MediaVersion version,
StreamingMode streamingMode,
string channelNumber,
string preferredAudioLanguage) =>
string preferredAudioLanguage,
string preferredAudioTitle) =>
Optional(version.Streams.First(s => s.MediaStreamKind == MediaStreamKind.Audio)).AsTask();
public Task<Option<Domain.Subtitle>> SelectSubtitleStream(

1
ErsatzTV.Core/Domain/Channel.cs

@ -23,6 +23,7 @@ public class Channel @@ -23,6 +23,7 @@ public class Channel
public List<Playout> Playouts { get; set; }
public List<Artwork> Artwork { get; set; }
public string PreferredAudioLanguageCode { get; set; }
public string PreferredAudioTitle { get; set; }
public string PreferredSubtitleLanguageCode { get; set; }
public ChannelSubtitleMode SubtitleMode { get; set; }
public ChannelMusicVideoCreditsMode MusicVideoCreditsMode { get; set; }

1
ErsatzTV.Core/Domain/PlayoutItem.cs

@ -24,6 +24,7 @@ public class PlayoutItem @@ -24,6 +24,7 @@ public class PlayoutItem
public int? WatermarkId { get; set; }
public bool DisableWatermarks { get; set; }
public string PreferredAudioLanguageCode { get; set; }
public string PreferredAudioTitle { get; set; }
public string PreferredSubtitleLanguageCode { get; set; }
public ChannelSubtitleMode? SubtitleMode { get; set; }
public DateTimeOffset StartOffset => new DateTimeOffset(Start, TimeSpan.Zero).ToLocalTime();

1
ErsatzTV.Core/Domain/ProgramScheduleItem.cs

@ -35,6 +35,7 @@ public abstract class ProgramScheduleItem @@ -35,6 +35,7 @@ public abstract class ProgramScheduleItem
public ChannelWatermark Watermark { get; set; }
public int? WatermarkId { get; set; }
public string PreferredAudioLanguageCode { get; set; }
public string PreferredAudioTitle { get; set; }
public string PreferredSubtitleLanguageCode { get; set; }
public ChannelSubtitleMode? SubtitleMode { get; set; }
}

2
ErsatzTV.Core/ErsatzTV.Core.csproj

@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.2.0" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.2.1" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.3.44">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

4
ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs

@ -49,6 +49,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -49,6 +49,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
string audioPath,
List<Subtitle> subtitles,
string preferredAudioLanguage,
string preferredAudioTitle,
string preferredSubtitleLanguage,
ChannelSubtitleMode subtitleMode,
DateTimeOffset start,
@ -72,7 +73,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -72,7 +73,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
audioVersion,
channel.StreamingMode,
channel.Number,
preferredAudioLanguage);
preferredAudioLanguage,
preferredAudioTitle);
Option<Subtitle> maybeSubtitle =
await _ffmpegStreamSelector.SelectSubtitleStream(
subtitles,

45
ErsatzTV.Core/FFmpeg/FFmpegStreamSelector.cs

@ -28,13 +28,14 @@ public class FFmpegStreamSelector : IFFmpegStreamSelector @@ -28,13 +28,14 @@ public class FFmpegStreamSelector : IFFmpegStreamSelector
MediaVersion version,
StreamingMode streamingMode,
string channelNumber,
string preferredAudioLanguage)
string preferredAudioLanguage,
string preferredAudioTitle)
{
if (streamingMode == StreamingMode.HttpLiveStreamingDirect &&
string.IsNullOrWhiteSpace(preferredAudioLanguage))
string.IsNullOrWhiteSpace(preferredAudioLanguage) && string.IsNullOrWhiteSpace(preferredAudioTitle))
{
_logger.LogDebug(
"Channel {Number} is HLS Direct with no preferred audio language; using all audio streams",
"Channel {Number} is HLS Direct with no preferred audio language or title; using all audio streams",
channelNumber);
return None;
}
@ -71,18 +72,18 @@ public class FFmpegStreamSelector : IFFmpegStreamSelector @@ -71,18 +72,18 @@ public class FFmpegStreamSelector : IFFmpegStreamSelector
if (correctLanguage.Any())
{
_logger.LogDebug(
"Found {Count} audio streams with preferred audio language code(s) {Code}; selecting stream with most channels",
"Found {Count} audio streams with preferred audio language code(s) {Code}",
correctLanguage.Count,
allCodes);
return correctLanguage.OrderByDescending(s => s.Channels).Head();
return PrioritizeAudioTitle(correctLanguage, preferredAudioTitle ?? string.Empty);
}
_logger.LogDebug(
"Unable to find audio stream with preferred audio language code(s) {Code}; selecting stream with most channels",
"Unable to find audio stream with preferred audio language code(s) {Code}",
allCodes);
return audioStreams.OrderByDescending(s => s.Channels).HeadOrNone();
return PrioritizeAudioTitle(audioStreams, preferredAudioTitle ?? string.Empty);
}
public async Task<Option<Subtitle>> SelectSubtitleStream(
@ -163,4 +164,34 @@ public class FFmpegStreamSelector : IFFmpegStreamSelector @@ -163,4 +164,34 @@ public class FFmpegStreamSelector : IFFmpegStreamSelector
return None;
}
private Option<MediaStream> PrioritizeAudioTitle(IReadOnlyCollection<MediaStream> streams, string title)
{
// return correctLanguage.OrderByDescending(s => s.Channels).Head();
if (string.IsNullOrWhiteSpace(title))
{
_logger.LogDebug("No audio title has been specified; selecting stream with most channels");
return streams.OrderByDescending(s => s.Channels).Head();
}
// prioritize matching titles
var matchingTitle = streams
.Filter(ms => (ms.Title ?? string.Empty).Contains(title, StringComparison.OrdinalIgnoreCase))
.ToList();
if (matchingTitle.Any())
{
_logger.LogDebug(
"Found {Count} audio streams with preferred title {Title}; selecting stream with most channels",
matchingTitle.Count,
title);
return matchingTitle.OrderByDescending(s => s.Channels).Head();
}
_logger.LogDebug(
"Unable to find audio stream with preferred title {Title}; selecting stream with most channels",
title);
return streams.OrderByDescending(s => s.Channels).Head();
}
}

1
ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs

@ -19,6 +19,7 @@ public interface IFFmpegProcessService @@ -19,6 +19,7 @@ public interface IFFmpegProcessService
string audioPath,
List<Subtitle> subtitles,
string preferredAudioLanguage,
string preferredAudioTitle,
string preferredSubtitleLanguage,
ChannelSubtitleMode subtitleMode,
DateTimeOffset start,

3
ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegStreamSelector.cs

@ -10,7 +10,8 @@ public interface IFFmpegStreamSelector @@ -10,7 +10,8 @@ public interface IFFmpegStreamSelector
MediaVersion version,
StreamingMode streamingMode,
string channelNumber,
string preferredAudioLanguage);
string preferredAudioLanguage,
string preferredAudioTitle);
Task<Option<Subtitle>> SelectSubtitleStream(
List<Subtitle> subtitles,

1
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerDuration.cs

@ -81,6 +81,7 @@ public class PlayoutModeSchedulerDuration : PlayoutModeSchedulerBase<ProgramSche @@ -81,6 +81,7 @@ public class PlayoutModeSchedulerDuration : PlayoutModeSchedulerBase<ProgramSche
CustomTitle = scheduleItem.CustomTitle,
WatermarkId = scheduleItem.WatermarkId,
PreferredAudioLanguageCode = scheduleItem.PreferredAudioLanguageCode,
PreferredAudioTitle = scheduleItem.PreferredAudioTitle,
PreferredSubtitleLanguageCode = scheduleItem.PreferredSubtitleLanguageCode,
SubtitleMode = scheduleItem.SubtitleMode
};

1
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerFlood.cs

@ -62,6 +62,7 @@ public class PlayoutModeSchedulerFlood : PlayoutModeSchedulerBase<ProgramSchedul @@ -62,6 +62,7 @@ public class PlayoutModeSchedulerFlood : PlayoutModeSchedulerBase<ProgramSchedul
CustomTitle = scheduleItem.CustomTitle,
WatermarkId = scheduleItem.WatermarkId,
PreferredAudioLanguageCode = scheduleItem.PreferredAudioLanguageCode,
PreferredAudioTitle = scheduleItem.PreferredAudioTitle,
PreferredSubtitleLanguageCode = scheduleItem.PreferredSubtitleLanguageCode,
SubtitleMode = scheduleItem.SubtitleMode
};

1
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerMultiple.cs

@ -70,6 +70,7 @@ public class PlayoutModeSchedulerMultiple : PlayoutModeSchedulerBase<ProgramSche @@ -70,6 +70,7 @@ public class PlayoutModeSchedulerMultiple : PlayoutModeSchedulerBase<ProgramSche
CustomTitle = scheduleItem.CustomTitle,
WatermarkId = scheduleItem.WatermarkId,
PreferredAudioLanguageCode = scheduleItem.PreferredAudioLanguageCode,
PreferredAudioTitle = scheduleItem.PreferredAudioTitle,
PreferredSubtitleLanguageCode = scheduleItem.PreferredSubtitleLanguageCode,
SubtitleMode = scheduleItem.SubtitleMode
};

1
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerOne.cs

@ -50,6 +50,7 @@ public class PlayoutModeSchedulerOne : PlayoutModeSchedulerBase<ProgramScheduleI @@ -50,6 +50,7 @@ public class PlayoutModeSchedulerOne : PlayoutModeSchedulerBase<ProgramScheduleI
CustomTitle = scheduleItem.CustomTitle,
WatermarkId = scheduleItem.WatermarkId,
PreferredAudioLanguageCode = scheduleItem.PreferredAudioLanguageCode,
PreferredAudioTitle = scheduleItem.PreferredAudioTitle,
PreferredSubtitleLanguageCode = scheduleItem.PreferredSubtitleLanguageCode,
SubtitleMode = scheduleItem.SubtitleMode
};

2
ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj

@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.7.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
<PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />

2
ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj

@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.7.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
<PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />

4303
ErsatzTV.Infrastructure/Migrations/20220830202251_Add_PreferredAudioTitle.Designer.cs generated

File diff suppressed because it is too large Load Diff

45
ErsatzTV.Infrastructure/Migrations/20220830202251_Add_PreferredAudioTitle.cs

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Migrations
{
public partial class Add_PreferredAudioTitle : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "PreferredAudioTitle",
table: "ProgramScheduleItem",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "PreferredAudioTitle",
table: "PlayoutItem",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "PreferredAudioTitle",
table: "Channel",
type: "TEXT",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "PreferredAudioTitle",
table: "ProgramScheduleItem");
migrationBuilder.DropColumn(
name: "PreferredAudioTitle",
table: "PlayoutItem");
migrationBuilder.DropColumn(
name: "PreferredAudioTitle",
table: "Channel");
}
}
}

11
ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs

@ -15,7 +15,7 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -15,7 +15,7 @@ namespace ErsatzTV.Infrastructure.Migrations
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "6.0.7");
modelBuilder.HasAnnotation("ProductVersion", "6.0.8");
modelBuilder.Entity("ErsatzTV.Core.Domain.Actor", b =>
{
@ -245,6 +245,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -245,6 +245,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Property<string>("PreferredAudioLanguageCode")
.HasColumnType("TEXT");
b.Property<string>("PreferredAudioTitle")
.HasColumnType("TEXT");
b.Property<string>("PreferredSubtitleLanguageCode")
.HasColumnType("TEXT");
@ -1425,6 +1428,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1425,6 +1428,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Property<string>("PreferredAudioLanguageCode")
.HasColumnType("TEXT");
b.Property<string>("PreferredAudioTitle")
.HasColumnType("TEXT");
b.Property<string>("PreferredSubtitleLanguageCode")
.HasColumnType("TEXT");
@ -1613,6 +1619,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -1613,6 +1619,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Property<string>("PreferredAudioLanguageCode")
.HasColumnType("TEXT");
b.Property<string>("PreferredAudioTitle")
.HasColumnType("TEXT");
b.Property<string>("PreferredSubtitleLanguageCode")
.HasColumnType("TEXT");

2
ErsatzTV/ErsatzTV.csproj

@ -55,7 +55,7 @@ @@ -55,7 +55,7 @@
<ItemGroup>
<PackageReference Include="Bugsnag.AspNet.Core" Version="3.1.0" />
<PackageReference Include="FluentValidation" Version="11.2.0" />
<PackageReference Include="FluentValidation" Version="11.2.1" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.2.1" />
<PackageReference Include="HtmlSanitizer" Version="7.1.542" />
<PackageReference Include="LanguageExt.Core" Version="4.2.9" />

2
ErsatzTV/Pages/Artist.razor

@ -258,7 +258,7 @@ @@ -258,7 +258,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled && result.Data is ProgramScheduleViewModel schedule)
{
await _mediator.Send(new AddProgramScheduleItem(schedule.Id, StartType.Dynamic, null, PlayoutMode.One, ProgramScheduleItemCollectionType.Artist, null, null, null, ArtistId, PlaybackOrder.Shuffle, null, null, TailMode.None, null, GuideMode.Normal, null, null, null, null, null, null, null, null, null), _cts.Token);
await _mediator.Send(new AddProgramScheduleItem(schedule.Id, StartType.Dynamic, null, PlayoutMode.One, ProgramScheduleItemCollectionType.Artist, null, null, null, ArtistId, PlaybackOrder.Shuffle, null, null, TailMode.None, null, GuideMode.Normal, null, null, null, null, null, null, null, null, null, null), _cts.Token);
_navigationManager.NavigateTo($"/schedules/{schedule.Id}/items");
}
}

2
ErsatzTV/Pages/ChannelEditor.razor

@ -54,6 +54,7 @@ @@ -54,6 +54,7 @@
<MudSelectItem Value="@culture.ThreeLetterISOLanguageName">@culture.EnglishName</MudSelectItem>
}
</MudSelect>
<MudTextField Label="Preferred Audio Title" @bind-Value="_model.PreferredAudioTitle" For="@(() => _model.PreferredAudioTitle)"/>
<MudSelect Class="mt-3"
Label="Preferred Subtitle Language"
@bind-Value="_model.PreferredSubtitleLanguageCode"
@ -167,6 +168,7 @@ @@ -167,6 +168,7 @@
_model.Logo = channelViewModel.Logo;
_model.StreamingMode = channelViewModel.StreamingMode;
_model.PreferredAudioLanguageCode = channelViewModel.PreferredAudioLanguageCode;
_model.PreferredAudioTitle = channelViewModel.PreferredAudioTitle;
_model.WatermarkId = channelViewModel.WatermarkId;
_model.FallbackFillerId = channelViewModel.FallbackFillerId;
_model.PreferredSubtitleLanguageCode = channelViewModel.PreferredSubtitleLanguageCode;

3
ErsatzTV/Pages/ScheduleItemsEditor.razor

@ -299,6 +299,7 @@ @@ -299,6 +299,7 @@
<MudSelectItem Value="@culture.ThreeLetterISOLanguageName">@culture.EnglishName</MudSelectItem>
}
</MudSelect>
<MudTextField Class="mt-3" Label="Preferred Audio Title" @bind-Value="@_selectedItem.PreferredAudioTitle" For="@(() => _selectedItem.PreferredAudioTitle)"/>
<MudSelect Class="mt-3"
Label="Preferred Subtitle Language"
@bind-Value="_selectedItem.PreferredSubtitleLanguageCode"
@ -422,6 +423,7 @@ @@ -422,6 +423,7 @@
FallbackFiller = item.FallbackFiller,
Watermark = item.Watermark,
PreferredAudioLanguageCode = item.PreferredAudioLanguageCode,
PreferredAudioTitle = item.PreferredAudioTitle,
PreferredSubtitleLanguageCode = item.PreferredSubtitleLanguageCode,
SubtitleMode = item.SubtitleMode
};
@ -500,6 +502,7 @@ @@ -500,6 +502,7 @@
item.FallbackFiller?.Id,
item.Watermark?.Id,
item.PreferredAudioLanguageCode,
item.PreferredAudioTitle,
item.PreferredSubtitleLanguageCode,
item.SubtitleMode)).ToList();

2
ErsatzTV/Pages/TelevisionEpisodeList.razor

@ -233,7 +233,7 @@ @@ -233,7 +233,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled && result.Data is ProgramScheduleViewModel schedule)
{
await _mediator.Send(new AddProgramScheduleItem(schedule.Id, StartType.Dynamic, null, PlayoutMode.One, ProgramScheduleItemCollectionType.TelevisionSeason, null, null, null, SeasonId, PlaybackOrder.Shuffle, null, null, TailMode.None, null, GuideMode.Normal, null, null, null, null, null, null, null, null, null), _cts.Token);
await _mediator.Send(new AddProgramScheduleItem(schedule.Id, StartType.Dynamic, null, PlayoutMode.One, ProgramScheduleItemCollectionType.TelevisionSeason, null, null, null, SeasonId, PlaybackOrder.Shuffle, null, null, TailMode.None, null, GuideMode.Normal, null, null, null, null, null, null, null, null, null, null), _cts.Token);
_navigationManager.NavigateTo($"/schedules/{schedule.Id}/items");
}
}

2
ErsatzTV/Pages/TelevisionSeasonList.razor

@ -228,7 +228,7 @@ @@ -228,7 +228,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled && result.Data is ProgramScheduleViewModel schedule)
{
await _mediator.Send(new AddProgramScheduleItem(schedule.Id, StartType.Dynamic, null, PlayoutMode.One, ProgramScheduleItemCollectionType.TelevisionShow, null, null, null, ShowId, PlaybackOrder.Shuffle, null, null, TailMode.None, null, GuideMode.Normal, null, null, null, null, null, null, null, null, null), _cts.Token);
await _mediator.Send(new AddProgramScheduleItem(schedule.Id, StartType.Dynamic, null, PlayoutMode.One, ProgramScheduleItemCollectionType.TelevisionShow, null, null, null, ShowId, PlaybackOrder.Shuffle, null, null, TailMode.None, null, GuideMode.Normal, null, null, null, null, null, null, null, null, null, null), _cts.Token);
_navigationManager.NavigateTo($"/schedules/{schedule.Id}/items");
}
}

3
ErsatzTV/ViewModels/ChannelEditViewModel.cs

@ -12,6 +12,7 @@ public class ChannelEditViewModel @@ -12,6 +12,7 @@ public class ChannelEditViewModel
public string Number { get; set; }
public int FFmpegProfileId { get; set; }
public string PreferredAudioLanguageCode { get; set; }
public string PreferredAudioTitle { get; set; }
public string Logo { get; set; }
public StreamingMode StreamingMode { get; set; }
public int? WatermarkId { get; set; }
@ -30,6 +31,7 @@ public class ChannelEditViewModel @@ -30,6 +31,7 @@ public class ChannelEditViewModel
FFmpegProfileId,
Logo,
PreferredAudioLanguageCode,
PreferredAudioTitle,
StreamingMode,
WatermarkId,
FallbackFillerId,
@ -46,6 +48,7 @@ public class ChannelEditViewModel @@ -46,6 +48,7 @@ public class ChannelEditViewModel
FFmpegProfileId,
Logo,
PreferredAudioLanguageCode,
PreferredAudioTitle,
StreamingMode,
WatermarkId,
FallbackFillerId,

1
ErsatzTV/ViewModels/ProgramScheduleItemEditViewModel.cs

@ -66,6 +66,7 @@ public class ProgramScheduleItemEditViewModel : INotifyPropertyChanged @@ -66,6 +66,7 @@ public class ProgramScheduleItemEditViewModel : INotifyPropertyChanged
public FillerPresetViewModel FallbackFiller { get; set; }
public WatermarkViewModel Watermark { get; set; }
public string PreferredAudioLanguageCode { get; set; }
public string PreferredAudioTitle { get; set; }
public string PreferredSubtitleLanguageCode { get; set; }
public ChannelSubtitleMode? SubtitleMode { get; set; }

11
ErsatzTV/client-app/src/views/ChannelsPage.vue

@ -7,10 +7,13 @@ @@ -7,10 +7,13 @@
class="elevation-1"
>
<template v-slot:[`item.actions`]="{ item }">
<v-icon small class="mr-2" @click="editRow(item.id)">
mdi-lead-pencil
</v-icon>
<v-icon small @click="deleteRow(item.id)">mdi-delete</v-icon>
<v-btn icon class="mr-2" @click="editRow(item.id)">
<v-icon>mdi-lead-pencil</v-icon>
</v-btn>
<v-btn icon @click.stop="deleteRow(item.id)">
<v-icon>mdi-delete</v-icon></v-btn
>
</template>
</v-data-table>
</div>

Loading…
Cancel
Save