mirror of
https://github.com/vondas-network/videobeaux.git
synced 2025-12-05 15:30:02 +01:00
added new feature, lagkage
This commit is contained in:
1
css/style.css
Normal file
1
css/style.css
Normal file
File diff suppressed because one or more lines are too long
1
css/style.min.css
vendored
Normal file
1
css/style.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
167
experimental/wipe2.sh
Normal file
167
experimental/wipe2.sh
Normal file
@@ -0,0 +1,167 @@
|
||||
#!/bin/bash
|
||||
# Combined ffmpeg -y commands for all static video wipes with 3-second delay between each
|
||||
|
||||
# Initial list wipes
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=wipeleft:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y horizontal_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=wipeup:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y vertical_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=diagbr:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y diagonal_topleft_bottomright_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=diagbl:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y diagonal_topright_bottomleft_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=circleopen:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y circular_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=rectcrop:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y square_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i diamond_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y diamond_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i star_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y star_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i cross_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y cross_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=pixelize:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y checkerboard_wipe.mp4
|
||||
sleep 3
|
||||
|
||||
# Expanded list wipes
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=fade:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y full_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=circleopen:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y circle_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=circleopen:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y iris_circle_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=circleopen:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y ellipse_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=clock:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y clock_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=circleopen:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y reveal_circle_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i triangle_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y triangle_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i triangle_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y reveal_triangle_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=wipeleft:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y half_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=rectcrop:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y iris_square_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=rectcrop:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y rectangle_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=rectcrop:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y reveal_square_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i star_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y reveal_star_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i heart_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y heart_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i pentagon_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y pentagon_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i hexagon_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y hexagon_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i octagon_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y octagon_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i parallelogram_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y parallelogram_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i chevron_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y chevron_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=diagbr:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y diagonal_linear_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=diagbl:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y split_diagonal_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=radial:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y angular_shutter_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i zigzag_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y zigzag_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i sawtooth_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y sawtooth_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=pixelize:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y grid_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i honeycomb_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y honeycomb_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i starfield_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y starfield_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=pixelize:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y mosaic_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=hlr:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y barn_door_h_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=vud:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y barn_door_v_w
|
||||
|
||||
ipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i four_panel_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y four_panel_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=hblur:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y blinds_h_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=vblur:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y blinds_v_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=radial:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y fan_blades_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=clock:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y clock_hands_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=circleopen:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y curved_arc_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i page_curl_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y page_curl_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i flipboard_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y flipboard_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=hblur:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y louver_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i sliding_polygon_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y sliding_polygon_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i fractal_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y fractal_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=pixelize:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y fade_grid_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i cube_spin_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y cube_spin_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i card_flip_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y card_flip_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i polygon_tunnel_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y polygon_tunnel_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i prism_fold_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y prism_fold_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i svg_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y svg_mask_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i text_reveal_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y text_reveal_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i logo_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y logo_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=rectcrop:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y letterboxed_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=radial:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y angular_burst_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i polygon_scatter_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y polygon_scatter_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i blade_slash_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y blade_slash_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=radial:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y rotating_window_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=radial:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y radial_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=wipeleft:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y bar_h_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=wipeup:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y bar_v_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=hblur:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y venetian_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=vblur:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y window_blind_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=hlr:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y door_h_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=vud:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y door_v_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i page_peel_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y page_peel_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=zoom:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y zoom_in_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=zoom:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y zoom_out_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=wipeleft:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y push_left_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=wiperight:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y push_right_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=wipeup:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y push_up_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=wipedown:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y push_down_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=radial:duration=1.0:offset=5,format=yuv420p" -c:v libx264 -y spiral_wipe.mp4
|
||||
sleep 3
|
||||
ffmpeg -y -i 0.mp4 -i 1.mp4 -i wave_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y wave_wipe.mp4
|
||||
34
experimental/wipercheck.sh
Normal file
34
experimental/wipercheck.sh
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
ffmpeg -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=wipeleft:duration=1:offset=5,format=yuv420p" -c:v libx264 -y horizontal_wipe.mp4
|
||||
sleep 2
|
||||
ffmpeg -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=wipeup:duration=1:offset=5,format=yuv420p" -c:v libx264 -y vertical_wipe.mp4
|
||||
sleep 2
|
||||
ffmpeg -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=diagbr:duration=1:offset=5,format=yuv420p" -c:v libx264 -y diagonal_topleft_bottomright_wipe.mp4
|
||||
sleep 2
|
||||
#!/bin/bash
|
||||
ffmpeg -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=diagbl:duration=1:offset=5,format=yuv420p" -c:v libx264 -y diagonal_topright_bottomleft_wipe.mp4
|
||||
sleep 2
|
||||
#!/bin/bash
|
||||
ffmpeg -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=circleopen:duration=1:offset=5,format=yuv420p" -c:v libx264 -y circular_wipe.mp4
|
||||
sleep 2
|
||||
#!/bin/bash
|
||||
ffmpeg -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=rectcrop:duration=1:offset=5,format=yuv420p" -c:v libx264 -y square_wipe.mp4
|
||||
sleep 2
|
||||
#!/bin/bash
|
||||
# Note: FFmpeg's xfade does not have a direct diamond wipe. Approximating with custom mask.
|
||||
# Create a diamond-shaped mask image (white diamond on black background) named diamond_mask.png
|
||||
ffmpeg -i 0.mp4 -i 1.mp4 -i diamond_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y diamond_wipe.mp4
|
||||
sleep 2
|
||||
#!/bin/bash
|
||||
# Note: FFmpeg's xfade does not support star wipe. Requires custom star-shaped mask.
|
||||
# Create a star-shaped mask image (white star on black background) named star_mask.png
|
||||
ffmpeg -i 0.mp4 -i 1.mp4 -i star_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y star_wipe.mp4
|
||||
sleep 2
|
||||
#!/bin/bash
|
||||
# Note: FFmpeg's xfade does not support cross wipe. Approximating with custom mask.
|
||||
# Create a cross-shaped mask image (white cross on black background) named cross_mask.png
|
||||
ffmpeg -i 0.mp4 -i 1.mp4 -i cross_mask.png -filter_complex "[1:v][2:v]alphamerge[fg];[0:v][fg]overlay=0:0,format=yuv420p" -c:v libx264 -y cross_wipe.mp4
|
||||
sleep 2
|
||||
#!/bin/bash
|
||||
# Note: FFmpeg's xfade does not support checkerboard wipe. Approximating with pixelize filter.
|
||||
ffmpeg -i 0.mp4 -i 1.mp4 -filter_complex "[0:v][1:v]xfade=transition=pixelize:duration=1:offset=5,format=yuv420p" -c:v libx264 -y checkerboard_wipe.mp4
|
||||
201
index.html
Normal file
201
index.html
Normal file
@@ -0,0 +1,201 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>MySoftware - The Missing Package Manager</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 1.6;
|
||||
color: #2f2f2f;
|
||||
background-color: #f5f5f5;
|
||||
text-align: center;
|
||||
}
|
||||
.container {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
header {
|
||||
padding: 80px 0;
|
||||
background-color: #fff;
|
||||
}
|
||||
.logo {
|
||||
max-width: 120px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 48px;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
}
|
||||
.tagline {
|
||||
font-size: 24px;
|
||||
color: #595959;
|
||||
margin: 16px 0 0;
|
||||
}
|
||||
section {
|
||||
padding: 80px 0;
|
||||
background-color: #fff;
|
||||
}
|
||||
h2 {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 24px;
|
||||
color: #000;
|
||||
}
|
||||
.two-column {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 40px;
|
||||
align-items: center;
|
||||
}
|
||||
.column-left {
|
||||
flex: 1;
|
||||
text-align: left;
|
||||
max-width: 50%;
|
||||
}
|
||||
.column-right {
|
||||
flex: 1;
|
||||
max-width: 50%;
|
||||
position: relative;
|
||||
}
|
||||
.column-left p {
|
||||
font-size: 18px;
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
line{
|
||||
color:black;
|
||||
}
|
||||
p {
|
||||
font-size: 18px;
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
.install-code {
|
||||
background: #2f2f2f;
|
||||
color: #fff;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
position: relative;
|
||||
}
|
||||
.copy-button {
|
||||
position: absolute;
|
||||
გეძებულია: top: 8px;
|
||||
right: 8px;
|
||||
background: #f36d21;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
.install-code:hover .copy-button {
|
||||
opacity: 1;
|
||||
}
|
||||
.copy-button:hover {
|
||||
background: #d15e1c;
|
||||
}
|
||||
a {
|
||||
color: #f36d21;
|
||||
text-decoration: underline;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
a:hover {
|
||||
color: #d15e1c;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.two-column {
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
}
|
||||
.column-left,
|
||||
.column-right {
|
||||
max-width: 100%;
|
||||
}
|
||||
h1 {
|
||||
font-size: 36px;
|
||||
}
|
||||
.tagline {
|
||||
font-size: 20px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 28px;
|
||||
}
|
||||
p, .column-left p {
|
||||
font-size: 16px;
|
||||
}
|
||||
.install-code {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="container">
|
||||
<img src="https://via.placeholder.com/120" alt="MySoftware Logo" class="logo">
|
||||
<h1>MySoftware</h1>
|
||||
<p class="tagline">The Missing Package Manager for Your System</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section id="install">
|
||||
<div class="container">
|
||||
<h2>Installation</h2>
|
||||
<p>Paste that in a macOS Terminal or Linux shell prompt.</p>
|
||||
<pre class="install-code">/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/MySoftware/install/main/install.sh)"<button class="copy-button" onclick="copyCode(this)">Copy</button></pre>
|
||||
<p>The script explains what it will do and then pauses before it does it. <a href="https://docs.mysoftware.com/Installation">Read about other installation options.</a></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="what-does">
|
||||
<div class="container">
|
||||
<h2>What Does videobeaux Do?</h2>
|
||||
<div class="two-column">
|
||||
<div class="column-left">
|
||||
<p>MySoftware installs the stuff you need that your system didn’t.</p>
|
||||
</div>
|
||||
<div class="column-right">
|
||||
<pre class="install-code">mysoftware install package<button class="copy-button" onclick="copyCode(this)">Copy</button></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="two-column">
|
||||
<div class="column-left">
|
||||
<p>MySoftware installs the stuff you need that your system didn’t.</p>
|
||||
</div>
|
||||
<div class="column-right">
|
||||
<pre class="install-code">mysoftware install package<button class="copy-button" onclick="copyCode(this)">Copy</button></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<script>
|
||||
function copyCode(button) {
|
||||
const code = button.parentElement.textContent.replace('Copy', '').trim();
|
||||
navigator.clipboard.writeText(code).then(() => {
|
||||
button.textContent = 'Copied!';
|
||||
setTimeout(() => {
|
||||
button.textContent = 'Copy';
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
39
lagkage_layouts/layout.json
Normal file
39
lagkage_layouts/layout.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "logo",
|
||||
"filename": "media/mask8.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_right",
|
||||
"size": 20,
|
||||
"opacity": 0.85,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "corner_camera",
|
||||
"filename": "media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 120,
|
||||
"pos_y": 200,
|
||||
"size": 35,
|
||||
"opacity": 0.7,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "animated_sticker",
|
||||
"filename": "media/media.gif",
|
||||
"type": "gif",
|
||||
"mode": "place",
|
||||
"place": "bottom_left",
|
||||
"size": 15,
|
||||
"opacity": 0.6,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
108
lagkage_layouts/layout_big.json
Normal file
108
lagkage_layouts/layout_big.json
Normal file
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "top_right_logo",
|
||||
"filename": "media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_right",
|
||||
"size": 18,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "top_left_bug",
|
||||
"filename": "media/screen.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_left",
|
||||
"size": 10,
|
||||
"opacity": 0.8,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "pip_camera_a",
|
||||
"filename": "media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 80,
|
||||
"pos_y": 80,
|
||||
"size": 28,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "pip_camera_b",
|
||||
"filename": "media/faith.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 80,
|
||||
"pos_y": 420,
|
||||
"size": 28,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 5,
|
||||
"name": "right_side_vertical_bar",
|
||||
"filename": "media/mask1.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "center_right",
|
||||
"size": 12,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 6,
|
||||
"name": "lower_third_plate",
|
||||
"filename": "media/gross.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "bottom_center",
|
||||
"size": 60,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 7,
|
||||
"name": "looping_sticker_left",
|
||||
"filename": "media/circus.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 120,
|
||||
"pos_y": 260,
|
||||
"size": 14,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 8,
|
||||
"name": "looping_sticker_right",
|
||||
"filename": "media/bunny.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 1520,
|
||||
"pos_y": 260,
|
||||
"size": 14,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 9,
|
||||
"name": "full_frame_vhs_noise",
|
||||
"filename": "media/media.gif",
|
||||
"type": "gif",
|
||||
"mode": "place",
|
||||
"place": "center",
|
||||
"size": 100,
|
||||
"opacity": 0.35,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
112
lagkage_layouts/layout_big_2.json
Normal file
112
lagkage_layouts/layout_big_2.json
Normal file
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "top_right_logo_floating",
|
||||
"filename": "media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 1430,
|
||||
"pos_y": 70,
|
||||
"size": 14,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "offset_corner_bug",
|
||||
"filename": "media/screen.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 90,
|
||||
"pos_y": 40,
|
||||
"size": 12,
|
||||
"opacity": 0.8,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "pip_camera_a_small_top",
|
||||
"filename": "media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 260,
|
||||
"pos_y": 120,
|
||||
"size": 22,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "pip_camera_b_large_bottom",
|
||||
"filename": "media/faith.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 1080,
|
||||
"pos_y": 530,
|
||||
"size": 36,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 5,
|
||||
"name": "angled_mask_mid_left",
|
||||
"filename": "media/mask1.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 160,
|
||||
"pos_y": 360,
|
||||
"size": 26,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 6,
|
||||
"name": "lower_third_plate_offset",
|
||||
"filename": "media/gross.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 260,
|
||||
"pos_y": 780,
|
||||
"size": 54,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 7,
|
||||
"name": "looping_sticker_left_mid",
|
||||
"filename": "media/circus.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 220,
|
||||
"pos_y": 260,
|
||||
"size": 18,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 8,
|
||||
"name": "looping_sticker_right_high",
|
||||
"filename": "media/bunny.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 1380,
|
||||
"pos_y": 180,
|
||||
"size": 11,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 9,
|
||||
"name": "full_frame_vhs_noise_soft",
|
||||
"filename": "media/media.gif",
|
||||
"type": "gif",
|
||||
"mode": "place",
|
||||
"place": "center",
|
||||
"size": 115,
|
||||
"opacity": 0.3,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
111
lagkage_layouts/layout_big_3.json
Normal file
111
lagkage_layouts/layout_big_3.json
Normal file
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"sequence_direction": "forward",
|
||||
"layers": [
|
||||
{
|
||||
"layer_number": 1,
|
||||
"name": "logo_top_right",
|
||||
"filename": "media/reem.png",
|
||||
"type": "img",
|
||||
"mode": "place",
|
||||
"place": "top_right",
|
||||
"size": 16,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 2,
|
||||
"name": "screen_near_top_center",
|
||||
"filename": "media/screen.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 420,
|
||||
"pos_y": 60,
|
||||
"size": 12,
|
||||
"opacity": 0.8,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "pip_camera_a_left_mid",
|
||||
"filename": "media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 120,
|
||||
"pos_y": 160,
|
||||
"size": 24,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "pip_camera_b_right_lower",
|
||||
"filename": "media/faith.mp4",
|
||||
"type": "video",
|
||||
"mode": "free",
|
||||
"pos_x": 1120,
|
||||
"pos_y": 520,
|
||||
"size": 32,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 5,
|
||||
"name": "mask_left_edge_half_off",
|
||||
"filename": "media/mask1.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": -80,
|
||||
"pos_y": 340,
|
||||
"size": 22,
|
||||
"opacity": 0.9,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 6,
|
||||
"name": "gross_lower_band",
|
||||
"filename": "media/gross.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 260,
|
||||
"pos_y": 800,
|
||||
"size": 52,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 7,
|
||||
"name": "circus_sticker_top_half_off",
|
||||
"filename": "media/circus.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 220,
|
||||
"pos_y": -40,
|
||||
"size": 16,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 8,
|
||||
"name": "bunny_sticker_right_mid",
|
||||
"filename": "media/bunny.gif",
|
||||
"type": "gif",
|
||||
"mode": "free",
|
||||
"pos_x": 1460,
|
||||
"pos_y": 260,
|
||||
"size": 12,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 9,
|
||||
"name": "full_frame_vhs_noise",
|
||||
"filename": "media/media.gif",
|
||||
"type": "gif",
|
||||
"mode": "place",
|
||||
"place": "center",
|
||||
"size": 110,
|
||||
"opacity": 0.3,
|
||||
"blend_mode": "normal"
|
||||
}
|
||||
]
|
||||
}
|
||||
111
lagkage_layouts/layout_big_4.json
Normal file
111
lagkage_layouts/layout_big_4.json
Normal file
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"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": 2,
|
||||
"name": "screen_top_left_free",
|
||||
"filename": "../media/screen.png",
|
||||
"type": "img",
|
||||
"mode": "free",
|
||||
"pos_x": 10,
|
||||
"pos_y": 210,
|
||||
"size": 50,
|
||||
"opacity": 0.8,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 3,
|
||||
"name": "pip_camera_a_left_mid_free",
|
||||
"filename": "../media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "place",
|
||||
"place": "top_right",
|
||||
"size": 40,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "pip_camera_b_right_low_free",
|
||||
"filename": "../media/faith.mp4",
|
||||
"type": "video",
|
||||
"mode": "place",
|
||||
"place": "top_left",
|
||||
"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": 450,
|
||||
"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": 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
87
lagkage_layouts/layout_big_5.json
Normal file
87
lagkage_layouts/layout_big_5.json
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"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": "pip_camera_a_left_mid_free",
|
||||
"filename": "../media/schwwaaa.mp4",
|
||||
"type": "video",
|
||||
"mode": "place",
|
||||
"place": "top_right",
|
||||
"size": 40,
|
||||
"opacity": 1.0,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 4,
|
||||
"name": "pip_camera_b_right_low_free",
|
||||
"filename": "../media/faith.mp4",
|
||||
"type": "video",
|
||||
"mode": "place",
|
||||
"place": "top_left",
|
||||
"size": 34,
|
||||
"opacity": 0.95,
|
||||
"blend_mode": "normal"
|
||||
},
|
||||
{
|
||||
"layer_number": 6,
|
||||
"name": "gross_lower_band_free",
|
||||
"filename": "../media/gross.png",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
media/audio.wav
Normal file
BIN
media/audio.wav
Normal file
Binary file not shown.
BIN
media/bunny.gif
Normal file
BIN
media/bunny.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 334 KiB |
BIN
media/circus.gif
Normal file
BIN
media/circus.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 179 KiB |
BIN
media/gross.png
Normal file
BIN
media/gross.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 MiB |
BIN
media/menino.mp4
BIN
media/menino.mp4
Binary file not shown.
BIN
media/reem.png
Normal file
BIN
media/reem.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 432 KiB |
BIN
media/screen.png
Normal file
BIN
media/screen.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 MiB |
1
transcription-example.json
Normal file
1
transcription-example.json
Normal file
@@ -0,0 +1 @@
|
||||
[{"content": "in the new", "start": 0.002077, "end": 10.096557, "words": [{"conf": 0.793502, "end": 3.831176, "start": 0.002077, "word": "in"}, {"conf": 1.0, "end": 9.721856, "start": 3.831176, "word": "the"}, {"conf": 0.257509, "end": 10.096557, "start": 9.899999, "word": "new"}]}, {"content": "receive a very special night of the what", "start": 10.593582, "end": 13.62, "words": [{"conf": 0.880698, "end": 10.98, "start": 10.593582, "word": "receive"}, {"conf": 0.908101, "end": 11.069593, "start": 10.980199, "word": "a"}, {"conf": 0.994212, "end": 11.34, "start": 11.069593, "word": "very"}, {"conf": 0.836042, "end": 11.819474, "start": 11.34, "word": "special"}, {"conf": 0.890157, "end": 12.539867, "start": 12.3, "word": "night"}, {"conf": 0.800731, "end": 12.627985, "start": 12.54, "word": "of"}, {"conf": 0.981462, "end": 12.87, "start": 12.627985, "word": "the"}, {"conf": 0.985449, "end": 13.62, "start": 13.47, "word": "what"}]}, {"content": "part of the came from avellino in the", "start": 13.62, "end": 16.979918, "words": [{"conf": 0.999751, "end": 13.859696, "start": 13.62, "word": "part"}, {"conf": 0.999751, "end": 13.919696, "start": 13.859696, "word": "of"}, {"conf": 0.985837, "end": 15.799475, "start": 14.543549, "word": "the"}, {"conf": 0.451724, "end": 16.014001, "start": 15.84, "word": "came"}, {"conf": 0.999723, "end": 16.14, "start": 16.014001, "word": "from"}, {"conf": 0.994632, "end": 16.71, "start": 16.14, "word": "avellino"}, {"conf": 0.421944, "end": 16.918451, "start": 16.831659, "word": "in"}, {"conf": 0.927838, "end": 16.979918, "start": 16.918451, "word": "the"}]}, {"content": "village o'clock them not which the", "start": 16.980042, "end": 25.115975, "words": [{"conf": 0.999343, "end": 17.28, "start": 16.980042, "word": "village"}, {"conf": 0.518104, "end": 17.578077, "start": 17.28, "word": "o'clock"}, {"conf": 0.531982, "end": 17.7, "start": 17.581578, "word": "them"}, {"conf": 0.357179, "end": 18.15, "start": 17.76, "word": "not"}, {"conf": 0.427486, "end": 18.57, "start": 18.36, "word": "which"}, {"conf": 0.99724, "end": 25.115975, "start": 18.718674, "word": "the"}]}]
|
||||
284
videobeaux/programs/lagkage.py
Normal file
284
videobeaux/programs/lagkage.py
Normal file
@@ -0,0 +1,284 @@
|
||||
# videobeaux/programs/lagkage.py
|
||||
#
|
||||
# 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
|
||||
# video duration, so the main overlay graph stays simple and stable.
|
||||
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
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."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--layout-json",
|
||||
required=True,
|
||||
help="Path to JSON layout describing all layers."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--sequence-direction",
|
||||
choices=["forward", "backward", "random"],
|
||||
help="Override sequence_direction in the JSON (optional)."
|
||||
)
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def _probe_base_info(path: str):
|
||||
"""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",
|
||||
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
|
||||
|
||||
return width, height, duration
|
||||
|
||||
|
||||
def _place_expr(layer):
|
||||
"""Return (x, y) expressions for the overlay filter."""
|
||||
mode = layer.get("mode", "free")
|
||||
|
||||
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")
|
||||
|
||||
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 _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.
|
||||
|
||||
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.
|
||||
"""
|
||||
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"
|
||||
|
||||
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",
|
||||
"-y",
|
||||
str(temp_path),
|
||||
]
|
||||
|
||||
proc = subprocess.run(cmd)
|
||||
if proc.returncode != 0:
|
||||
raise RuntimeError(f"GIF preprocess failed for {src} (code {proc.returncode})")
|
||||
|
||||
return str(temp_path)
|
||||
|
||||
|
||||
def run(args):
|
||||
layout_path = Path(args.layout_json)
|
||||
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)
|
||||
|
||||
base_input = args.input
|
||||
if not base_input:
|
||||
raise ValueError("Global --input (base video) is required for json_layers.")
|
||||
|
||||
# 1) Probe base video info once
|
||||
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)
|
||||
|
||||
# Folder to hold temp GIF->MP4 files (next to the output file)
|
||||
tmp_dir = Path(args.output).with_suffix("")
|
||||
|
||||
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
|
||||
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 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))
|
||||
|
||||
# 3) Build filter_complex
|
||||
filter_parts = []
|
||||
|
||||
# Base video label is [0:v] directly (like overlay_img_pro)
|
||||
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}]"
|
||||
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()
|
||||
|
||||
x_expr, y_expr = _place_expr(layer)
|
||||
|
||||
# scale + alpha
|
||||
filter_parts.append(
|
||||
f"{lay_in}"
|
||||
f"scale={target_w}:-1,"
|
||||
f"format=rgba,colorchannelmixer=aa={opacity}"
|
||||
f"{lay_alpha}"
|
||||
)
|
||||
|
||||
# overlay
|
||||
filter_parts.append(
|
||||
f"{current_label}{lay_alpha}"
|
||||
f"overlay=x={x_expr}:y={y_expr}:format=auto"
|
||||
f"{next_label}"
|
||||
)
|
||||
|
||||
current_label = next_label
|
||||
|
||||
out_label = "[out_v]"
|
||||
filter_parts.append(f"{current_label}format=yuv420p{out_label}")
|
||||
|
||||
filter_complex = ";".join(filter_parts)
|
||||
|
||||
# 4) Build main ffmpeg command
|
||||
command = [
|
||||
"ffmpeg",
|
||||
"-err_detect", "ignore_err",
|
||||
"-fflags", "+discardcorrupt+genpts",
|
||||
]
|
||||
|
||||
for _typ, src in inputs_for_ffmpeg:
|
||||
command.extend(["-i", src])
|
||||
|
||||
command.extend([
|
||||
"-filter_complex", filter_complex,
|
||||
"-map", out_label,
|
||||
"-map", "0:a",
|
||||
"-c:v", "libx264",
|
||||
"-profile:v", "high",
|
||||
"-level:v", "4.2",
|
||||
"-pix_fmt", "yuv420p",
|
||||
"-movflags", "+faststart",
|
||||
"-c:a", "aac",
|
||||
args.output,
|
||||
])
|
||||
|
||||
final_cmd = (command[:1] + ["-y"] + command[1:]) if args.force else command
|
||||
|
||||
run_ffmpeg_with_progress(final_cmd, args.input, args.output)
|
||||
203
videobeaux/programs/xpiritualism.py
Normal file
203
videobeaux/programs/xpiritualism.py
Normal file
@@ -0,0 +1,203 @@
|
||||
# videobeaux/programs/xpiritualism.py
|
||||
#
|
||||
# Xpiritualism-style aesthetic:
|
||||
# - Soft pastel glow
|
||||
# - Multi-layer bloom
|
||||
# - Hazy vignette (implemented via blend, not fragile options)
|
||||
# - Gentle film grain
|
||||
# - Optional hue shift
|
||||
#
|
||||
# Usage example:
|
||||
# videobeaux -P xpiritualism -i input.mp4 -o xpiri_soft.mp4 \
|
||||
# --style soft --bloom-radius 10 --bloom-strength 0.8 \
|
||||
# --saturation 1.2 --grain 8 --vignette 0.4
|
||||
|
||||
from videobeaux.utils.ffmpeg_operations import run_ffmpeg_with_progress
|
||||
|
||||
|
||||
def register_arguments(parser):
|
||||
parser.description = (
|
||||
"Xpiritualism aesthetic: multi-layer bloom + pastel spiritualizer.\n"
|
||||
"Layers:\n"
|
||||
" - Soft bloom / glow\n"
|
||||
" - Pastel color grade / hue shift\n"
|
||||
" - Hazy vignette\n"
|
||||
" - Film-like grain overlay\n"
|
||||
"Presets via --style, with tunable intensities."
|
||||
)
|
||||
|
||||
# High-level “mood” preset
|
||||
parser.add_argument(
|
||||
"--style",
|
||||
choices=["soft", "deep", "cosmic"],
|
||||
default="soft",
|
||||
help=(
|
||||
"Xpiritualism style preset:\n"
|
||||
" soft = gentle, pastel, minimal grain (default)\n"
|
||||
" deep = richer contrast, stronger vignette & bloom\n"
|
||||
" cosmic = more hue shift, grainier, dreamy"
|
||||
)
|
||||
)
|
||||
|
||||
# Bloom controls
|
||||
parser.add_argument(
|
||||
"--bloom-radius",
|
||||
type=float,
|
||||
default=8.0,
|
||||
help="Bloom blur radius (luma_radius for boxblur). Higher = softer glow. Default: 8.0"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--bloom-strength",
|
||||
type=float,
|
||||
default=0.7,
|
||||
help="Bloom blend opacity (0.0–1.0). Default: 0.7"
|
||||
)
|
||||
|
||||
# Color & tone
|
||||
parser.add_argument(
|
||||
"--saturation",
|
||||
type=float,
|
||||
default=1.15,
|
||||
help="Overall saturation multiplier. Default: 1.15"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--hue-shift",
|
||||
type=float,
|
||||
default=0.0,
|
||||
help="Hue shift in DEGREES (used in ffmpeg hue filter). Default: 0.0"
|
||||
)
|
||||
|
||||
# Vignette intensity (we implement this via blend, not filter options)
|
||||
parser.add_argument(
|
||||
"--vignette",
|
||||
type=float,
|
||||
default=0.35,
|
||||
help="Vignette blend strength (0.0–1.0-ish). Default: 0.35"
|
||||
)
|
||||
|
||||
# Grain
|
||||
parser.add_argument(
|
||||
"--grain",
|
||||
type=float,
|
||||
default=8.0,
|
||||
help="Film grain strength (ffmpeg noise alls parameter). Default: 8.0"
|
||||
)
|
||||
|
||||
|
||||
def run(args):
|
||||
"""
|
||||
Filtergraph structure:
|
||||
|
||||
[0:v] format=yuv444p,split=4 [base][bloom_src][grain_src][vig_src];
|
||||
|
||||
# Bloom layer:
|
||||
[bloom_src] boxblur -> eq (sat bump) -> [bloom]
|
||||
[base][bloom] blend=screen -> [bloomed]
|
||||
|
||||
# Pastel grade + hue shift:
|
||||
[bloomed] eq (contrast/brightness/sat) + hue -> [pastel]
|
||||
|
||||
# Vignette branch (no fancy options; defaults are robust):
|
||||
[vig_src] vignette [vig_mask]
|
||||
[pastel][vig_mask] blend=multiply:opacity=VIGNETTE -> [vigged]
|
||||
|
||||
# Grain layer:
|
||||
[grain_src] noise -> [grain]
|
||||
|
||||
# Final composite:
|
||||
[vigged][grain] blend=overlay:opacity=0.30 -> [out_v]
|
||||
"""
|
||||
|
||||
# Start with user-provided values
|
||||
bloom_radius = float(getattr(args, "bloom_radius", 8.0))
|
||||
bloom_strength = float(getattr(args, "bloom_strength", 0.7))
|
||||
saturation = float(getattr(args, "saturation", 1.15))
|
||||
hue_shift = float(getattr(args, "hue_shift", 0.0))
|
||||
vignette = float(getattr(args, "vignette", 0.35))
|
||||
# this is a blend opacity, not a direct vignette filter param now
|
||||
grain = float(getattr(args, "grain", 8.0))
|
||||
|
||||
# Adjust based on style preset
|
||||
style = getattr(args, "style", "soft")
|
||||
|
||||
if style == "deep":
|
||||
bloom_strength *= 1.15
|
||||
saturation *= 1.10
|
||||
vignette *= 1.20
|
||||
grain *= 1.05
|
||||
elif style == "cosmic":
|
||||
# if user left hue_shift at default, give it a gentle cosmic twist
|
||||
if abs(hue_shift) < 0.01:
|
||||
hue_shift = 18.0 # degrees
|
||||
bloom_strength *= 1.10
|
||||
saturation *= 1.05
|
||||
vignette *= 1.10
|
||||
grain *= 1.30
|
||||
|
||||
# Clamp some values into sane ranges
|
||||
def clamp(val, lo, hi):
|
||||
return max(lo, min(hi, val))
|
||||
|
||||
bloom_strength = clamp(bloom_strength, 0.0, 1.0)
|
||||
saturation = clamp(saturation, 0.5, 2.0)
|
||||
vignette = clamp(vignette, 0.0, 1.0) # now used directly as blend opacity
|
||||
grain = clamp(grain, 0.0, 40.0)
|
||||
|
||||
# Build filtergraph
|
||||
# Note: hue filter uses radians internally; we pass degrees * PI/180.
|
||||
filtergraph = (
|
||||
# Prep + split into four branches
|
||||
f"[0:v]format=yuv444p,split=4[base][bloom_src][grain_src][vig_src];"
|
||||
|
||||
# Bloom branch
|
||||
f"[bloom_src]boxblur=luma_radius={bloom_radius}:luma_power=2,"
|
||||
f"eq=saturation=1.20[bloom];"
|
||||
|
||||
# Screen bloom over base
|
||||
f"[base][bloom]blend=all_mode=screen:all_opacity={bloom_strength}[bloomed];"
|
||||
|
||||
# Pastel EQ + hue shift
|
||||
f"[bloomed]eq=contrast=1.02:brightness=0.02:saturation={saturation},"
|
||||
f"hue=h={hue_shift}*PI/180[pastel];"
|
||||
|
||||
# Vignette branch – robust: use default vignette, then multiply with pastel
|
||||
f"[vig_src]vignette[vig_mask];"
|
||||
f"[pastel][vig_mask]blend=all_mode=multiply:all_opacity={vignette}[vigged];"
|
||||
|
||||
# Grain branch
|
||||
f"[grain_src]noise=alls={int(grain)}:allf=t+u[grain];"
|
||||
|
||||
# Final overlay blend
|
||||
f"[vigged][grain]blend=all_mode=overlay:all_opacity=0.30[out_v]"
|
||||
)
|
||||
|
||||
command = [
|
||||
"ffmpeg",
|
||||
"-err_detect", "ignore_err",
|
||||
"-fflags", "+genpts+discardcorrupt",
|
||||
|
||||
"-i", args.input,
|
||||
|
||||
"-filter_complex", filtergraph,
|
||||
"-map", "[out_v]",
|
||||
"-map", "0:a?", # keep audio if present
|
||||
|
||||
# Video encoding
|
||||
"-c:v", "libx264",
|
||||
"-preset", "medium",
|
||||
"-crf", "18",
|
||||
"-pix_fmt", "yuv420p",
|
||||
|
||||
# Audio – copy to avoid unnecessary re-encode
|
||||
"-c:a", "copy",
|
||||
|
||||
args.output,
|
||||
]
|
||||
|
||||
# Respect global --force: inject -y right after 'ffmpeg'
|
||||
final_cmd = (command[:1] + ["-y"] + command[1:]) if getattr(args, "force", False) else command
|
||||
|
||||
# Uncomment this if we need to debug the exact ffmpeg command later:
|
||||
# print(" ".join(f'"{c}"' if " " in c else c for c in final_cmd))
|
||||
|
||||
run_ffmpeg_with_progress(final_cmd, args.input, args.output)
|
||||
Reference in New Issue
Block a user