feat(web): FFmpeg profile, filler preset, watermark editors (#143)
Three new SPA list+editor surfaces, closing the Blazor-only gap found in the #91 phase-(b) recon: - FFmpeg Profiles (System group): list with resolution/video/audio summary + add/copy/delete; full General/Video/Audio editor. Per-hwaccel video-format and profile rules ported from FFmpegProfileEditViewModelValidator; video preset list ported from FFmpegLibraryHelper.PresetsForFFmpegProfile / AvailablePresets.ForAccelAndFormat (cited in code). Resolutions from /api/settings/resolutions, hwaccel kinds from the new lookup endpoint. Copy = client-side clone into the create form (no server copy endpoint). - Filler Presets (Media group): list + editor with the Blazor VM interlocks (Fallback/Tail force mode None; non-MidRoll clears expression; Fallback disables chapters; Playlist collection type forces Count mode; duration/ count/pad fields gated by mode). Collection pickers use the existing /api/library/browse client for all seven collection types incl. Playlist. - Watermarks (System group): list + editor with mode-driven field enabling, ChannelLogo vs Custom image source, upload via /api/artwork/uploads (target=watermark) and preview via /artwork/watermarks/{path}. - App.tsx: ScreenIds/routes (sub-paths /add + /{id}) and nav registration; api modules ffmpegProfiles/fillerPresets/watermarks with URL-assert tests; SettingsScreen Classic-UI help text updated (these are no longer Blazor-only) and the FFmpeg-profiles card now links to the new screen. - Regenerated web/src/api/generated/v1.d.ts from the #159 OpenAPI changes. web verification: typecheck clean; vitest 188/188 (14 files); vite build OK; eslint clean on touched files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Vendored
+122
-9
@@ -303,6 +303,23 @@ export interface components {
|
||||
"normalizeFramerate": boolean;
|
||||
"normalizeColors": boolean;
|
||||
"deinterlaceVideo": boolean;
|
||||
};
|
||||
"CreateFillerPresetRequest": {
|
||||
"name": string;
|
||||
"fillerKind": components["schemas"]["FillerKind"];
|
||||
"fillerMode": components["schemas"]["FillerMode"];
|
||||
"duration": null | string;
|
||||
"count": null | number;
|
||||
"padToNearestMinute": null | number;
|
||||
"allowWatermarks": boolean;
|
||||
"collectionType": components["schemas"]["CollectionType"];
|
||||
"collectionId": null | number;
|
||||
"mediaItemId": null | number;
|
||||
"multiCollectionId": null | number;
|
||||
"smartCollectionId": null | number;
|
||||
"playlistId": null | number;
|
||||
"expression": null | string;
|
||||
"useChaptersAsMediaItems": boolean;
|
||||
};
|
||||
"CreatePlayoutRequest": {
|
||||
"channelId": number;
|
||||
@@ -323,21 +340,43 @@ export interface components {
|
||||
"CreateSmartCollectionRequest": {
|
||||
"name": null | string;
|
||||
"query": null | string;
|
||||
};
|
||||
"CreateWatermarkRequest": {
|
||||
"name": string;
|
||||
"mode": components["schemas"]["ChannelWatermarkMode"];
|
||||
"imageSource": components["schemas"]["ChannelWatermarkImageSource"];
|
||||
"image": null | string;
|
||||
"imageContentType": null | string;
|
||||
"location": components["schemas"]["WatermarkLocation"];
|
||||
"size": components["schemas"]["WatermarkSize"];
|
||||
"width": number;
|
||||
"horizontalMargin": number;
|
||||
"verticalMargin": number;
|
||||
"frequencyMinutes": number;
|
||||
"durationSeconds": number;
|
||||
"opacity": number;
|
||||
"opacityExpression": null | string;
|
||||
"zIndex": number;
|
||||
"placeWithinSourceContent": boolean;
|
||||
};
|
||||
"FFmpegFullProfileResponseModel": {
|
||||
"id": number;
|
||||
"name": null | string;
|
||||
"name": string;
|
||||
"threadCount": number;
|
||||
"normalizeAudio": boolean;
|
||||
"normalizeVideo": boolean;
|
||||
"hardwareAcceleration": components["schemas"]["HardwareAccelerationKind"];
|
||||
"vaapiDisplay": null | string;
|
||||
"vaapiDisplay": string;
|
||||
"vaapiDriver": components["schemas"]["VaapiDriver"];
|
||||
"vaapiDevice": null | string;
|
||||
"vaapiDevice": string;
|
||||
"qsvExtraHardwareFrames": null | number;
|
||||
"resolution": null | string;
|
||||
"resolutionId": number;
|
||||
"resolution": string;
|
||||
"scalingBehavior": components["schemas"]["ScalingBehavior"];
|
||||
"padMode": components["schemas"]["FilterMode"];
|
||||
"videoFormat": components["schemas"]["FFmpegProfileVideoFormat"];
|
||||
"videoProfile": null | string;
|
||||
"videoPreset": null | string;
|
||||
"videoProfile": string;
|
||||
"videoPreset": string;
|
||||
"allowBFrames": boolean;
|
||||
"bitDepth": components["schemas"]["FFmpegProfileBitDepth"];
|
||||
"videoBitrate": number;
|
||||
@@ -347,10 +386,12 @@ export interface components {
|
||||
"audioBitrate": number;
|
||||
"audioBufferSize": number;
|
||||
"normalizeLoudnessMode": components["schemas"]["NormalizeLoudnessMode"];
|
||||
"targetLoudness": null | number;
|
||||
"audioChannels": number;
|
||||
"audioSampleRate": number;
|
||||
"normalizeFramerate": boolean;
|
||||
"deinterlaceVideo": null | boolean;
|
||||
"normalizeColors": boolean;
|
||||
"deinterlaceVideo": boolean;
|
||||
};
|
||||
"FFmpegProfileAudioFormat": "None" | "Aac" | "Ac3" | "AacLatm" | "Copy";
|
||||
"FFmpegProfileBitDepth": "EightBit" | "TenBit";
|
||||
@@ -375,6 +416,24 @@ export interface components {
|
||||
};
|
||||
"FillerKind": "None" | "PreRoll" | "MidRoll" | "PostRoll" | "Tail" | "Fallback" | "GuideMode" | "DecoDefault";
|
||||
"FillerMode": "None" | "Duration" | "Count" | "Pad" | "RandomCount";
|
||||
"FillerPresetFullResponseModel": {
|
||||
"id": number;
|
||||
"name": string;
|
||||
"fillerKind": components["schemas"]["FillerKind"];
|
||||
"fillerMode": components["schemas"]["FillerMode"];
|
||||
"duration": null | string;
|
||||
"count": null | number;
|
||||
"padToNearestMinute": null | number;
|
||||
"allowWatermarks": boolean;
|
||||
"collectionType": components["schemas"]["CollectionType"];
|
||||
"collectionId": null | number;
|
||||
"mediaItemId": null | number;
|
||||
"multiCollectionId": null | number;
|
||||
"smartCollectionId": null | number;
|
||||
"playlistId": null | number;
|
||||
"expression": null | string;
|
||||
"useChaptersAsMediaItems": boolean;
|
||||
};
|
||||
"FillerPresetResponseModel": {
|
||||
"id": number;
|
||||
"name": null | string;
|
||||
@@ -832,6 +891,23 @@ export interface components {
|
||||
"initialSegmentCount": number;
|
||||
"hlsDirectOutputFormat": components["schemas"]["OutputFormatKind"];
|
||||
"defaultMpegTsScript": null | string;
|
||||
};
|
||||
"UpdateFillerPresetRequest": {
|
||||
"name": string;
|
||||
"fillerKind": components["schemas"]["FillerKind"];
|
||||
"fillerMode": components["schemas"]["FillerMode"];
|
||||
"duration": null | string;
|
||||
"count": null | number;
|
||||
"padToNearestMinute": null | number;
|
||||
"allowWatermarks": boolean;
|
||||
"collectionType": components["schemas"]["CollectionType"];
|
||||
"collectionId": null | number;
|
||||
"mediaItemId": null | number;
|
||||
"multiCollectionId": null | number;
|
||||
"smartCollectionId": null | number;
|
||||
"playlistId": null | number;
|
||||
"expression": null | string;
|
||||
"useChaptersAsMediaItems": boolean;
|
||||
};
|
||||
"UpdateHdhrSettingsRequest": {
|
||||
"tunerCount": number;
|
||||
@@ -867,6 +943,24 @@ export interface components {
|
||||
"UpdateUiSettingsRequest": {
|
||||
"isDarkMode": boolean;
|
||||
"language": null | string;
|
||||
};
|
||||
"UpdateWatermarkRequest": {
|
||||
"name": string;
|
||||
"mode": components["schemas"]["ChannelWatermarkMode"];
|
||||
"imageSource": components["schemas"]["ChannelWatermarkImageSource"];
|
||||
"image": null | string;
|
||||
"imageContentType": null | string;
|
||||
"location": components["schemas"]["WatermarkLocation"];
|
||||
"size": components["schemas"]["WatermarkSize"];
|
||||
"width": number;
|
||||
"horizontalMargin": number;
|
||||
"verticalMargin": number;
|
||||
"frequencyMinutes": number;
|
||||
"durationSeconds": number;
|
||||
"opacity": number;
|
||||
"opacityExpression": null | string;
|
||||
"zIndex": number;
|
||||
"placeWithinSourceContent": boolean;
|
||||
};
|
||||
"UpdateXmltvSettingsRequest": {
|
||||
"daysToBuild": number;
|
||||
@@ -874,12 +968,31 @@ export interface components {
|
||||
"blockBehavior": components["schemas"]["XmltvBlockBehavior"];
|
||||
};
|
||||
"VaapiDriver": "Default" | "iHD" | "i965" | "RadeonSI" | "Nouveau";
|
||||
"WatermarkLocation": number;
|
||||
"WatermarkFullResponseModel": {
|
||||
"id": number;
|
||||
"name": string;
|
||||
"mode": components["schemas"]["ChannelWatermarkMode"];
|
||||
"imageSource": components["schemas"]["ChannelWatermarkImageSource"];
|
||||
"image": null | string;
|
||||
"imageContentType": null | string;
|
||||
"location": components["schemas"]["WatermarkLocation"];
|
||||
"size": components["schemas"]["WatermarkSize"];
|
||||
"width": number;
|
||||
"horizontalMargin": number;
|
||||
"verticalMargin": number;
|
||||
"frequencyMinutes": number;
|
||||
"durationSeconds": number;
|
||||
"opacity": number;
|
||||
"opacityExpression": null | string;
|
||||
"zIndex": number;
|
||||
"placeWithinSourceContent": boolean;
|
||||
};
|
||||
"WatermarkLocation": "BottomRight" | "BottomLeft" | "TopRight" | "TopLeft" | "TopMiddle" | "RightMiddle" | "BottomMiddle" | "LeftMiddle" | "MiddleCenter";
|
||||
"WatermarkResponseModel": {
|
||||
"id": number;
|
||||
"name": null | string;
|
||||
};
|
||||
"WatermarkSize": number;
|
||||
"WatermarkSize": "Scaled" | "ActualSize";
|
||||
"WatermarkViewModel": {
|
||||
"id": number;
|
||||
"image": components["schemas"]["ArtworkContentTypeModel"];
|
||||
|
||||
Reference in New Issue
Block a user