I've put together the following code to try and automate the tedious parts of my video editing process. The issue I'm running into is that I run the code in Visual Studio Code, but nothing happens and I receive no errors.
I'm wanting the folder specified to be monitored for unedited/new .mp4 files. When one or multiple are detected, the .mp4 should be formatted in the specified for TikTok upload. (As I would within a video editor, the clip should maintain its aspect ratio within a project setting of 1080x1920/9:16, then a duplicate of the clip is put behind the clip, not maintaining its aspect ratio and have a gaussian blur applied to it. Once the process is done, each edited clip would be put into a separate "edited" folder, or back in the original destination, but renamed as edited.
Here is my current code:
import os
import shutil
import ffmpeg
import watchdog
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
# path to monitor
monitor_path = "D:\Downloads\TikTok"
# path to output folder
output_folder = "D:\Downloads\Edited"
# define a function to be called when a new .mp4 file is detected
def detect_new_file(event):
# input video file path
input_video = event.src_path
# output video file name
output_video_name = os.path.basename(input_video)
# output video file path
output_video = os.path.join(output_folder, output_video_name)
# start ffmpeg command
ffmpeg_command = (
ffmpeg
.input(input_video)
.trim(start_time=0, end_time='-10')
.filter('scale', size='1080x1920')
.filter('blur', size=5)
.overlay(input_video, x=540, y=960)
.output(output_video, format='mp4')
.run()
)
# move the original file to a new folder
shutil.move(input_video, os.path.join(monitor_path, "D:\Downloads\OriginalClips"))
# create a handler for the file event
class MP4Handler(watchdog.events.FileSystemEventHandler):
def on_created(self, event):
if event.src_path.endswith('.mp4'):
detect_new_file(event)
handler = MP4Handler()
# create a watcher object
watcher = watchdog.observers.Observer()
# start monitoring the folder
watcher.schedule(handler, monitor_path, recursive=True)
watcher.start()
# wait for events
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
watcher.stop()
# close the watcher
watcher.join()
Current received error(s):
TypeError: Expected incoming stream(s) to be of one of the following types: ffmpeg.nodes.FilterableStream; got <class 'str'>
Any help is appreciated.
Related
I have a program that sends some data to GCS in a while loop, which requires a JSON credential file, it roughly looks like the following:
import os
import sys
from pathlib import Path
GOOGLE_SERVICE_ACCOUNT_FP = "pos-service-account.json"
IS_INSTALLER = getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS")
if IS_INSTALLER:
GOOGLE_SERVICE_ACCOUNT_FP = os.path.join(sys._MEIPASS, GOOGLE_SERVICE_ACCOUNT_FP)
def send_data(data, credential_fp: Path = Path(GOOGLE_SERVICE_ACCOUNT_FP)):
if not credential_fp.is_file():
msg = f"Google service account key json file: {str(credential_fp)!r} is not found!\nPWD files:\n{list(Path.cwd().glob('*'))}"
raise FileNotFoundError(msg)
# some function post data to cloud storage
post_to_gcs(data)
def main():
while True:
data = ...
send_data(data)
if __name__ == '__main__':
main()
I packaged using one file with the following command:
pyinstaller --onefile -w .\main.py --add-data 'pos-service-account.json;.'
Then when I clicked on the .exe file on windows, it was working fine, I could see the data posted by this program. However, I came back after a few days, I got the file not found error:
Google service account key json file: 'C:\\Users\\POS\\AppData\\Local\\Temp\\_MEI30522\\pos-service-account.json' is not found!
this does not make sense to me as the program was working at the beginning, which means it did find the json file, I'm still trying to replicate the error, but so far my suspicion is:
Does the sys._MEIPASS directory ever change? e.g. if the computer goes to sleep and comes back on (I'm going to test it), I could see how my script would fail if the _MEIPASS changes when the program is running.
Yes the MEIPASS folder changes it's name often, so hardcoding that file location is a bad idea. Instead save the file relative to the __file__ path variable so it will always be referring to the currently running programs location.
import os
import sys
from pathlib import Path
GOOGLE_SERVICE_ACCOUNT_FP = "pos-service-account.json"
CREDENTIAL = os.path.join(os.path.dirname(os.path.abspath(__file__)), GOOGLE_SERVICES_ACCOUNT_FP)
def send_data(data, credential_fp=CREDENTIAL):
if not credential_fp.is_file():
msg = f"Google service account key json file: {str(credential_fp)!r} is not found!\nPWD files:\n{list(Path.cwd().glob('*'))}"
raise FileNotFoundError(msg)
# some function post data to cloud storage
post_to_gcs(data)
def main():
while True:
data = ...
send_data(data)
if __name__ == '__main__':
main()
My process to achieve, is to print multiple pdf files from a folder, closing Adobe Acrobat afterwards and lastly mapping the files to new folders, using another of my scripts.
The Mapping works as intended, but I´m struggling with printing all files. I can either print only one, or none of the files and Adobe still remains open. I've been playing around with asyncio, but I dont know if it gets the job done.
The code is not very well documented or of outstanding quality, it just has to get the task done and willl be very likely never be touched agein. Here it is:
import os
import sys
import keyboard
import asyncio
import psutil
from win32 import win32api, win32print
import map_files
import utility
def prepareFilesToPrint(folder):
# Scans folder for files with naming convention and puts them in a seperate array to print
filesToPrint = []
for file in os.listdir(folder.value):
if utility.checkFileName(file):
filesToPrint.append(file)
return filesToPrint
def preparePrinter():
# Opens the printer and defines attributes such as duplex mode
name = win32print.GetDefaultPrinter()
printdefaults = {"DesiredAccess": win32print.PRINTER_ALL_ACCESS}
handle = win32print.OpenPrinter(name, printdefaults)
attributes = win32print.GetPrinter(handle, 2)
attributes['pDevMode'].Duplex = 2 # Lange Seite spiegeln
win32print.SetPrinter(handle, 2, attributes, 0)
return handle
async def printFiles(filesToPrint):
for file in filesToPrint:
await win32api.ShellExecute(
0, "print", file, '"%s"' % win32print.GetDefaultPrinter(), ".", 0)
def cleanup(handle):
# Closes Adobe after printing ALL files (!working)
win32print.ClosePrinter(handle)
for p in psutil.process_iter():
if 'AcroRd' in str(p):
p.kill()
async def printTaskFiles():
# Iterates over files in downloads folder and prints them if they are task sheets (!working)
os.chdir("C:/Users/Gebker/Downloads/")
filesToPrint = prepareFilesToPrint(utility.Folder.DOWNLOADS)
if filesToPrint.__len__() == 0:
print("No Files to print. Exiting...")
sys.exit()
print("=============================================================")
print("The following files will be printed:")
for file in filesToPrint:
print(file)
print("=============================================================")
input("Press ENTER to print. Exit with ESC")
while True:
try:
if keyboard.is_pressed('ENTER'):
print("ENTER pressed. Printing...")
handle = preparePrinter()
await printFiles(filesToPrint)
cleanup(handle)
print("Done printing. Mapping files now...")
# map_files.scanFolders()
break
elif keyboard.is_pressed('ESC'):
print("ESC pressed. Exiting...")
sys.exit()
except:
break
if __name__ == "__main__":
asyncio.run(printTaskFiles())
I want to find a minimal audio file (like the testfile_gif below) for unit testing.
I don't want to load it from the hard drive (like here).
I want the second test to perform like the first one.
import magic
from django.core.files.uploadedfile import SimpleUploadedFile
class TestFiles(TestCase):
def test_working(self):
# Test an Image File #
testfile_gif = (
b'\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x00\x00\x00\x21\xf9\x04'
b'\x01\x0a\x00\x01\x00\x2c\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02'
b'\x02\x4c\x01\x00\x3b')
gif_file = SimpleUploadedFile(name='image.gif', content=testfile_gif,
content_type='image/gif')
mime = magic.from_buffer(gif_file.read(1024), mime=True)
self.assertEqual('image/gif', mime)
def test_not_working(self):
# Test an Audio File #
testfile_audio = b'What should be written in here?'
audio_file = SimpleUploadedFile(name='music.mp3',
content=testfile_audio,
content_type='audio/mpeg')
mime = magic.from_buffer(audio_file.read(1024), mime=True)
self.assertEqual('audio/mpeg', mime)
Preferably, I don't want to use any packages (like import mock).
UPDATE
Here is an mp3 file with audio/mpeg mime:
b'MM\x00*\x00\x00\x00\x08\x00\x03\x01\x00\x00\x03\x00\x00\x00\x01\x00\x01'
b'\x00\x00\x01\x01\x00\x03\x00\x00\x00\x01\x00\x01\x00\x00\x01\x11\x00\x03'
b'\x00\x00\x00\x01\x00\x00\x00\x00'
Here's a very simple .wav file. You can make these with python quite easily using the wave module.
b'RIFF$\x00\x00\x00WAVEfmt \x10\x00\x00\x00\x01\x00\x01\x00\x00\x04\x00\x00\x00\x04\x00\x00\x01\x00\x08\x00data\x00\x00\x00\x00'
I am trying to remove files after I'm done using them but I get an error all the time...
The error:
PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: '0.mp4'
I tried closing the Objects but Its not working...
import os
from moviepy.editor import VideoFileClip, concatenate_videoclips
DEFAULT_HEIGHT = 720
DEFAULT_WIDTH = 1280
clip_names = ["0.mp4", "1.mp4"]
clips = []
for name in clip_names:
clips.append(VideoFileClip(name).resize(width=DEFAULT_WIDTH,height=DEFAULT_HEIGHT))
final_clip = concatenate_videoclips(clips)
final_clip.write_videofile("video.mp4")
for clip in clips:
clip.__del__()
for name in clip_names:
os.remove(name)
I want to remove the file with os.remove...
Did you try simply closing the clips?
for clip in clips:
clip.close()
From the source here.
Or, if you want to do this cleanly in case of errors, use a with context:
import contextlib
with contextlib.ExitStack() as stack:
clips = []
for name in clip_names:
clip = VideoFileClip(name)
stack.enter_context(contextlib.closing(clip))
clips.append(clip.resize(width=DEFAULT_WIDTH,height=DEFAULT_HEIGHT))
final_clip = concatenate_videoclips(clips)
final_clip.write_videofile("video.mp4")
# exiting the `with` block released the clips
for name in clip_names:
os.remove(name)
This approach uses an ExitStack to track clips. When the program exits the with block, all contexts passed to enter_context are exited, releasing all the clips.
i am trying to write a program to read either mp3 or mp4 music files and then print there tags to the screen but the code i have written dose not seem to work and i am completely lost in now to fix it
# import mutagen
from mutagen.easyid3 import EasyID3
from mutagen.mp4 import MP4
from mutagen.mp3 import MP3
# Define a function to read ID3 tags
MP4 == (audio = MP4(ip))
MP3 == (Audio = MP3(ip))
def readid3 (ip):
if music_file == MP4:
print(audio['\xa9alb'])
print(audio['\xa9nam'])
print(audio['\xa9ART'])
print(audio['\xa9gen'])
else:
if music_file == MP3:
print(audio['TALB'])
print(audio['TIT2'])
print(audio['TPE1'])
print(audio['TCON'])
else:
print('Is not a MP3 or MP4 file')
####### main body ########
# prompt user to enter a MP4
music_file = open('mp4file.txt','a+')
music_file.write("/Users/martins-mac/Music/iTunes/iTunes Media/Music/Frightened Rabbit/Pedestrian Verse/01 Acts of Man.m4a")
music_file.close()
# call function to print ID3 tags
readid3(music_file)
So these two lines
MP4 == (audio = MP4(ip))
MP3 == (Audio = MP3(ip))
Are just comparing something. They'll simply return True or False, and have no effect. As it stands, they're just comparing the modules you've imported to an assignment, which is confusing to me. What were you trying to do with those lines?
if music_file == MP4:
Is the correct way to use ==, but music_file is not defined at this point. When you call it later on, it's defined globally, but it looks like
if ip == MP4:
Would serve you better.
When you get down to readid3(music_file), you've already closed the file. Try putting music_file = open("mp4file.txt", "r") before that line.
You may also be well-served to check out https://mutagen.readthedocs.org/en/latest/api/base.html, as there are bound to be some useable examples in there.