diff --git a/moviepy/video/io/html_tools.py b/moviepy/video/io/html_tools.py new file mode 100644 index 000000000..88c23a57e --- /dev/null +++ b/moviepy/video/io/html_tools.py @@ -0,0 +1,181 @@ +""" +This module implements ipython_display +A function to embed images/videos/audio in the IPython Notebook +""" + +# Notes: +# All media are physically embedded in the IPython Notebook +# (instead of simple links to the original files) +# That is because most browsers use a cache system and they won't +# properly refresh the media when the original files are changed. + +import os +from base64 import b64encode +from moviepy.tools import extensions_dict + +try: + from IPython.display import HTML + ipython_available = True +except ImportError: + ipython_available = False + +from .ffmpeg_reader import ffmpeg_parse_infos + +sorry = "Sorry, seems like your browser doesn't support HTML5 audio/video" +templates = {"audio":("
"), + "image":"
", + "video":("
")} + + +def html_embed(clip, filetype=None, maxduration=60, rendering_kwargs=None, + **html_kwargs): + """ Returns HTML5 code embedding the clip + + clip + Either a file name, or a clip to preview. + Either an image, a sound or a video. Clips will actually be + written to a file and embedded as if a filename was provided. + + + filetype: + One of 'video','image','audio'. If None is given, it is determined + based on the extension of ``filename``, but this can bug. + + **kwargs: + Allow you to give some options, like width=260, etc. + + Examples + ========= + + >>> import moviepy.editor as mpy + >>> # later ... + >>> clip.write_videofile("test.mp4") + >>> mpy.ipython_display("test.mp4", width=360) + + >>> clip.audio.write_audiofile('test.ogg') # Sound ! + >>> mpy.ipython_display('test.ogg') + + >>> clip.write_gif("test.gif") + >>> mpy.ipython_display('test.gif') + + >>> clip.save_frame("first_frame.jpeg") + >>> mpy.ipython_display("first_frame.jpeg") + + """ + + if rendering_kwargs is None: + rendering_kwargs = {} + # QUICK AND VERY DIRTY: next step is use "isinstance" with classes. + # But cross-dependencies in modules may make it difficult... + if "Clip" in str(clip.__class__): + TEMP_PREFIX = "__temp__" + if "ImageClip" in str(clip.__class__): + filename = TEMP_PREFIX+".png" + kwargs = {'filename':filename} + kwargs.update(rendering_kwargs) + clip.save_frame(**kwargs) + elif "Video" in str(clip.__class__): + filename = TEMP_PREFIX+".mp4" + kwargs = {'filename':filename, 'verbose':False, 'preset':'ultrafast'} + kwargs.update(rendering_kwargs) + clip.write_videofile(**kwargs) + elif "AudioClip" in str(clip.__class__): + filename = TEMP_PREFIX+".mp3" + kwargs = {'filename': filename, 'verbose':False} + kwargs.update(rendering_kwargs) + clip.write_audiofile(filename, **kwargs) + else: + raise ValueError("Unknown class for the clip. Cannot embed and preview.") + + return html_embed(filename, maxduration=maxduration, rendering_kwargs=rendering_kwargs, + **html_kwargs) + + filename = clip + options = " ".join(["%s='%s'"%(str(k), str(v)) for k,v in html_kwargs.items()]) + name, ext = os.path.splitext(filename) + ext = ext[1:] + + if filetype is None: + ext = filename.split('.')[-1].lower() + if ext == "gif": + filetype = 'image' + elif ext in extensions_dict: + filetype = extensions_dict[ext]['type'] + else: + raise ValueError("No file type is known for the provided file. Please provide " + "argument `filetype` (one of 'image', 'video', 'sound') to the " + "ipython display function.") + + + if filetype== 'video': + # The next lines set the HTML5-cvompatible extension and check that the + # extension is HTML5-valid + exts_htmltype = {'mp4': 'mp4', 'webm':'webm', 'ogv':'ogg'} + allowed_exts = " ".join(exts_htmltype.keys()) + try: + ext = exts_htmltype[ext] + except: + raise ValueError("This video extension cannot be displayed in the " + "IPython Notebook. Allowed extensions: "+allowed_exts) + + if filetype in ['audio', 'video']: + + duration = ffmpeg_parse_infos(filename)['duration'] + if duration > maxduration: + raise ValueError("The duration of video %s (%.1f) exceeds the 'max_duration' "%(filename, duration)+ + "attribute. You can increase 'max_duration', " + "but note that embedding large videos may take all the memory away !") + + with open(filename, "rb") as f: + data= b64encode(f.read()) + + template = templates[filetype] + + return template%{'data':data, 'options':options, 'ext':ext} + + +def ipython_display(clip, filetype=None, maxduration=60, rendering_kwargs=None, + **html_kwargs): + """ + clip + Either the name of a file, or a clip to preview. The clip will + actually be written to a file and embedded as if a filename was provided. + + + filetype: + One of 'video','image','audio'. If None is given, it is determined + based on the extension of ``filename``, but this can bug. + + **kwargs: + Allow you to give some options, like width=260, etc. + + Remarks: If your browser doesn't support HTML5, this should warn you. + If nothing is displayed, maybe your file or filename is wrong. + The media will be physically embedded in the notebook. + + Examples + ========= + + >>> import moviepy.editor as mpy + >>> # later ... + >>> clip.write_videofile("test.mp4") + >>> mpy.ipython_display("test.mp4", width=360) + + >>> clip.audio.write_audiofile('test.ogg') # Sound ! + >>> mpy.ipython_display('test.ogg') + + >>> clip.write_gif("test.gif") + >>> mpy.ipython_display('test.gif') + + >>> clip.save_frame("first_frame.jpeg") + >>> mpy.ipython_display("first_frame.jpeg") + """ + if not ipython_available: + raise ImportError("Only works inside an IPython Notebook") + return HTML(html_embed(clip, filetype=filetype, maxduration=maxduration, + rendering_kwargs=rendering_kwargs, **html_kwargs)) \ No newline at end of file