feat(ads/seo/release): v1.2.0 with live Google Ads, isolated ad profile, ads.txt, and SEO/AEO/GEO optimization
This commit is contained in:
parent
a88aafa1e5
commit
0e568f1f0a
975 changed files with 130593 additions and 16783 deletions
81
src/Paca.Core/VideoSpriteSheetGenerator.cs
Normal file
81
src/Paca.Core/VideoSpriteSheetGenerator.cs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Paca.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for tiled thumbnail sprite sheet generation.
|
||||
/// </summary>
|
||||
public sealed record SpriteSheetConfig(
|
||||
int Columns,
|
||||
int Rows,
|
||||
int ThumbWidth,
|
||||
int ThumbHeight,
|
||||
int IntervalSec)
|
||||
{
|
||||
public int TotalTilesPerSheet => Columns * Rows;
|
||||
public int SheetWidth => Columns * ThumbWidth;
|
||||
public int SheetHeight => Rows * ThumbHeight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates ffmpeg tile filter pipelines and WebVTT sprite coordinate mappings for scrubbing preview.
|
||||
/// </summary>
|
||||
public static class VideoSpriteSheetGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds ffmpeg CLI command line for rendering tiled thumbnail sprite sheet JPEG.
|
||||
/// </summary>
|
||||
public static string BuildFfmpegSpriteCommandLine(
|
||||
string inputVideoPath,
|
||||
string outputJpgPath,
|
||||
SpriteSheetConfig config)
|
||||
{
|
||||
if (config.IntervalSec <= 0) throw new ArgumentOutOfRangeException(nameof(config.IntervalSec), "Interval must be positive.");
|
||||
if (config.Columns <= 0 || config.Rows <= 0) throw new ArgumentOutOfRangeException(nameof(config), "Columns and Rows must be positive.");
|
||||
return $"-i \"{inputVideoPath}\" -vf \"fps=1/{config.IntervalSec},scale={config.ThumbWidth}:{config.ThumbHeight},tile={config.Columns}x{config.Rows}\" -frames:v 1 \"{outputJpgPath}\" -y";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates standard WebVTT subtitle/scrubber specification with #xywh coordinates.
|
||||
/// </summary>
|
||||
public static string GenerateVttMapping(
|
||||
string spriteFileName,
|
||||
double totalDurationSec,
|
||||
SpriteSheetConfig config)
|
||||
{
|
||||
if (config.IntervalSec <= 0) throw new ArgumentOutOfRangeException(nameof(config.IntervalSec), "Interval must be positive.");
|
||||
if (config.Columns <= 0 || config.Rows <= 0) throw new ArgumentOutOfRangeException(nameof(config), "Columns and Rows must be positive.");
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("WEBVTT");
|
||||
sb.AppendLine();
|
||||
|
||||
var tileIndex = 0;
|
||||
for (double start = 0; start < totalDurationSec; start += config.IntervalSec)
|
||||
{
|
||||
var end = Math.Min(start + config.IntervalSec, totalDurationSec);
|
||||
var startTimeStr = FormatVttTime(start);
|
||||
var endTimeStr = FormatVttTime(end);
|
||||
|
||||
var col = tileIndex % config.Columns;
|
||||
var row = (tileIndex / config.Columns) % config.Rows;
|
||||
var x = col * config.ThumbWidth;
|
||||
var y = row * config.ThumbHeight;
|
||||
|
||||
sb.AppendLine($"{startTimeStr} --> {endTimeStr}");
|
||||
sb.AppendLine($"{spriteFileName}#xywh={x},{y},{config.ThumbWidth},{config.ThumbHeight}");
|
||||
sb.AppendLine();
|
||||
|
||||
tileIndex++;
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string FormatVttTime(double seconds)
|
||||
{
|
||||
var ts = TimeSpan.FromSeconds(seconds);
|
||||
return $"{(int)ts.TotalHours:D2}:{ts.Minutes:D2}:{ts.Seconds:D2}.{ts.Milliseconds:D3}";
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue