mirror of
https://github.com/vondas-network/videobeaux.git
synced 2025-12-05 15:30:02 +01:00
added lagkage and updating
This commit is contained in:
402
docs/docs-lagkage-examples.txt
Normal file
402
docs/docs-lagkage-examples.txt
Normal file
@@ -0,0 +1,402 @@
|
||||
LAGKAGE – CORE FUNCTIONALITY EXAMPLES (v2)
|
||||
========================================
|
||||
|
||||
Program:
|
||||
lagkage (Videobeaux)
|
||||
|
||||
Base usage pattern:
|
||||
videobeaux -P lagkage -i media/base.mp4 -o out/NAME.mp4 --layout-json lagkage_layouts/LAYOUT.json --force
|
||||
|
||||
This file contains 20+ examples demonstrating:
|
||||
- place vs free positioning
|
||||
- images / videos / GIFs
|
||||
- sequence_direction variations
|
||||
- per-layer zoom
|
||||
- per-layer crop
|
||||
- combinations of the above
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 01 – Minimal logo + sticker (place + free)
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_minimal.json
|
||||
|
||||
Description:
|
||||
- Top-right logo bug using place mode
|
||||
- Free-position looping GIF sticker at lower-left area
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex01_minimal_logo_sticker.mp4 \
|
||||
--layout-json lagkage_layouts/layout_minimal.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 02 – Same layout, different base
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_minimal.json
|
||||
|
||||
Description:
|
||||
- Reuses the same layout with a different base video (e.g. a show cut)
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base_show.mp4 \
|
||||
-o out/ex02_minimal_logo_sticker_show.mp4 \
|
||||
--layout-json lagkage_layouts/layout_minimal.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 03 – Dual PIP + logo + lower third
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_pip_dual.json
|
||||
|
||||
Description:
|
||||
- Logo in top-left
|
||||
- Left and right PIP cameras using free mode
|
||||
- Image lower-third band
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex03_pip_dual_show.mp4 \
|
||||
--layout-json lagkage_layouts/layout_pip_dual.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 04 – Dual PIP, backward stacking
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_pip_dual.json
|
||||
(Edit JSON: "sequence_direction": "backward")
|
||||
|
||||
Description:
|
||||
- Same positions as Example 03
|
||||
- Backward stacking puts higher layer_number on top
|
||||
- Good for making sure logo always ends above other overlays
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex04_pip_dual_backward.mp4 \
|
||||
--layout-json lagkage_layouts/layout_pip_dual.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 05 – Stickers + VHS noise overlay
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_stickers.json
|
||||
|
||||
Description:
|
||||
- Full-frame looping VHS noise (low opacity)
|
||||
- Circus and bunny GIF stickers
|
||||
- Floating logo
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex05_stickers_vhs_noise.mp4 \
|
||||
--layout-json lagkage_layouts/layout_stickers.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 06 – Freeform showcase (cams, gifs, masks, noise)
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_freeform_showcase.json
|
||||
|
||||
Description:
|
||||
- Multiple layers:
|
||||
- top-right logo, top-left screen
|
||||
- left/right PIP cams, mask on edge, lower band
|
||||
- circus & bunny GIFs, center VHS noise
|
||||
- All positioned using free mode coordinates
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex06_freeform_showcase.mp4 \
|
||||
--layout-json lagkage_layouts/layout_freeform_showcase.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 07 – 3-up horizontal video grid
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_grid_3up.json
|
||||
|
||||
Description:
|
||||
- Three video tiles left/center/right
|
||||
- Small logo in top-left
|
||||
- Good for side-by-side comparison or triptych views
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex07_grid_3up.mp4 \
|
||||
--layout-json lagkage_layouts/layout_grid_3up.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 08 – 4-up grid (quad cam)
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_grid_4up.json
|
||||
|
||||
Description:
|
||||
- Four camera feeds in a 2x2 grid
|
||||
- Classic multi-cam surveillance / gallery layout
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex08_grid_4up.mp4 \
|
||||
--layout-json lagkage_layouts/layout_grid_4up.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 09 – Heavy VHS wash + mask + logo
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_vhs_heavy.json
|
||||
|
||||
Description:
|
||||
- VHS noise strongly overlayed (higher opacity)
|
||||
- Center mask image
|
||||
- Top-center logo
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex09_vhs_heavy_mask_logo.mp4 \
|
||||
--layout-json lagkage_layouts/layout_vhs_heavy.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 10 – Logo wall + mid screens
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_logo_wall.json
|
||||
|
||||
Description:
|
||||
- Logos in all four corners using place mode
|
||||
- Two mid-frame screen images using free mode
|
||||
- Good for brand-heavy overlays
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex10_logo_wall.mp4 \
|
||||
--layout-json lagkage_layouts/layout_logo_wall.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 11 – Random spray of gifs + center logo
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_random_spray.json
|
||||
(sequence_direction set to "random")
|
||||
|
||||
Description:
|
||||
- Circus and bunny GIFs in four corners/edges
|
||||
- Logo in center
|
||||
- Randomized stacking order each run (depending on code behavior)
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex11_random_spray.mp4 \
|
||||
--layout-json lagkage_layouts/layout_random_spray.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 12 – Story mode: lower third + mask + tiny PIP
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_story_mode.json
|
||||
|
||||
Description:
|
||||
- Wide lower-third band defined via free mode
|
||||
- Side mask element
|
||||
- Small corner PIP video + logo
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex12_story_mode_package.mp4 \
|
||||
--layout-json lagkage_layouts/layout_story_mode.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 13 – Freeform scatter demo
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_freeform_showcase.json
|
||||
|
||||
Description:
|
||||
- Same freeform layout as Example 06
|
||||
- Emphasis on scattered positioning to test boundaries
|
||||
- Demonstrates partially off-screen elements
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex13_freeform_scatter.mp4 \
|
||||
--layout-json lagkage_layouts/layout_freeform_showcase.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 14 – Minimal zoom-only sticker
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_minimal_zoomcrop.json
|
||||
(Create JSON from README example)
|
||||
|
||||
Description:
|
||||
- Corner logo
|
||||
- One GIF sticker with zoom > 1.0 but no crop, so content is pushed in
|
||||
- Demonstrates per-layer zoom inside fixed box
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex14_minimal_zoom_sticker.mp4 \
|
||||
--layout-json lagkage_layouts/layout_minimal_zoomcrop.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 15 – Center-cropped GIF in the corner
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_cropped_gif_corner.json
|
||||
(GIF layer has crop_x/crop_y/crop_w/crop_h set to central 50%)
|
||||
|
||||
Description:
|
||||
- Uses only the core of a noisy or busy GIF
|
||||
- Placed bottom-right as a cropped badge
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex15_cropped_gif_corner.mp4 \
|
||||
--layout-json lagkage_layouts/layout_cropped_gif_corner.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 16 – Zoomed-in PIP camera box
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_zoomed_pip.json
|
||||
(PIP layer has zoom > 1.0)
|
||||
|
||||
Description:
|
||||
- Small PIP box, but content is zoomed in
|
||||
- Good for focusing on a region of the camera feed without changing frame size
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex16_zoomed_pip_box.mp4 \
|
||||
--layout-json lagkage_layouts/layout_zoomed_pip.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 17 – Combined crop + zoom (GIF)
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_crop_zoom_gif.json
|
||||
|
||||
Description:
|
||||
- Layer crops to middle 60% of GIF
|
||||
- Then applies zoom=1.8 inside overlay box
|
||||
- Great for punching into the “interesting” part of the animation
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex17_crop_zoom_gif.mp4 \
|
||||
--layout-json lagkage_layouts/layout_crop_zoom_gif.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 18 – Split-screen: left static, right zoom+crop
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_splitscreen_zoomcrop.json
|
||||
|
||||
Description:
|
||||
- Left side: standard PIP video
|
||||
- Right side: same video source but with crop+zoom on a different region
|
||||
- Demonstrates comparative views of the same source file
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex18_splitscreen_zoomcrop.mp4 \
|
||||
--layout-json lagkage_layouts/layout_splitscreen_zoomcrop.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 19 – Multi-logo scatter + zoomed center logo
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_logo_scatter_zoom_center.json
|
||||
|
||||
Description:
|
||||
- Logos in corners and midpoints using place + free
|
||||
- Center logo uses zoom to feel larger within a fixed box
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex19_logo_scatter_zoom_center.mp4 \
|
||||
--layout-json lagkage_layouts/layout_logo_scatter_zoom_center.json \
|
||||
--force
|
||||
|
||||
|
||||
------------------------------------------------------------
|
||||
EXAMPLE 20 – Full package: PIPs, stickers, crop+zoom, noise
|
||||
------------------------------------------------------------
|
||||
|
||||
Layout:
|
||||
lagkage_layouts/layout_full_package_showcase.json
|
||||
|
||||
Description:
|
||||
- Combines:
|
||||
- Base logo
|
||||
- Dual PIPs
|
||||
- Multiple GIF stickers (some cropped, some zoomed)
|
||||
- VHS noise layer at low opacity
|
||||
- Designed as an “everything on” stress test for lagkage
|
||||
|
||||
Command:
|
||||
videobeaux -P lagkage -i media/base.mp4 \
|
||||
-o out/ex20_full_package_showcase.mp4 \
|
||||
--layout-json lagkage_layouts/layout_full_package_showcase.json \
|
||||
--force
|
||||
|
||||
|
||||
END
|
||||
BIN
lagkage_all_layouts_zoomcrop_examples.zip
Normal file
BIN
lagkage_all_layouts_zoomcrop_examples.zip
Normal file
Binary file not shown.
88
lagkage_examples_zoomcrop.txt
Normal file
88
lagkage_examples_zoomcrop.txt
Normal file
@@ -0,0 +1,88 @@
|
||||
Lagkage Zoom + Crop Example Commands
|
||||
====================================
|
||||
|
||||
These examples assume:
|
||||
- You are in the project root.
|
||||
- Base video is at: media/base.mp4
|
||||
- JSON layouts are in: lagkage_layouts/
|
||||
- Output directory: out/
|
||||
|
||||
1) Corner logo with zoom
|
||||
------------------------
|
||||
videobeaux -P lagkage \
|
||||
-i media/base.mp4 \
|
||||
-o out/ex01_logo_corner_zoom.mp4 \
|
||||
--layout-json lagkage_layouts/layout_ex01_logo_corner_zoom.json \
|
||||
--force
|
||||
|
||||
2) GIF sticker with zoom + crop
|
||||
-------------------------------
|
||||
videobeaux -P lagkage \
|
||||
-i media/base.mp4 \
|
||||
-o out/ex02_gif_zoomcrop_sticker.mp4 \
|
||||
--layout-json lagkage_layouts/layout_ex02_gif_zoomcrop_sticker.json \
|
||||
--force
|
||||
|
||||
3) Dual PIP, right pane zoomed
|
||||
------------------------------
|
||||
videobeaux -P lagkage \
|
||||
-i media/base.mp4 \
|
||||
-o out/ex03_dual_pip_zoom_right.mp4 \
|
||||
--layout-json lagkage_layouts/layout_ex03_dual_pip_zoom_right.json \
|
||||
--force
|
||||
|
||||
4) Lower-third band via crop
|
||||
----------------------------
|
||||
videobeaux -P lagkage \
|
||||
-i media/base.mp4 \
|
||||
-o out/ex04_lower_third_crop_band.mp4 \
|
||||
--layout-json lagkage_layouts/layout_ex04_lower_third_crop_band.json \
|
||||
--force
|
||||
|
||||
5) Center mask, cropped + zoomed
|
||||
--------------------------------
|
||||
videobeaux -P lagkage \
|
||||
-i media/base.mp4 \
|
||||
-o out/ex05_mask_crop_zoom_center.mp4 \
|
||||
--layout-json lagkage_layouts/layout_ex05_mask_crop_zoom_center.json \
|
||||
--force
|
||||
|
||||
6) Freeform spray (GIFs + logo, random sequence)
|
||||
------------------------------------------------
|
||||
videobeaux -P lagkage \
|
||||
-i media/base.mp4 \
|
||||
-o out/ex06_freeform_spray_zoom.mp4 \
|
||||
--layout-json lagkage_layouts/layout_ex06_freeform_spray_zoom.json \
|
||||
--force
|
||||
|
||||
7) Logo grid with zoomed center
|
||||
-------------------------------
|
||||
videobeaux -P lagkage \
|
||||
-i media/base.mp4 \
|
||||
-o out/ex07_logo_grid_zoom_center.mp4 \
|
||||
--layout-json lagkage_layouts/layout_ex07_logo_grid_zoom_center.json \
|
||||
--force
|
||||
|
||||
8) Story mode: band + zoomed PIP + logo
|
||||
---------------------------------------
|
||||
videobeaux -P lagkage \
|
||||
-i media/base.mp4 \
|
||||
-o out/ex08_story_mode_zoom.mp4 \
|
||||
--layout-json lagkage_layouts/layout_ex08_story_mode_zoom.json \
|
||||
--force
|
||||
|
||||
9) Split-screen: left zoom+crop, right normal
|
||||
---------------------------------------------
|
||||
videobeaux -P lagkage \
|
||||
-i media/base.mp4 \
|
||||
-o out/ex09_split_screen_zoom_left.mp4 \
|
||||
--layout-json lagkage_layouts/layout_ex09_split_screen_zoom_left.json \
|
||||
--force
|
||||
|
||||
10) Full showcase (logos, bands, PIPs, GIFs)
|
||||
--------------------------------------------
|
||||
videobeaux -P lagkage \
|
||||
-i media/base.mp4 \
|
||||
-o out/ex10_full_showcase_zoomcrop.mp4 \
|
||||
--layout-json lagkage_layouts/layout_ex10_full_showcase_zoomcrop.json \
|
||||
--force
|
||||
93
lagkage_layouts/layout_big_6.json
Normal file
93
lagkage_layouts/layout_big_6.json
Normal file
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "logo_top_right_free",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 20,
|
||||
"pos_y": 420,
|
||||
"size": 30,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "cropped_circus",
|
||||
"filename": "../media/circus.gif",
|
||||
"type": "gif",
|
||||
"mode": "place",
|
||||
"place": "bottom_right",
|
||||
"size": 20,
|
||||
"opacity": 1.0,
|
||||
"zoom": 1.0,
|
||||
"crop_x": 25,
|
||||
"crop_y": 25,
|
||||
"crop_w": 50,
|
||||
"crop_h": 50
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "zoomed_bunny",
|
||||
"filename": "../media/bunny.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 200,
|
||||
"pos_y": 500,
|
||||
"size": 15,
|
||||
"opacity": 1.0,
|
||||
"zoom": 2.0
|
||||
}
|
||||
,
|
||||
{
|
||||
"layer_number": 6,
|
||||
"name": "gross_lower_band_free",
|
||||
"filename": "../media/gross.jpg",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 180,
|
||||
"pos_y": 180,
|
||||
"size": 25,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 7,
|
||||
"name": "circus_sticker_top_center_free",
|
||||
"filename": "../media/circus.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 60,
|
||||
"pos_y": 260,
|
||||
"size": 50,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 8,
|
||||
"name": "bunny_sticker_right_mid_free",
|
||||
"filename": "../media/bunny.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 50,
|
||||
"pos_y": 365,
|
||||
"size": 75,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 9,
|
||||
"name": "vhs_noise_center_free",
|
||||
"filename": "../media/media.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 175,
|
||||
"pos_y": 350,
|
||||
"size": 60,
|
||||
"opacity": 0.3,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
34
lagkage_layouts/layout_crop_zoom_gif.json
Normal file
34
lagkage_layouts/layout_crop_zoom_gif.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "logo_top_center",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 840,
|
||||
"pos_y": 40,
|
||||
"size": 18,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "crop_zoom_circus",
|
||||
"filename": "../media/circus.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 260,
|
||||
"pos_y": 260,
|
||||
"size": 22,
|
||||
"opacity": 1.0,
|
||||
"zoom": 1.8,
|
||||
"crop_x": 20,
|
||||
"crop_y": 20,
|
||||
"crop_w": 60,
|
||||
"crop_h": 60,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
31
lagkage_layouts/layout_cropped_gif_corner.json
Normal file
31
lagkage_layouts/layout_cropped_gif_corner.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "corner_logo",
|
||||
"filename": "../media/logo.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_left",
|
||||
"size": 10,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "cropped_circus_corner",
|
||||
"filename": "../media/circus.gif",
|
||||
"type": "gif",
|
||||
"mode": "place",
|
||||
"place": "bottom_right",
|
||||
"size": 20,
|
||||
"opacity": 1.0,
|
||||
"crop_x": 25,
|
||||
"crop_y": 25,
|
||||
"crop_w": 50,
|
||||
"crop_h": 50,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
16
lagkage_layouts/layout_ex01_logo_corner_zoom.json
Normal file
16
lagkage_layouts/layout_ex01_logo_corner_zoom.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "logo_zoom_top_right",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_right",
|
||||
"size": 42,
|
||||
"opacity": 0.95,
|
||||
"zoom": 4.5
|
||||
}
|
||||
]
|
||||
}
|
||||
21
lagkage_layouts/layout_ex02_gif_zoomcrop_sticker.json
Normal file
21
lagkage_layouts/layout_ex02_gif_zoomcrop_sticker.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "gif_zoomcrop_sticker",
|
||||
"filename": "../media/circus.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 150,
|
||||
"pos_y": 420,
|
||||
"size": 18,
|
||||
"opacity": 1.0,
|
||||
"zoom": 1.8,
|
||||
"crop_x": 10,
|
||||
"crop_y": 10,
|
||||
"crop_w": 80,
|
||||
"crop_h": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
28
lagkage_layouts/layout_ex03_dual_pip_zoom_right.json
Normal file
28
lagkage_layouts/layout_ex03_dual_pip_zoom_right.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "pip_left_normal",
|
||||
"filename": "../media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 80,
|
||||
"pos_y": 160,
|
||||
"size": 28,
|
||||
"opacity": 1.0
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "pip_right_zoomed",
|
||||
"filename": "../media/faith.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 1180,
|
||||
"pos_y": 160,
|
||||
"size": 28,
|
||||
"opacity": 1.0,
|
||||
"zoom": 1.7
|
||||
}
|
||||
]
|
||||
}
|
||||
20
lagkage_layouts/layout_ex04_lower_third_crop_band.json
Normal file
20
lagkage_layouts/layout_ex04_lower_third_crop_band.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "lower_third_band_crop",
|
||||
"filename": "../media/gross.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 260,
|
||||
"pos_y": 780,
|
||||
"size": 54,
|
||||
"opacity": 0.95,
|
||||
"crop_x": 0,
|
||||
"crop_y": 20,
|
||||
"crop_w": 100,
|
||||
"crop_h": 60
|
||||
}
|
||||
]
|
||||
}
|
||||
20
lagkage_layouts/layout_ex05_mask_crop_zoom_center.json
Normal file
20
lagkage_layouts/layout_ex05_mask_crop_zoom_center.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "mask_center_zoomcrop",
|
||||
"filename": "../media/mask1.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "center",
|
||||
"size": 30,
|
||||
"opacity": 0.9,
|
||||
"zoom": 1.6,
|
||||
"crop_x": 10,
|
||||
"crop_y": 10,
|
||||
"crop_w": 80,
|
||||
"crop_h": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
39
lagkage_layouts/layout_ex06_freeform_spray_zoom.json
Normal file
39
lagkage_layouts/layout_ex06_freeform_spray_zoom.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"sequence_direction": "random",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "circus_left_zoom",
|
||||
"filename": "../media/circus.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 120,
|
||||
"pos_y": 200,
|
||||
"size": 20,
|
||||
"opacity": 1.0,
|
||||
"zoom": 1.5
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "bunny_right_zoom",
|
||||
"filename": "../media/bunny.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 100,
|
||||
"pos_y": 320,
|
||||
"size": 26,
|
||||
"opacity": 1.0,
|
||||
"zoom": 2.5
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "logo_center",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "center",
|
||||
"size": 48,
|
||||
"opacity": 4.95
|
||||
}
|
||||
]
|
||||
}
|
||||
56
lagkage_layouts/layout_ex07_logo_grid_zoom_center.json
Normal file
56
lagkage_layouts/layout_ex07_logo_grid_zoom_center.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "logo_tl",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_left",
|
||||
"size": 10,
|
||||
"opacity": 0.9
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "logo_tr",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_right",
|
||||
"size": 10,
|
||||
"opacity": 0.9
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "logo_bl",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "bottom_left",
|
||||
"size": 10,
|
||||
"opacity": 0.9
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "logo_br",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "bottom_right",
|
||||
"size": 10,
|
||||
"opacity": 0.9
|
||||
},
|
||||
{
|
||||
"layer_number": 5,
|
||||
"name": "logo_center_zoom",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "center",
|
||||
"size": 16,
|
||||
"opacity": 0.95,
|
||||
"zoom": 1.7
|
||||
}
|
||||
]
|
||||
}
|
||||
37
lagkage_layouts/layout_ex08_story_mode_zoom.json
Normal file
37
lagkage_layouts/layout_ex08_story_mode_zoom.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "band_bottom",
|
||||
"filename": "../media/gross.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 220,
|
||||
"pos_y": 800,
|
||||
"size": 60,
|
||||
"opacity": 0.95
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "pip_top_right_zoom",
|
||||
"filename": "../media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "place",
|
||||
"place": "top_right",
|
||||
"size": 22,
|
||||
"opacity": 1.0,
|
||||
"zoom": 1.6
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "logo_top_left",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_left",
|
||||
"size": 12,
|
||||
"opacity": 0.95
|
||||
}
|
||||
]
|
||||
}
|
||||
32
lagkage_layouts/layout_ex09_split_screen_zoom_left.json
Normal file
32
lagkage_layouts/layout_ex09_split_screen_zoom_left.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "pip_left_zoomcrop",
|
||||
"filename": "../media/faith.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 80,
|
||||
"pos_y": 160,
|
||||
"size": 42,
|
||||
"opacity": 1.0,
|
||||
"zoom": 1.8,
|
||||
"crop_x": 10,
|
||||
"crop_y": 10,
|
||||
"crop_w": 80,
|
||||
"crop_h": 80
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "pip_right_normal",
|
||||
"filename": "../media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 1040,
|
||||
"pos_y": 160,
|
||||
"size": 42,
|
||||
"opacity": 1.0
|
||||
}
|
||||
]
|
||||
}
|
||||
76
lagkage_layouts/layout_ex10_full_showcase_zoomcrop.json
Normal file
76
lagkage_layouts/layout_ex10_full_showcase_zoomcrop.json
Normal file
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "corner_logo",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_left",
|
||||
"size": 14,
|
||||
"opacity": 0.95
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "pip_left",
|
||||
"filename": "../media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 120,
|
||||
"pos_y": 220,
|
||||
"size": 24,
|
||||
"opacity": 1.0
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "pip_right_zoom",
|
||||
"filename": "../media/faith.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 1180,
|
||||
"pos_y": 220,
|
||||
"size": 24,
|
||||
"opacity": 1.0,
|
||||
"zoom": 1.5
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "lower_third_band",
|
||||
"filename": "../media/gross.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 260,
|
||||
"pos_y": 780,
|
||||
"size": 54,
|
||||
"opacity": 0.95
|
||||
},
|
||||
{
|
||||
"layer_number": 5,
|
||||
"name": "circus_zoomcrop",
|
||||
"filename": "../media/circus.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 420,
|
||||
"pos_y": 260,
|
||||
"size": 18,
|
||||
"opacity": 1.0,
|
||||
"zoom": 1.8,
|
||||
"crop_x": 20,
|
||||
"crop_y": 20,
|
||||
"crop_w": 60,
|
||||
"crop_h": 60
|
||||
},
|
||||
{
|
||||
"layer_number": 6,
|
||||
"name": "bunny_free",
|
||||
"filename": "../media/bunny.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 1320,
|
||||
"pos_y": 260,
|
||||
"size": 16,
|
||||
"opacity": 1.0
|
||||
}
|
||||
]
|
||||
}
|
||||
113
lagkage_layouts/layout_freeform_showcase.json
Normal file
113
lagkage_layouts/layout_freeform_showcase.json
Normal file
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "logo_top_right_free",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 1500,
|
||||
"pos_y": 40,
|
||||
"size": 14,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "screen_top_left_free",
|
||||
"filename": "../media/screen.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 120,
|
||||
"pos_y": 70,
|
||||
"size": 12,
|
||||
"opacity": 0.8,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "pip_cam_a_left_mid_free",
|
||||
"filename": "../media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 260,
|
||||
"pos_y": 260,
|
||||
"size": 24,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "pip_cam_b_right_low_free",
|
||||
"filename": "../media/faith.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 1180,
|
||||
"pos_y": 540,
|
||||
"size": 34,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 5,
|
||||
"name": "mask_left_edge_half_off_free",
|
||||
"filename": "../media/mask1.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": -50,
|
||||
"pos_y": 360,
|
||||
"size": 22,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 6,
|
||||
"name": "gross_lower_band_free",
|
||||
"filename": "../media/gross.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 340,
|
||||
"pos_y": 780,
|
||||
"size": 50,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 7,
|
||||
"name": "circus_sticker_top_center_free",
|
||||
"filename": "../media/circus.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 760,
|
||||
"pos_y": 120,
|
||||
"size": 18,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 8,
|
||||
"name": "bunny_sticker_right_mid_free",
|
||||
"filename": "../media/bunny.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 1450,
|
||||
"pos_y": 320,
|
||||
"size": 13,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 9,
|
||||
"name": "vhs_noise_center_free",
|
||||
"filename": "../media/media.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 0,
|
||||
"pos_y": 0,
|
||||
"size": 110,
|
||||
"opacity": 0.3,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
106
lagkage_layouts/layout_full_package_showcase.json
Normal file
106
lagkage_layouts/layout_full_package_showcase.json
Normal file
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "corner_logo",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_left",
|
||||
"size": 14,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "pip_left",
|
||||
"filename": "../media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 120,
|
||||
"pos_y": 200,
|
||||
"size": 24,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "pip_right",
|
||||
"filename": "../media/faith.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 1180,
|
||||
"pos_y": 200,
|
||||
"size": 24,
|
||||
"opacity": 1.0,
|
||||
"zoom": 1.4,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "lower_third_band",
|
||||
"filename": "../media/gross.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 260,
|
||||
"pos_y": 780,
|
||||
"size": 54,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 5,
|
||||
"name": "mask_left_edge",
|
||||
"filename": "../media/mask1.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": -40,
|
||||
"pos_y": 320,
|
||||
"size": 26,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 6,
|
||||
"name": "circus_sticker_zoomcrop",
|
||||
"filename": "../media/circus.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 420,
|
||||
"pos_y": 260,
|
||||
"size": 18,
|
||||
"opacity": 1.0,
|
||||
"zoom": 1.8,
|
||||
"crop_x": 20,
|
||||
"crop_y": 20,
|
||||
"crop_w": 60,
|
||||
"crop_h": 60,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 7,
|
||||
"name": "bunny_sticker_free",
|
||||
"filename": "../media/bunny.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 1320,
|
||||
"pos_y": 260,
|
||||
"size": 16,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 8,
|
||||
"name": "vhs_noise_background",
|
||||
"filename": "../media/media.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 0,
|
||||
"pos_y": 0,
|
||||
"size": 110,
|
||||
"opacity": 0.3,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
52
lagkage_layouts/layout_grid_3up.json
Normal file
52
lagkage_layouts/layout_grid_3up.json
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "logo_top_left_small",
|
||||
"filename": "../media/logo.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_left",
|
||||
"size": 10,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "tile_left",
|
||||
"filename": "../media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 80,
|
||||
"pos_y": 160,
|
||||
"size": 28,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "tile_center",
|
||||
"filename": "../media/faith.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 640,
|
||||
"pos_y": 160,
|
||||
"size": 28,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "tile_right",
|
||||
"filename": "../media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 1200,
|
||||
"pos_y": 160,
|
||||
"size": 28,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
53
lagkage_layouts/layout_grid_4up.json
Normal file
53
lagkage_layouts/layout_grid_4up.json
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "cam_tl",
|
||||
"filename": "../media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 80,
|
||||
"pos_y": 80,
|
||||
"size": 40,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "cam_tr",
|
||||
"filename": "../media/faith.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 1040,
|
||||
"pos_y": 80,
|
||||
"size": 40,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "cam_bl",
|
||||
"filename": "../media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 80,
|
||||
"pos_y": 540,
|
||||
"size": 40,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "cam_br",
|
||||
"filename": "../media/faith.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 1040,
|
||||
"pos_y": 540,
|
||||
"size": 40,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
85
lagkage_layouts/layout_logo_scatter_zoom_center.json
Normal file
85
lagkage_layouts/layout_logo_scatter_zoom_center.json
Normal file
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "logo_tl",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_left",
|
||||
"size": 10,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "logo_tr",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_right",
|
||||
"size": 10,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "logo_bl",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "bottom_left",
|
||||
"size": 10,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "logo_br",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "bottom_right",
|
||||
"size": 10,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 5,
|
||||
"name": "logo_mid_left",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 220,
|
||||
"pos_y": 360,
|
||||
"size": 10,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 6,
|
||||
"name": "logo_mid_right",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 1400,
|
||||
"pos_y": 360,
|
||||
"size": 10,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 7,
|
||||
"name": "logo_center_zoomed",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "center",
|
||||
"size": 18,
|
||||
"opacity": 0.95,
|
||||
"zoom": 1.6,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
73
lagkage_layouts/layout_logo_wall.json
Normal file
73
lagkage_layouts/layout_logo_wall.json
Normal file
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "logo_tl",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_left",
|
||||
"size": 10,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "logo_tr",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_right",
|
||||
"size": 10,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "logo_bl",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "bottom_left",
|
||||
"size": 10,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "logo_br",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "bottom_right",
|
||||
"size": 10,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 5,
|
||||
"name": "screen_mid_left",
|
||||
"filename": "../media/screen.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 200,
|
||||
"pos_y": 300,
|
||||
"size": 14,
|
||||
"opacity": 0.8,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 6,
|
||||
"name": "screen_mid_right",
|
||||
"filename": "../media/screen.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 1300,
|
||||
"pos_y": 300,
|
||||
"size": 14,
|
||||
"opacity": 0.8,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
28
lagkage_layouts/layout_minimal.json
Normal file
28
lagkage_layouts/layout_minimal.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "corner_logo",
|
||||
"filename": "../media/logo.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_right",
|
||||
"size": 12,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "looping_sticker",
|
||||
"filename": "../media/sticker.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 160,
|
||||
"pos_y": 420,
|
||||
"size": 18,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
33
lagkage_layouts/layout_minimal_zoomcrop.json
Normal file
33
lagkage_layouts/layout_minimal_zoomcrop.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "corner_logo",
|
||||
"filename": "../media/logo.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_right",
|
||||
"size": 12,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "zoomcrop_sticker",
|
||||
"filename": "../media/sticker.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 160,
|
||||
"pos_y": 420,
|
||||
"size": 18,
|
||||
"opacity": 1.0,
|
||||
"zoom": 1.5,
|
||||
"crop_x": 10,
|
||||
"crop_y": 10,
|
||||
"crop_w": 80,
|
||||
"crop_h": 80,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
52
lagkage_layouts/layout_pip_dual.json
Normal file
52
lagkage_layouts/layout_pip_dual.json
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "logo_top_left",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_left",
|
||||
"size": 16,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "pip_cam_left",
|
||||
"filename": "../media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 80,
|
||||
"pos_y": 120,
|
||||
"size": 26,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "pip_cam_right",
|
||||
"filename": "../media/faith.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 1180,
|
||||
"pos_y": 120,
|
||||
"size": 26,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "lower_third_band",
|
||||
"filename": "../media/gross.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 260,
|
||||
"pos_y": 780,
|
||||
"size": 54,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
64
lagkage_layouts/layout_random_spray.json
Normal file
64
lagkage_layouts/layout_random_spray.json
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"sequence_direction": "random",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "circus_left",
|
||||
"filename": "../media/circus.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 120,
|
||||
"pos_y": 200,
|
||||
"size": 22,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "circus_right",
|
||||
"filename": "../media/circus.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 1400,
|
||||
"pos_y": 100,
|
||||
"size": 18,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "bunny_low_left",
|
||||
"filename": "../media/bunny.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 80,
|
||||
"pos_y": 640,
|
||||
"size": 18,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "bunny_low_right",
|
||||
"filename": "../media/bunny.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 1480,
|
||||
"pos_y": 640,
|
||||
"size": 18,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 5,
|
||||
"name": "logo_center",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "center",
|
||||
"size": 20,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
34
lagkage_layouts/layout_splitscreen_zoomcrop.json
Normal file
34
lagkage_layouts/layout_splitscreen_zoomcrop.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "pip_left_normal",
|
||||
"filename": "../media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 80,
|
||||
"pos_y": 160,
|
||||
"size": 40,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "pip_right_zoomcrop",
|
||||
"filename": "../media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 1040,
|
||||
"pos_y": 160,
|
||||
"size": 40,
|
||||
"opacity": 1.0,
|
||||
"zoom": 1.6,
|
||||
"crop_x": 15,
|
||||
"crop_y": 15,
|
||||
"crop_w": 70,
|
||||
"crop_h": 70,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
53
lagkage_layouts/layout_stickers.json
Normal file
53
lagkage_layouts/layout_stickers.json
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "vhs_noise_center_overlay",
|
||||
"filename": "../media/media.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 0,
|
||||
"pos_y": 0,
|
||||
"size": 110,
|
||||
"opacity": 0.3,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "circus_sticker",
|
||||
"filename": "../media/circus.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 220,
|
||||
"pos_y": 260,
|
||||
"size": 18,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "bunny_sticker",
|
||||
"filename": "../media/bunny.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 1460,
|
||||
"pos_y": 260,
|
||||
"size": 14,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "logo_floating",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 1500,
|
||||
"pos_y": 40,
|
||||
"size": 14,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
51
lagkage_layouts/layout_story_mode.json
Normal file
51
lagkage_layouts/layout_story_mode.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "lower_third_band",
|
||||
"filename": "../media/gross.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 200,
|
||||
"pos_y": 800,
|
||||
"size": 60,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "mask_right_mid",
|
||||
"filename": "../media/mask1.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 1200,
|
||||
"pos_y": 360,
|
||||
"size": 28,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "pip_corner_small",
|
||||
"filename": "../media/faith.mp4",
|
||||
"type": "video",
|
||||
"mode": "place",
|
||||
"place": "top_right",
|
||||
"size": 20,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "logo_top_left_small",
|
||||
"filename": "../media/logo.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_left",
|
||||
"size": 10,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
41
lagkage_layouts/layout_vhs_heavy.json
Normal file
41
lagkage_layouts/layout_vhs_heavy.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "vhs_noise_full",
|
||||
"filename": "../media/media.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 0,
|
||||
"pos_y": 0,
|
||||
"size": 120,
|
||||
"opacity": 0.5,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "mask_center",
|
||||
"filename": "../media/mask1.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 640,
|
||||
"pos_y": 260,
|
||||
"size": 40,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "logo_top_center",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 840,
|
||||
"pos_y": 40,
|
||||
"size": 18,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
28
lagkage_layouts/layout_zoomed_pip.json
Normal file
28
lagkage_layouts/layout_zoomed_pip.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "pip_zoomed_small",
|
||||
"filename": "../media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "place",
|
||||
"place": "top_right",
|
||||
"size": 22,
|
||||
"opacity": 1.0,
|
||||
"zoom": 1.8,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "logo_top_left",
|
||||
"filename": "../media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_left",
|
||||
"size": 14,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -3,13 +3,14 @@
|
||||
# Compose multiple visual layers (images/gifs/videos) on top of a base video
|
||||
# using a single JSON layout file.
|
||||
#
|
||||
# GIF layers are preprocessed into finite MP4s that loop for roughly the base
|
||||
# GIF layers are preprocessed into finite videos that loop for roughly the base
|
||||
# video duration, so the main overlay graph stays simple and stable.
|
||||
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import subprocess
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from videobeaux.utils.ffmpeg_operations import run_ffmpeg_with_progress
|
||||
@@ -17,155 +18,185 @@ from videobeaux.utils.ffmpeg_operations import run_ffmpeg_with_progress
|
||||
|
||||
def register_arguments(parser):
|
||||
parser.description = (
|
||||
"Compose multiple visual layers (images/gifs/videos) on a base video "
|
||||
"using a JSON layout file. All layers are sized & positioned relative "
|
||||
"to the base video dimensions."
|
||||
"Compose a base video with layered media defined by a JSON layout "
|
||||
"(images / videos / GIFs) using the lagkage compositor."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--layout-json",
|
||||
dest="layout_json",
|
||||
required=True,
|
||||
help="Path to JSON layout describing all layers."
|
||||
type=str,
|
||||
help="Path to layout JSON describing layers.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--sequence-direction",
|
||||
dest="sequence_direction",
|
||||
choices=["forward", "backward", "random"],
|
||||
help="Override sequence_direction in the JSON (optional)."
|
||||
default=None,
|
||||
help="Override sequence_direction from JSON (forward|backward|random).",
|
||||
)
|
||||
|
||||
|
||||
def _load_layout(path: Path):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
layout = json.load(f)
|
||||
if "layers" not in layout or not layout["layers"]:
|
||||
raise ValueError("Layout JSON must contain a non-empty 'layers' array.")
|
||||
return layout
|
||||
|
||||
|
||||
def _resolve_sequence(layout, override=None):
|
||||
seq = override or layout.get("sequence_direction", "forward")
|
||||
layers = layout["layers"]
|
||||
layers_sorted = sorted(layers, key=lambda L: L.get("layer_number", 0))
|
||||
|
||||
if seq == "forward":
|
||||
ordered = layers_sorted
|
||||
elif seq == "backward":
|
||||
ordered = list(reversed(layers_sorted))
|
||||
elif seq == "random":
|
||||
ordered = layers_sorted[:]
|
||||
random.shuffle(ordered)
|
||||
else:
|
||||
ordered = layers_sorted
|
||||
|
||||
return seq, ordered
|
||||
# ----------------- ffprobe helpers -----------------
|
||||
|
||||
|
||||
def _probe_base_info(path: str):
|
||||
"""Return (width, height, duration_seconds) for the base video."""
|
||||
"""
|
||||
Return (width, height, duration_seconds) for the base video.
|
||||
"""
|
||||
cmd = [
|
||||
"ffprobe",
|
||||
"-v", "error",
|
||||
"-select_streams", "v:0",
|
||||
"-show_entries", "stream=width,height",
|
||||
"-show_entries", "format=duration",
|
||||
"-of", "json",
|
||||
"-v",
|
||||
"error",
|
||||
"-select_streams",
|
||||
"v:0",
|
||||
"-show_entries",
|
||||
"stream=width,height",
|
||||
"-show_entries",
|
||||
"format=duration",
|
||||
"-of",
|
||||
"json",
|
||||
path,
|
||||
]
|
||||
proc = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if proc.returncode != 0:
|
||||
raise RuntimeError(
|
||||
f"ffprobe failed for {path} (code {proc.returncode}): {proc.stderr}"
|
||||
)
|
||||
|
||||
data = json.loads(proc.stdout)
|
||||
streams = data.get("streams") or []
|
||||
if not streams:
|
||||
raise RuntimeError(f"No video stream found in {path}")
|
||||
|
||||
s0 = streams[0]
|
||||
width = int(s0.get("width", 0) or 0)
|
||||
height = int(s0.get("height", 0) or 0)
|
||||
if width <= 0 or height <= 0:
|
||||
raise RuntimeError(f"Invalid video size from ffprobe for {path}: {width}x{height}")
|
||||
|
||||
fmt = data.get("format") or {}
|
||||
dur_str = fmt.get("duration") or "0"
|
||||
try:
|
||||
duration = float(dur_str)
|
||||
except ValueError:
|
||||
duration = 0.0
|
||||
|
||||
if duration <= 0:
|
||||
duration = 0.0
|
||||
raise RuntimeError(f"ffprobe failed for base {path}: {proc.stderr}")
|
||||
info = json.loads(proc.stdout)
|
||||
|
||||
width = int(info["streams"][0]["width"])
|
||||
height = int(info["streams"][0]["height"])
|
||||
duration = float(info["format"]["duration"])
|
||||
return width, height, duration
|
||||
|
||||
|
||||
def _place_expr(layer):
|
||||
"""Return (x, y) expressions for the overlay filter."""
|
||||
mode = layer.get("mode", "free")
|
||||
def _probe_video_size(path: str):
|
||||
"""
|
||||
Return (width, height) of the first video/image stream.
|
||||
"""
|
||||
cmd = [
|
||||
"ffprobe",
|
||||
"-v",
|
||||
"error",
|
||||
"-select_streams",
|
||||
"v:0",
|
||||
"-show_entries",
|
||||
"stream=width,height",
|
||||
"-of",
|
||||
"csv=s=x:p=0",
|
||||
path,
|
||||
]
|
||||
proc = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if proc.returncode != 0:
|
||||
raise RuntimeError(f"ffprobe size failed for {path}: {proc.stderr}")
|
||||
line = proc.stdout.strip()
|
||||
if not line:
|
||||
raise RuntimeError(f"ffprobe size returned empty output for {path}")
|
||||
w_str, h_str = line.split("x")
|
||||
return int(w_str), int(h_str)
|
||||
|
||||
if mode == "free":
|
||||
x = str(layer.get("pos_x", 0))
|
||||
y = str(layer.get("pos_y", 0))
|
||||
return x, y
|
||||
|
||||
slot = layer.get("place", "center")
|
||||
# ----------------- small helpers -----------------
|
||||
|
||||
if slot == "top_left":
|
||||
return "0", "0"
|
||||
if slot == "top_right":
|
||||
return "W-w", "0"
|
||||
if slot == "bottom_left":
|
||||
return "0", "H-h"
|
||||
if slot == "bottom_right":
|
||||
return "W-w", "H-h"
|
||||
|
||||
return "(W-w)/2", "(H-h)/2"
|
||||
def _even(x: int) -> int:
|
||||
"""
|
||||
Force an integer to be even (needed for some codecs / filters).
|
||||
"""
|
||||
return x if x % 2 == 0 else x + 1
|
||||
|
||||
|
||||
def _preprocess_gif(src: str, base_duration: float, tmp_dir: Path, idx: int) -> str:
|
||||
"""
|
||||
Transcode a GIF to a looping video with alpha that roughly matches
|
||||
the base duration.
|
||||
Convert GIF to a looping RGBA video with even dimensions and alpha.
|
||||
Returns path to the temp video file.
|
||||
|
||||
IMPORTANT:
|
||||
- We force even dimensions so filters/encoders don't choke.
|
||||
- We use an alpha-capable codec (qtrle in a MOV container),
|
||||
so transparency is preserved instead of being flattened.
|
||||
- Loops the GIF for approximately base_duration.
|
||||
- Forces even width/height.
|
||||
- Keeps alpha via format=rgba + qtrle.
|
||||
"""
|
||||
tmp_dir.mkdir(parents=True, exist_ok=True)
|
||||
# Use .mov since qtrle is typically stored in a QuickTime container
|
||||
temp_path = tmp_dir / f"lagkage_gif_{idx}.mov"
|
||||
out_path = tmp_dir / f"gif_pre_{idx:03d}.mov"
|
||||
|
||||
vf = "scale=trunc(iw/2)*2:trunc(ih/2)*2,format=rgba"
|
||||
|
||||
cmd = [
|
||||
"ffmpeg",
|
||||
"-hide_banner",
|
||||
"-loglevel", "error",
|
||||
"-ignore_loop", "0",
|
||||
"-stream_loop", "-1",
|
||||
"-i", src,
|
||||
# Make width/height even and ensure we have RGBA (with alpha)
|
||||
"-vf", "scale=trunc(iw/2)*2:trunc(ih/2)*2,format=rgba",
|
||||
]
|
||||
|
||||
# If we know the base duration, trim to that; otherwise just loop and
|
||||
# let the main overlay graph's base video duration cap play-out.
|
||||
if base_duration > 0:
|
||||
cmd += ["-t", f"{base_duration:.3f}"]
|
||||
|
||||
cmd += [
|
||||
# Alpha-capable codec
|
||||
"-c:v", "qtrle",
|
||||
"-an",
|
||||
"-loglevel",
|
||||
"error",
|
||||
"-y",
|
||||
str(temp_path),
|
||||
"-ignore_loop",
|
||||
"0",
|
||||
"-stream_loop",
|
||||
"-1",
|
||||
"-i",
|
||||
src,
|
||||
"-t",
|
||||
f"{base_duration:.3f}",
|
||||
"-vf",
|
||||
vf,
|
||||
"-c:v",
|
||||
"qtrle",
|
||||
"-an",
|
||||
str(out_path),
|
||||
]
|
||||
|
||||
proc = subprocess.run(cmd)
|
||||
if proc.returncode != 0:
|
||||
raise RuntimeError(f"GIF preprocess failed for {src} (code {proc.returncode})")
|
||||
|
||||
return str(temp_path)
|
||||
return str(out_path)
|
||||
|
||||
|
||||
def _compute_place_coordinates(place: str, base_w: int, base_h: int,
|
||||
box_w: int, box_h: int):
|
||||
"""
|
||||
Compute overlay x,y for place mode, given base and box dimensions.
|
||||
"""
|
||||
place = (place or "center").lower()
|
||||
if place == "top_left":
|
||||
return 0, 0
|
||||
elif place == "top_right":
|
||||
return base_w - box_w, 0
|
||||
elif place == "bottom_left":
|
||||
return 0, base_h - box_h
|
||||
elif place == "bottom_right":
|
||||
return base_w - box_w, base_h - box_h
|
||||
elif place == "center":
|
||||
return (base_w - box_w) // 2, (base_h - box_h) // 2
|
||||
else:
|
||||
# Fallback to center for unknown place values
|
||||
return (base_w - box_w) // 2, (base_h - box_h) // 2
|
||||
|
||||
|
||||
def _compute_overlay_box(base_w: int, src_w: int, src_h: int, size_pct: float):
|
||||
"""
|
||||
Compute the final overlay box width/height (even integers) based on:
|
||||
- base width
|
||||
- layer source aspect ratio
|
||||
- size percentage of base width.
|
||||
"""
|
||||
if size_pct <= 0:
|
||||
size_pct = 1.0
|
||||
|
||||
target_w = int(base_w * (size_pct / 100.0))
|
||||
if target_w < 2:
|
||||
target_w = 2
|
||||
target_w = _even(target_w)
|
||||
|
||||
if src_h > 0:
|
||||
ar = src_w / src_h
|
||||
target_h = int(target_w / ar) if ar > 0 else target_w
|
||||
else:
|
||||
target_h = target_w
|
||||
|
||||
if target_h < 2:
|
||||
target_h = 2
|
||||
target_h = _even(target_h)
|
||||
|
||||
return target_w, target_h
|
||||
|
||||
|
||||
# ----------------- main entry -----------------
|
||||
|
||||
|
||||
def run(args):
|
||||
@@ -173,78 +204,147 @@ def run(args):
|
||||
if not layout_path.exists():
|
||||
raise FileNotFoundError(f"Layout JSON not found: {layout_path}")
|
||||
|
||||
layout = _load_layout(layout_path)
|
||||
seq, ordered_layers = _resolve_sequence(layout, args.sequence_direction)
|
||||
# Load layout JSON
|
||||
layout = json.loads(layout_path.read_text())
|
||||
layers = layout.get("layers", [])
|
||||
if not isinstance(layers, list) or not layers:
|
||||
raise ValueError("Layout JSON has no 'layers' array or it's empty.")
|
||||
|
||||
# Determine sequence direction
|
||||
seq_dir = args.sequence_direction or layout.get("sequence_direction", "forward")
|
||||
if seq_dir not in ("forward", "backward", "random"):
|
||||
seq_dir = "forward"
|
||||
|
||||
# Order layers by layer_number, then adjust sequence if needed
|
||||
ordered_layers = sorted(layers, key=lambda L: L.get("layer_number", 0))
|
||||
if seq_dir == "backward":
|
||||
ordered_layers = list(reversed(ordered_layers))
|
||||
elif seq_dir == "random":
|
||||
ordered_layers = random.sample(ordered_layers, len(ordered_layers))
|
||||
|
||||
base_input = args.input
|
||||
if not base_input:
|
||||
raise ValueError("Global --input (base video) is required for json_layers.")
|
||||
raise ValueError("Global --input (base video) is required for lagkage.")
|
||||
|
||||
# 1) Probe base video info once
|
||||
# Probe base
|
||||
base_w, base_h, base_duration = _probe_base_info(base_input)
|
||||
|
||||
# 2) Prepare inputs for main ffmpeg call
|
||||
# index 0: base video
|
||||
# index 1..N: layer sources (with GIFs preprocessed to MP4)
|
||||
inputs_for_ffmpeg = [("base", base_input)]
|
||||
layer_inputs = [] # (layer_dict, input_index, target_width_px)
|
||||
# Temp dir for GIF preprocess
|
||||
tmp_dir = Path(tempfile.mkdtemp(prefix="lagkage_gifs_"))
|
||||
|
||||
# Folder to hold temp GIF->MP4 files (next to the output file)
|
||||
tmp_dir = Path(args.output).with_suffix("")
|
||||
# 1) Build input list for ffmpeg
|
||||
# input 0 = base, 1..N = overlays
|
||||
input_files = [base_input]
|
||||
overlay_specs = [] # (input_index, layer_dict, src_w, src_h)
|
||||
|
||||
for idx, layer in enumerate(ordered_layers, start=1):
|
||||
filename = layer.get("filename")
|
||||
if not filename:
|
||||
raise ValueError(f"Layer missing 'filename': {layer}")
|
||||
|
||||
# Resolve relative to layout JSON file
|
||||
# Relative paths are relative to the layout JSON directory.
|
||||
if not os.path.isabs(filename):
|
||||
src = str(layout_path.parent / filename)
|
||||
else:
|
||||
src = filename
|
||||
|
||||
size_pct = float(layer.get("size", 100)) / 100.0
|
||||
target_w = max(1, int(base_w * size_pct))
|
||||
|
||||
layer_type = (layer.get("type") or "").lower()
|
||||
|
||||
# If GIF, pre-process to finite-length MP4 that loops to base duration
|
||||
# If GIF, pre-process to finite-length video that loops to base duration
|
||||
if layer_type == "gif":
|
||||
overlay_src = _preprocess_gif(src, base_duration, tmp_dir, idx)
|
||||
else:
|
||||
overlay_src = src
|
||||
|
||||
inputs_for_ffmpeg.append(("video", overlay_src))
|
||||
layer_inputs.append((layer, len(inputs_for_ffmpeg) - 1, target_w))
|
||||
src_w, src_h = _probe_video_size(overlay_src)
|
||||
|
||||
# 3) Build filter_complex
|
||||
input_files.append(overlay_src)
|
||||
in_index = len(input_files) - 1
|
||||
overlay_specs.append((in_index, layer, src_w, src_h))
|
||||
|
||||
# 2) Build filter_complex
|
||||
filter_parts = []
|
||||
|
||||
# Base video label is [0:v] directly (like overlay_img_pro)
|
||||
# Start with base video
|
||||
current_label = "[0:v]"
|
||||
|
||||
for idx, (layer, input_index, target_w) in enumerate(layer_inputs, start=1):
|
||||
lay_in = f"[{input_index}:v]"
|
||||
lay_alpha = f"[lay{idx}]"
|
||||
for idx, (in_index, layer, src_w, src_h) in enumerate(overlay_specs, start=1):
|
||||
size_pct = float(layer.get("size", 100.0))
|
||||
opacity = float(layer.get("opacity", 1.0))
|
||||
if opacity < 0.0:
|
||||
opacity = 0.0
|
||||
if opacity > 1.0:
|
||||
opacity = 1.0
|
||||
|
||||
zoom = float(layer.get("zoom", 1.0))
|
||||
if zoom < 1.0:
|
||||
zoom = 1.0
|
||||
|
||||
# Compute final overlay box size on the base
|
||||
box_w, box_h = _compute_overlay_box(base_w, src_w, src_h, size_pct)
|
||||
|
||||
mode = (layer.get("mode") or "place").lower()
|
||||
|
||||
if mode == "free":
|
||||
x_expr = str(int(layer.get("pos_x", 0)))
|
||||
y_expr = str(int(layer.get("pos_y", 0)))
|
||||
else:
|
||||
place = layer.get("place", "center")
|
||||
px, py = _compute_place_coordinates(place, base_w, base_h, box_w, box_h)
|
||||
x_expr, y_expr = str(px), str(py)
|
||||
|
||||
in_label = f"[{in_index}:v]"
|
||||
layer_label = f"[lay{idx}]"
|
||||
next_label = f"[tmp{idx}]"
|
||||
|
||||
opacity = float(layer.get("opacity", 1.0))
|
||||
# blend_mode is kept for future use but ignored here
|
||||
_blend_mode = (layer.get("blend_mode") or "normal").lower()
|
||||
layer_filters = []
|
||||
|
||||
x_expr, y_expr = _place_expr(layer)
|
||||
# Optional crop (percent of source, BEFORE zoom/scale)
|
||||
cx = layer.get("crop_x")
|
||||
cy = layer.get("crop_y")
|
||||
cw = layer.get("crop_w")
|
||||
ch = layer.get("crop_h")
|
||||
if cx is not None and cy is not None and cw is not None and ch is not None:
|
||||
try:
|
||||
cx_f = float(cx) / 100.0
|
||||
cy_f = float(cy) / 100.0
|
||||
cw_f = float(cw) / 100.0
|
||||
ch_f = float(ch) / 100.0
|
||||
layer_filters.append(
|
||||
f"crop=in_w*{cw_f}:in_h*{ch_f}:in_w*{cx_f}:in_h*{cy_f}"
|
||||
)
|
||||
except Exception:
|
||||
# If parsing fails, skip cropping instead of blowing up.
|
||||
pass
|
||||
|
||||
# scale + alpha
|
||||
filter_parts.append(
|
||||
f"{lay_in}"
|
||||
f"scale={target_w}:-1,"
|
||||
f"format=rgba,colorchannelmixer=aa={opacity}"
|
||||
f"{lay_alpha}"
|
||||
# Zoom INSIDE fixed box:
|
||||
# 1) scale up to zoom * box size
|
||||
# 2) crop center back down to box_w x box_h
|
||||
scaled_w = _even(int(box_w * zoom))
|
||||
scaled_h = _even(int(box_h * zoom))
|
||||
if scaled_w < 2:
|
||||
scaled_w = 2
|
||||
if scaled_h < 2:
|
||||
scaled_h = 2
|
||||
|
||||
layer_filters.append(f"scale={scaled_w}:{scaled_h}")
|
||||
|
||||
if zoom > 1.0:
|
||||
layer_filters.append(
|
||||
f"crop={box_w}:{box_h}:(iw-{box_w})/2:(ih-{box_h})/2"
|
||||
)
|
||||
|
||||
# overlay
|
||||
# Force RGBA and apply opacity
|
||||
layer_filters.append("format=rgba")
|
||||
layer_filters.append(f"colorchannelmixer=aa={opacity}")
|
||||
|
||||
filter_parts.append(
|
||||
f"{current_label}{lay_alpha}"
|
||||
f"{in_label}{','.join(layer_filters)}{layer_label}"
|
||||
)
|
||||
|
||||
# Overlay on top of current composite
|
||||
filter_parts.append(
|
||||
f"{current_label}{layer_label}"
|
||||
f"overlay=x={x_expr}:y={y_expr}:format=auto"
|
||||
f"{next_label}"
|
||||
)
|
||||
@@ -256,20 +356,16 @@ def run(args):
|
||||
|
||||
filter_complex = ";".join(filter_parts)
|
||||
|
||||
# 4) Build main ffmpeg command
|
||||
command = [
|
||||
"ffmpeg",
|
||||
"-err_detect", "ignore_err",
|
||||
"-fflags", "+discardcorrupt+genpts",
|
||||
]
|
||||
# 3) Build main ffmpeg command
|
||||
command = ["ffmpeg", "-hide_banner", "-loglevel", "error"]
|
||||
|
||||
for _typ, src in inputs_for_ffmpeg:
|
||||
command.extend(["-i", src])
|
||||
for path in input_files:
|
||||
command.extend(["-i", path])
|
||||
|
||||
command.extend([
|
||||
"-filter_complex", filter_complex,
|
||||
"-map", out_label,
|
||||
"-map", "0:a",
|
||||
"-map", "0:a?",
|
||||
"-c:v", "libx264",
|
||||
"-profile:v", "high",
|
||||
"-level:v", "4.2",
|
||||
@@ -280,5 +376,4 @@ def run(args):
|
||||
])
|
||||
|
||||
final_cmd = (command[:1] + ["-y"] + command[1:]) if args.force else command
|
||||
|
||||
run_ffmpeg_with_progress(final_cmd, args.input, args.output)
|
||||
|
||||
Reference in New Issue
Block a user