ajout de création de formats vidéo mp4

This commit is contained in:
Francois Pelletier 2023-02-12 01:04:11 -05:00
parent 4f6f49e313
commit bb814381c5
7 changed files with 240 additions and 45 deletions

97
main.py
View file

@ -12,6 +12,7 @@ import os
from wand.image import Image
from wand.color import Color
import shutil
import cv2
class DocumentSpecs(BaseModel):
@ -28,6 +29,8 @@ class DocumentSpecs(BaseModel):
margin: int
vmargin: int
extension: str
fps: int
stilltime: int
class FormatParameters(BaseModel):
@ -41,6 +44,8 @@ class FormatParameters(BaseModel):
margin: int
vmargin: int
extension: str
fps: int
stilltime: int
class Styles(BaseModel):
@ -78,6 +83,48 @@ def convert_pdf(filename, filetype, output_path, resolution=300):
img.save(filename=image_filename)
def convert_video(images_path, output_path, width, height, fps, stilltime):
"""
Convert images in output_path into a mp4 file usine OpenCV.
:param images_path:
:param output_path:
:param images_path:
:param width:
:param height:
:return:
"""
# define a frame array
frame_array = []
# list all files in images_path
files = [f for f in os.listdir(images_path) if os.path.isfile(os.path.join(images_path, f))]
# sort the files
files.sort()
# create a video writer object
for i in range(len(files)):
file = os.path.join(images_path, files[i])
logging.log(logging.INFO, f'Converting {file} to mp4')
img = cv2.imread(file)
for j in range(fps*stilltime):
frame_array.append(img)
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
video_writer = cv2.VideoWriter(output_path,
fourcc,
fps,
(width, height))
for i in range(len(frame_array)):
# writing to a image array
video_writer.write(frame_array[i])
video_writer.release()
logging.log(logging.INFO, f'Finished converting {output_path}')
app = FastAPI()
@ -121,7 +168,6 @@ async def generer(specs: DocumentSpecs):
cover_file = f'{os.getcwd()}/styles/{specs.style}/{specs.format}/cover.tex'
datef = datetime.datetime.now().strftime("%Y-%m-%d")
os.makedirs("out", exist_ok=True)
output_file = f"./out/{specs.style}-{specs.format}-{datef}-output.pdf"
filters = ['latex-emoji.lua', 'centered.lua']
pdoc_args = [
f'--include-in-header={header_file}',
@ -132,13 +178,15 @@ async def generer(specs: DocumentSpecs):
f'--pdf-engine={specs.pdfengine}',
'-V', f'linkcolor={specs.linkcolor}',
'-V', f'fontsize={specs.fontsize}pt',
'-V', f'geometry:paperwidth={round(specs.paperwidth * specs.ratio / 100,-1) / 300}in',
'-V', f'geometry:paperheight={round(specs.paperheight * specs.ratio / 100,-1) / 300}in',
'-V', f'geometry:paperwidth={round(specs.paperwidth * specs.ratio / 100, -1) / 300}in',
'-V', f'geometry:paperheight={round(specs.paperheight * specs.ratio / 100, -1) / 300}in',
'-V', f'geometry:left={specs.margin / 300}in',
'-V', f'geometry:right={specs.margin / 300}in',
'-V', f'geometry:top={specs.vmargin / 300}in',
'-V', f'geometry:bottom={specs.vmargin / 300}in'
]
pdf_file_path = f"./out/{specs.style}-{specs.format}-{datef}-output.pdf"
images_path = f"./out/{specs.style}-{specs.format}-{datef}-images"
try:
logging.info("Dossier courant = " + os.getcwd())
@ -149,32 +197,45 @@ async def generer(specs: DocumentSpecs):
extra_args=pdoc_args,
filters=filters,
cworkdir=os.getcwd(),
outputfile=output_file
outputfile=pdf_file_path
)
except RuntimeError as rerr:
logging.exception(rerr)
except OSError as oerr:
logging.exception(oerr)
if specs.extension in ["png", "jpg"]:
zip_filename = os.path.join("out", os.path.splitext(os.path.basename(output_file))[0])
png_output_dir = "./out/png_output"
if not os.path.exists(png_output_dir):
os.mkdir(png_output_dir)
if specs.extension in ["png", "jpg", "mp4"]:
filename = os.path.join("out", os.path.splitext(os.path.basename(pdf_file_path))[0])
if not os.path.exists(images_path):
os.mkdir(images_path)
conversion_extension = specs.extension
output_extension = specs.extension
if specs.extension in ["mp4"]:
conversion_extension = "jpg"
try:
convert_pdf(output_file,
specs.extension,
png_output_dir,
convert_pdf(pdf_file_path,
conversion_extension,
images_path,
resolution=300)
shutil.make_archive(base_name=zip_filename,
format='zip',
root_dir=png_output_dir)
shutil.rmtree(png_output_dir)
if specs.extension in ["png", "jpg"]:
shutil.make_archive(base_name=filename,
format='zip',
root_dir=images_path)
output_extension = "zip"
shutil.rmtree(images_path)
if specs.extension in ["mp4"]:
output_extension = "mp4"
convert_video(images_path=images_path,
output_path=f"{filename}.{output_extension}",
width=specs.paperwidth,
height=specs.paperheight,
fps=specs.fps,
stilltime=specs.stilltime)
except Exception as e:
logging.exception(e)
return FileResponse(zip_filename + ".zip")
return FileResponse(f"{filename}.{output_extension}")
elif specs.extension == "pdf":
return FileResponse(output_file)
return FileResponse(pdf_file_path)
else:
return 0

View file

@ -20,4 +20,5 @@ watchfiles~=0.18.1
httpx~=0.23.1
setuptools~=67.2.0
pypandoc~=1.10
Wand~=0.6.10
Wand~=0.6.10
opencv-python~=4.7.0.68

View file

@ -6,8 +6,11 @@
"fontsize": 12,
"paperwidth": 2480,
"paperheight": 3507,
"ratio": 100,
"margin": 248,
"vmargin": 350,
"vmargin": 525,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
},
"lettre": {
@ -17,8 +20,11 @@
"fontsize": 12,
"paperwidth": 2550,
"paperheight": 3300,
"ratio": 100,
"margin": 255,
"vmargin": 330,
"vmargin": 495,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
},
"linkedin": {
@ -28,8 +34,11 @@
"fontsize": 16,
"paperwidth": 1200,
"paperheight": 1200,
"margin": 120,
"vmargin": 120,
"ratio": 100,
"margin": 180,
"vmargin": 180,
"fps": 15,
"stilltime": 2,
"extension": "jpg"
},
"instagram": {
@ -39,8 +48,11 @@
"fontsize": 16,
"paperwidth": 1080,
"paperheight": 1920,
"ratio": 100,
"margin": 108,
"vmargin": 192,
"vmargin": 244,
"fps": 15,
"stilltime": 2,
"extension": "jpg"
},
"pinterest": {
@ -50,8 +62,11 @@
"fontsize": 16,
"paperwidth": 735,
"paperheight": 1102,
"ratio": 100,
"margin": 75,
"vmargin": 110,
"vmargin": 165,
"fps": 15,
"stilltime": 2,
"extension": "jpg"
},
"slide43": {
@ -61,8 +76,11 @@
"fontsize": 16,
"paperwidth": 2560,
"paperheight": 1920,
"ratio": 100,
"margin": 256,
"vmargin": 192,
"vmargin": 288,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
},
"slide169": {
@ -72,8 +90,11 @@
"fontsize": 16,
"paperwidth": 2560,
"paperheight": 1440,
"ratio": 100,
"margin": 256,
"vmargin": 144,
"vmargin": 216,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
}
}

View file

@ -6,8 +6,11 @@
"fontsize": 12,
"paperwidth": 2480,
"paperheight": 3507,
"ratio": 100,
"margin": 248,
"vmargin": 350,
"vmargin": 525,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
},
"lettre": {
@ -17,8 +20,11 @@
"fontsize": 12,
"paperwidth": 2550,
"paperheight": 3300,
"ratio": 100,
"margin": 255,
"vmargin": 330,
"vmargin": 495,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
},
"linkedin": {
@ -28,8 +34,11 @@
"fontsize": 16,
"paperwidth": 1200,
"paperheight": 1200,
"margin": 120,
"vmargin": 120,
"ratio": 100,
"margin": 180,
"vmargin": 180,
"fps": 15,
"stilltime": 2,
"extension": "jpg"
},
"instagram": {
@ -39,8 +48,11 @@
"fontsize": 16,
"paperwidth": 1080,
"paperheight": 1920,
"ratio": 100,
"margin": 108,
"vmargin": 192,
"vmargin": 244,
"fps": 15,
"stilltime": 2,
"extension": "jpg"
},
"pinterest": {
@ -50,8 +62,11 @@
"fontsize": 16,
"paperwidth": 735,
"paperheight": 1102,
"ratio": 100,
"margin": 75,
"vmargin": 110,
"vmargin": 165,
"fps": 15,
"stilltime": 2,
"extension": "jpg"
},
"slide43": {
@ -61,8 +76,11 @@
"fontsize": 16,
"paperwidth": 2560,
"paperheight": 1920,
"ratio": 100,
"margin": 256,
"vmargin": 192,
"vmargin": 288,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
},
"slide169": {
@ -72,8 +90,11 @@
"fontsize": 16,
"paperwidth": 2560,
"paperheight": 1440,
"ratio": 100,
"margin": 256,
"vmargin": 144,
"vmargin": 216,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
}
}

View file

@ -9,6 +9,8 @@
"ratio": 100,
"margin": 248,
"vmargin": 525,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
},
"lettre": {
@ -21,6 +23,8 @@
"ratio": 100,
"margin": 255,
"vmargin": 495,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
},
"linkedin": {
@ -33,6 +37,8 @@
"ratio": 100,
"margin": 180,
"vmargin": 180,
"fps": 15,
"stilltime": 2,
"extension": "jpg"
},
"instagram": {
@ -45,6 +51,8 @@
"ratio": 100,
"margin": 108,
"vmargin": 244,
"fps": 15,
"stilltime": 2,
"extension": "jpg"
},
"pinterest": {
@ -57,6 +65,8 @@
"ratio": 100,
"margin": 75,
"vmargin": 165,
"fps": 15,
"stilltime": 2,
"extension": "jpg"
},
"slide43": {
@ -69,6 +79,8 @@
"ratio": 100,
"margin": 256,
"vmargin": 288,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
},
"slide169": {
@ -81,6 +93,8 @@
"ratio": 100,
"margin": 256,
"vmargin": 216,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
}
}

View file

@ -6,8 +6,11 @@
"fontsize": 12,
"paperwidth": 2480,
"paperheight": 3507,
"ratio": 100,
"margin": 248,
"vmargin": 350,
"vmargin": 525,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
},
"lettre": {
@ -17,8 +20,11 @@
"fontsize": 12,
"paperwidth": 2550,
"paperheight": 3300,
"ratio": 100,
"margin": 255,
"vmargin": 330,
"vmargin": 495,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
},
"linkedin": {
@ -28,8 +34,11 @@
"fontsize": 16,
"paperwidth": 1200,
"paperheight": 1200,
"margin": 120,
"vmargin": 120,
"ratio": 100,
"margin": 180,
"vmargin": 180,
"fps": 15,
"stilltime": 2,
"extension": "jpg"
},
"instagram": {
@ -39,8 +48,11 @@
"fontsize": 16,
"paperwidth": 1080,
"paperheight": 1920,
"ratio": 100,
"margin": 108,
"vmargin": 192,
"vmargin": 244,
"fps": 15,
"stilltime": 2,
"extension": "jpg"
},
"pinterest": {
@ -50,8 +62,11 @@
"fontsize": 16,
"paperwidth": 735,
"paperheight": 1102,
"ratio": 100,
"margin": 75,
"vmargin": 110,
"vmargin": 165,
"fps": 15,
"stilltime": 2,
"extension": "jpg"
},
"slide43": {
@ -61,8 +76,11 @@
"fontsize": 16,
"paperwidth": 2560,
"paperheight": 1920,
"ratio": 100,
"margin": 256,
"vmargin": 192,
"vmargin": 288,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
},
"slide169": {
@ -72,8 +90,11 @@
"fontsize": 16,
"paperwidth": 2560,
"paperheight": 1440,
"ratio": 100,
"margin": 256,
"vmargin": 144,
"vmargin": 216,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
}
}

View file

@ -17,11 +17,37 @@ Accept: application/zip
"ratio": 100,
"margin": 180,
"vmargin": 180,
"fps": 15,
"stilltime": 2,
"extension": "jpg"
}
###
GET http://127.0.0.1:8000/generer/
Content-Type: application/json
Accept: application/zip
{
"format": "instagram",
"style": "jevalideca",
"linkcolor": "blue",
"tocdepth": 2,
"pdfengine": "lualatex",
"content": "# Ceci est un titre\n## Ceci est un sous-titre\n\nCeci est un paragraphe\n\n## Ceci est un autre sous-titre\n\n> Ceci est du code\n\nCeci est un emoji :heart_eyes:\n\n::: {.center}\nCeci est centré\n:::",
"fontsize": 14,
"paperwidth": 1080,
"paperheight": 1920,
"ratio": 100,
"margin": 180,
"vmargin": 180,
"fps": 15,
"stilltime": 2,
"extension": "mp4"
}
###
GET http://127.0.0.1:8000/generer/
Content-Type: application/json
Accept: application/pdf
@ -39,6 +65,8 @@ Accept: application/pdf
"ratio": 100,
"margin": 90,
"vmargin": 90,
"fps": 15,
"stilltime": 2,
"extension": "pdf"
}
@ -60,8 +88,10 @@ Accept: application/pdf
"paperheight": 1080,
"ratio": 100,
"margin": 180,
"extension": "jpg",
"vmargin": 300
"vmargin": 300,
"fps": 15,
"stilltime": 2,
"extension": "jpg"
}
###
@ -83,11 +113,37 @@ Accept: application/pdf
"ratio": 100,
"margin": 256,
"vmargin": 216,
"fps": 15,
"stilltime": 2,
"extension": "jpg"
}
###
GET http://127.0.0.1:8000/generer/
Content-Type: application/json
Accept: application/pdf
{
"format": "slide169",
"style": "lcm",
"linkcolor": "blue",
"tocdepth": 2,
"pdfengine": "lualatex",
"content": "# Ceci est un titre\n## Ceci est un sous-titre\n\nCeci est un paragraphe\n\n## Ceci est un autre sous-titre\n\n> Ceci est du code\n\nCeci est un emoji :heart_eyes:\n\n::: {.center}\nCeci est centré\n:::",
"fontsize": 14,
"paperwidth": 2560,
"paperheight": 1440,
"ratio": 100,
"margin": 256,
"vmargin": 216,
"fps": 15,
"stilltime": 2,
"extension": "mp4"
}
###
GET http://127.0.0.1:8000/format_parameters/jevalideca/slide169/
Content-Type: application/json
Accept: application/pdf