mirror of https://github.com/ErsatzTV/ErsatzTV.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
148 lines
5.3 KiB
148 lines
5.3 KiB
using System.Text; |
|
using ErsatzTV.Core.Domain; |
|
using ErsatzTV.Core.Extensions; |
|
using ErsatzTV.Core.FFmpeg; |
|
using ErsatzTV.Core.Interfaces.FFmpeg; |
|
using Microsoft.Extensions.Logging; |
|
using Scriban; |
|
|
|
namespace ErsatzTV.Infrastructure.FFmpeg; |
|
|
|
public class MusicVideoCreditsGenerator : IMusicVideoCreditsGenerator |
|
{ |
|
private readonly ITempFilePool _tempFilePool; |
|
private readonly ILogger<MusicVideoCreditsGenerator> _logger; |
|
|
|
public MusicVideoCreditsGenerator(ITempFilePool tempFilePool, ILogger<MusicVideoCreditsGenerator> logger) |
|
{ |
|
_tempFilePool = tempFilePool; |
|
_logger = logger; |
|
} |
|
|
|
public async Task<Option<Subtitle>> GenerateCreditsSubtitle(MusicVideo musicVideo, FFmpegProfile ffmpegProfile) |
|
{ |
|
const int HORIZONTAL_MARGIN_PERCENT = 3; |
|
const int VERTICAL_MARGIN_PERCENT = 5; |
|
|
|
var fontSize = (int)Math.Round(ffmpegProfile.Resolution.Height / 20.0); |
|
|
|
int leftMarginPercent = HORIZONTAL_MARGIN_PERCENT; |
|
int rightMarginPercent = HORIZONTAL_MARGIN_PERCENT; |
|
|
|
var leftMargin = (int)Math.Round(leftMarginPercent / 100.0 * ffmpegProfile.Resolution.Width); |
|
var rightMargin = (int)Math.Round(rightMarginPercent / 100.0 * ffmpegProfile.Resolution.Width); |
|
var verticalMargin = |
|
(int)Math.Round(VERTICAL_MARGIN_PERCENT / 100.0 * ffmpegProfile.Resolution.Height); |
|
|
|
foreach (MusicVideoMetadata metadata in musicVideo.MusicVideoMetadata) |
|
{ |
|
var sb = new StringBuilder(); |
|
|
|
string artist = string.Empty; |
|
foreach (ArtistMetadata artistMetadata in Optional(metadata.MusicVideo?.Artist?.ArtistMetadata).Flatten()) |
|
{ |
|
artist = artistMetadata.Title; |
|
} |
|
|
|
if (!string.IsNullOrWhiteSpace(artist)) |
|
{ |
|
sb.Append(artist); |
|
} |
|
|
|
if (!string.IsNullOrWhiteSpace(metadata.Title)) |
|
{ |
|
sb.Append($"\\N\"{metadata.Title}\""); |
|
} |
|
|
|
if (!string.IsNullOrWhiteSpace(metadata.Album)) |
|
{ |
|
sb.Append($"\\N{metadata.Album}"); |
|
} |
|
|
|
string subtitles = await new SubtitleBuilder(_tempFilePool) |
|
.WithResolution(ffmpegProfile.Resolution) |
|
.WithFontName("OPTIKabel-Heavy") |
|
.WithFontSize(fontSize) |
|
.WithPrimaryColor("&HFFFFFF") |
|
.WithOutlineColor("&H444444") |
|
.WithAlignment(0) |
|
.WithMarginRight(rightMargin) |
|
.WithMarginLeft(leftMargin) |
|
.WithMarginV(verticalMargin) |
|
.WithBorderStyle(1) |
|
.WithShadow(3) |
|
.WithFormattedContent(sb.ToString()) |
|
.WithStartEnd(TimeSpan.FromSeconds(9), TimeSpan.FromSeconds(16)) |
|
.WithFade(true) |
|
.BuildFile(); |
|
|
|
return new Subtitle |
|
{ |
|
Codec = "ass", |
|
Default = true, |
|
Forced = true, |
|
IsExtracted = false, |
|
SubtitleKind = SubtitleKind.Generated, |
|
Path = subtitles, |
|
SDH = false |
|
}; |
|
} |
|
|
|
return None; |
|
} |
|
|
|
public async Task<Option<Subtitle>> GenerateCreditsSubtitleFromTemplate( |
|
MusicVideo musicVideo, |
|
FFmpegProfile ffmpegProfile, |
|
FFmpegPlaybackSettings settings, |
|
string templateFileName) |
|
{ |
|
try |
|
{ |
|
string text = await File.ReadAllTextAsync(templateFileName); |
|
var template = Template.Parse(text, templateFileName); |
|
foreach (MusicVideoMetadata metadata in musicVideo.MusicVideoMetadata) |
|
{ |
|
string artist = string.Empty; |
|
foreach (ArtistMetadata artistMetadata in Optional(musicVideo.Artist?.ArtistMetadata).Flatten()) |
|
{ |
|
artist = artistMetadata.Title; |
|
} |
|
|
|
string result = await template.RenderAsync( |
|
new |
|
{ |
|
ffmpegProfile.Resolution, |
|
metadata.Title, |
|
metadata.Track, |
|
metadata.Album, |
|
metadata.Plot, |
|
metadata.ReleaseDate, |
|
AllArtists = (metadata.Artists ?? new List<MusicVideoArtist>()).Map(a => a.Name), |
|
Artist = artist, |
|
musicVideo.GetHeadVersion().Duration, |
|
StreamSeek = await settings.StreamSeek.IfNoneAsync(TimeSpan.Zero) |
|
}); |
|
|
|
string fileName = _tempFilePool.GetNextTempFile(TempFileCategory.Subtitle); |
|
await File.WriteAllTextAsync(fileName, result); |
|
return new Subtitle |
|
{ |
|
Codec = "ass", |
|
Default = true, |
|
Forced = true, |
|
IsExtracted = false, |
|
SubtitleKind = SubtitleKind.Generated, |
|
Path = fileName, |
|
SDH = false |
|
}; |
|
} |
|
} |
|
catch (Exception ex) |
|
{ |
|
_logger.LogError(ex, "Error generating music video credits from template {Template}", templateFileName); |
|
} |
|
|
|
return None; |
|
} |
|
}
|
|
|