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:
Yun Chan 2026-09-14 00:39:44 +09:00
parent a88aafa1e5
commit 0e568f1f0a
975 changed files with 130593 additions and 16783 deletions

View file

@ -0,0 +1,80 @@
using System;
using System.Globalization;
namespace Paca.Core;
/// <summary>
/// Image format for extracted video poster thumbnails.
/// </summary>
public enum PosterFormat
{
Jpeg,
Png,
WebP
}
/// <summary>
/// Specification for capturing high-quality single-frame video posters.
/// </summary>
public sealed record PosterExtractionSpec(
double SeekTimeSec,
int Width,
int Height,
PosterFormat Format,
int Quality = 85
);
/// <summary>
/// Video poster frame extractor generating intelligent seek points and scaling pipelines.
/// </summary>
public static class VideoPosterExtractor
{
/// <summary>
/// 배터리 절전 모드 활성화 시 CPU 집약적인 썸네일/포스터 프레임 추출을 일시 유예할지 판정합니다.
/// </summary>
public static bool ShouldDeferExtraction(Platform.BatterySaverPowerManager? powerManager) =>
powerManager?.ShouldDeferBackgroundProcessing ?? false;
/// <summary>
/// Computes optimal frame timestamp skipping black intro/logos while avoiding spoilers.
/// </summary>
public static double CalculateOptimalSeekTime(double totalDurationSec)
{
if (totalDurationSec <= 0)
return 0.0;
if (totalDurationSec < 10.0)
return Math.Min(1.0, totalDurationSec * 0.5);
return Math.Min(10.0, totalDurationSec * 0.1);
}
/// <summary>
/// Builds aspect-ratio preserving downscale filter.
/// </summary>
public static string BuildScalingFilter(PosterExtractionSpec spec)
{
return $"scale={spec.Width}:{spec.Height}:force_original_aspect_ratio=decrease";
}
/// <summary>
/// Builds full ffmpeg command line for single-frame thumbnail extraction.
/// </summary>
public static string BuildPosterCommandLine(
string inputVideoPath,
string outputImagePath,
PosterExtractionSpec spec)
{
var seekStr = spec.SeekTimeSec.ToString("F2", CultureInfo.InvariantCulture);
var scaleFilter = BuildScalingFilter(spec);
var encoderFlags = spec.Format switch
{
PosterFormat.WebP => $"-c:v libwebp -quality {spec.Quality}",
PosterFormat.Png => "-c:v png",
_ => "-q:v 2"
};
return $"-ss {seekStr} -i \"{inputVideoPath}\" -vframes 1 -vf \"{scaleFilter}\" {encoderFlags} \"{outputImagePath}\" -y";
}
}