mirror of
https://github.com/chev2/shitpost-video-generator.git
synced 2025-10-30 08:12:03 +00:00
Main script + update README
This commit is contained in:
parent
a801a18130
commit
65a2dc82a9
2 changed files with 186 additions and 1 deletions
|
|
@ -5,7 +5,7 @@ Generates an output video with overlayed audio and effects using a list of audio
|
|||
Here's how it works:
|
||||
1. Gets all video & audio sources in the `VideoSources` and `AudioSources` folders.
|
||||
2. Gets a seed for the random module, which can be used if a user wants to re-generate the same video again.
|
||||
3. Chooses _n_ random videos where _n_ is the number of videos the user wants to merge into the final results (e.g. if you want to merge 40 different videos, it chooses 40 random videos). The max amount of random videos is capped to the number of video files in the `VideoSources` folder.
|
||||
3. Chooses _n_ random videos where _n_ is the number of videos the user wants to merge into the final results (e.g. if you want to merge 40 different videos, it chooses 40 random videos). The script will first to attempt to avoid duplicate videos. If the chosen video amount is higher than the available videos, then the script will add duplicate videos to meet that amount.
|
||||
4. Applies a bunch of random effects to the video files, which include trimming the video to be a random short length, mirroring the video, speeding it up or slowing it down, reversing the video, as well as other effects.
|
||||
5. Merges all the now effect-applied videos into one big video.
|
||||
6. Applies effects to the audio files, which include trimming the audio to be a random short length. The script will also attemt to repeat audio clips less than 5 seconds to give a repetitive-like nature to the end result.
|
||||
|
|
|
|||
185
ShitpostGenerator.py
Normal file
185
ShitpostGenerator.py
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
import moviepy.audio
|
||||
import random
|
||||
from sys import maxsize
|
||||
from os import listdir
|
||||
from moviepy import editor
|
||||
from moviepy.video import fx
|
||||
|
||||
videoSourceFolder = "VideoSources"
|
||||
audioSourceFolder = "AudioSources"
|
||||
|
||||
videoFiles = [videoSourceFolder + "/" + vid for vid in listdir(videoSourceFolder)]
|
||||
audioFiles = [audioSourceFolder + "/" + vid for vid in listdir(audioSourceFolder)]
|
||||
|
||||
chosenSeed = input("Video seed (or 'any' to use system time): ")
|
||||
while not chosenSeed.isdecimal() and not chosenSeed in ["any", "skip", "default", "time"]:
|
||||
chosenSeed = input("Video seed (or 'any' to use system time): ")
|
||||
|
||||
seed = int(chosenSeed) if chosenSeed.isdecimal() else random.randrange(maxsize) #get a chosen or random seed to use and reference later
|
||||
|
||||
print(f'Chose seed: {seed}')
|
||||
|
||||
rng = random.Random(seed)
|
||||
|
||||
print(f'Found {len(videoFiles)} videos and {len(audioFiles)} sounds')
|
||||
|
||||
def ContinuousFlipVideo(clip): #flip a video multiple times over its duration
|
||||
flipAmount = rng.randint(1, 7) #how many times the clip will be flipped
|
||||
flipPeriods = [rng.uniform(0, clip.duration) for _ in range(flipAmount)] #random periods at which to shuffle
|
||||
|
||||
flipPeriods.sort() #in ascending order
|
||||
allClips = []
|
||||
lastPeriod = 0
|
||||
|
||||
for period in flipPeriods:
|
||||
newClip = clip.subclip(lastPeriod, period)
|
||||
allClips.append(newClip)
|
||||
lastPeriod = period
|
||||
|
||||
newClip = clip.subclip(lastPeriod, clip.duration)
|
||||
allClips.append(newClip)
|
||||
|
||||
for i in range(len(allClips)):
|
||||
clip = allClips[i]
|
||||
allClips[i] = rng.choice([fx.mirror_x.mirror_x, fx.mirror_y.mirror_y, lambda v: v])(allClips[i]) #flip on the x, or y, or don't flip
|
||||
|
||||
finalClip = editor.concatenate_videoclips(allClips)
|
||||
|
||||
return finalClip
|
||||
|
||||
def RepeatVideo(clip): #repeat a video multiple times
|
||||
randomDuration = rng.uniform(0.01, 0.2)
|
||||
repeatAmount = int((clip.duration/randomDuration)*0.5)
|
||||
|
||||
startOffset = rng.uniform(0, clip.duration - randomDuration)
|
||||
newClip = clip.subclip(startOffset, startOffset+randomDuration)
|
||||
|
||||
finalClip = editor.concatenate_videoclips([newClip]*repeatAmount)
|
||||
return finalClip
|
||||
|
||||
def ShuffleVideo(clip): #take a clip, split it into multiple parts, shuffle those parts
|
||||
shuffleAmount = rng.randint(20, 50) #how many times the clip will be split and shuffled
|
||||
shufflePeriods = [rng.uniform(0, clip.duration) for _ in range(shuffleAmount)] #random periods at which to shuffle
|
||||
|
||||
shufflePeriods.sort() #in ascending order
|
||||
allClips = []
|
||||
lastPeriod = 0
|
||||
|
||||
for period in shufflePeriods:
|
||||
newClip = clip.subclip(lastPeriod, period)
|
||||
allClips.append(newClip)
|
||||
lastPeriod = period
|
||||
|
||||
newClip = clip.subclip(lastPeriod, clip.duration)
|
||||
allClips.append(newClip)
|
||||
rng.shuffle(allClips) #shuffle around the clips to get the final result
|
||||
finalClip = editor.concatenate_videoclips(allClips)
|
||||
|
||||
return finalClip
|
||||
|
||||
def BadQuality(clip):
|
||||
w, h = clip.size
|
||||
return clip.resize((rng.randint(32, w), rng.randint(32, h))).resize((w, h))
|
||||
|
||||
videoEffects = [
|
||||
lambda v: fx.speedx.speedx(v, rng.uniform(0.7, 3)), #speed up/slow down
|
||||
lambda v: fx.mirror_x.mirror_x(v), #mirror on the x axis
|
||||
lambda v: fx.time_mirror.time_mirror(v), #reverse the video
|
||||
lambda v: v.fx(fx.time_symmetrize.time_symmetrize).fx(fx.speedx.speedx, factor=rng.uniform(1.4, 2.3)), #forward + reverse with speed up
|
||||
lambda v: RepeatVideo(v), #repeat the video multiple times
|
||||
lambda v: ShuffleVideo(v), #shuffle up parts of the video for a glitch-like effect
|
||||
lambda v: ContinuousFlipVideo(v), #flip the video on the x and y axis multiple times
|
||||
lambda v: fx.lum_contrast.lum_contrast(v, lum=0, contrast=rng.uniform(0.3, 2)), #change contrast
|
||||
lambda v: BadQuality(v)
|
||||
]
|
||||
|
||||
videoObjects = []
|
||||
audioObjects = []
|
||||
|
||||
videoAmount = input("Amount of videos: ")
|
||||
while not videoAmount.isdecimal():
|
||||
videoAmount = input("Amount of videos: ")
|
||||
|
||||
videoAmount = int(videoAmount)
|
||||
|
||||
randomVideos = rng.sample(videoFiles, min(videoAmount, len(videoFiles)))
|
||||
if videoAmount > len(videoFiles): #if there is a higher chosen amount than total, re-use videos
|
||||
videoAmountToAdd = videoAmount - len(videoFiles)
|
||||
print(f'Chosen video amount is higher than available video amount - re-using {videoAmountToAdd} videos...')
|
||||
additionalVideos = rng.choices(videoFiles, k=videoAmountToAdd)
|
||||
randomVideos += additionalVideos
|
||||
|
||||
print(f"Compiling {videoAmount} videos...", end="")
|
||||
|
||||
for video in randomVideos:
|
||||
print(".", end="")
|
||||
|
||||
newClip = editor.VideoFileClip(video).resize(height=480) #target_resolution=(512, 512)
|
||||
|
||||
randomDuration = rng.uniform(0.5, 3.5) #0.5, 2.6
|
||||
if newClip.duration > randomDuration:
|
||||
startOffset = rng.uniform(0, newClip.duration - randomDuration)
|
||||
newClip = newClip.subclip(startOffset, startOffset+randomDuration)
|
||||
|
||||
if rng.choice([True, True, False]):
|
||||
newClip = rng.choice(videoEffects)(newClip) #apply a random effect
|
||||
|
||||
videoObjects.append(newClip)
|
||||
|
||||
print("Finished compiling videos.")
|
||||
|
||||
finalVideo = editor.concatenate_videoclips(videoObjects, method="compose") # method="compose"
|
||||
|
||||
audioAmount = int(videoAmount*0.75)
|
||||
|
||||
randomSounds = rng.sample(audioFiles, min(audioAmount, len(audioFiles)))
|
||||
|
||||
if audioAmount > len(audioFiles):
|
||||
audioAmountToAdd = audioAmount - len(audioFiles)
|
||||
print(f'Chosen audio amount is higher than available audio amount - re-using {audioAmountToAdd} audio sources...')
|
||||
additionalAudio = rng.choices(audioFiles, k=audioAmountToAdd)
|
||||
randomSounds += additionalAudio
|
||||
|
||||
print(f"Compiling {audioAmount} sounds...", end="")
|
||||
|
||||
copiedSoundAmount = 0
|
||||
for audio in randomSounds:
|
||||
print(".", end="")
|
||||
|
||||
newClip = editor.AudioFileClip(audio)
|
||||
newClip = moviepy.audio.fx.volumex.volumex(newClip, 0.5) # modify volume
|
||||
|
||||
if newClip.duration > 5: #for long clips
|
||||
randomDuration = rng.uniform(0.7, 6) # crop audio duration
|
||||
if newClip.duration > randomDuration: # if the audio is longer than the cropped duration, crop the audio at a random position
|
||||
startOffset = rng.choice([rng.uniform(0, newClip.duration - randomDuration), 0]) #either use a random offset, or start at beginning of audio clip
|
||||
newClip = newClip.subclip(startOffset, startOffset+randomDuration)
|
||||
|
||||
newClip = newClip.set_start(rng.uniform(0, finalVideo.duration-newClip.duration)) # move audio around video length
|
||||
audioObjects.append(newClip)
|
||||
else:
|
||||
clipPosition = rng.uniform(0, finalVideo.duration-newClip.duration)
|
||||
|
||||
newClip = newClip.set_start(clipPosition) # move audio around video length
|
||||
audioObjects.append(newClip)
|
||||
for i in range(rng.randint(1, 5)): #add a copy of the clip near the original clip
|
||||
print(".", end="")
|
||||
copiedSoundAmount += 1
|
||||
|
||||
minimumRange = max(0, clipPosition-2)
|
||||
maximumRange = min(finalVideo.duration, clipPosition+2) - newClip.duration
|
||||
|
||||
copiedClip = newClip.set_start(rng.uniform(minimumRange, maximumRange)) # move audio around video length
|
||||
audioObjects.append(copiedClip)
|
||||
|
||||
print(f"Finished compiling audio. Added {copiedSoundAmount} duplicate sounds, total {audioAmount+copiedSoundAmount}.")
|
||||
|
||||
newAudioClip = editor.CompositeAudioClip([finalVideo.audio] + audioObjects)
|
||||
|
||||
finalVideo.audio = newAudioClip
|
||||
finalVideo.write_videofile(f'final_result_{seed}_{videoAmount}.mp4', fps=30, audio_bitrate="96k")
|
||||
|
||||
for video in videoObjects:
|
||||
video.close()
|
||||
for audio in audioObjects:
|
||||
audio.close()
|
||||
Loading…
Add table
Reference in a new issue