Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 11f0464

Browse files
committed
Limit size of HTML embedded animations.
This makes both the HTMLWriter and the HTML5 video support check the amount of data being saved into HTML against the rc parameter "animation.embed_limit".
1 parent 34b6ea3 commit 11f0464

File tree

2 files changed

+64
-14
lines changed

2 files changed

+64
-14
lines changed

lib/matplotlib/animation.py

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -912,16 +912,28 @@ def isAvailable(cls):
912912
return True
913913

914914
def __init__(self, fps=30, codec=None, bitrate=None, extra_args=None,
915-
metadata=None, embed_frames=False, default_mode='loop'):
915+
metadata=None, embed_frames=False, default_mode='loop',
916+
embed_limit=None):
916917
self.embed_frames = embed_frames
917918
self.default_mode = default_mode.lower()
918919

920+
# Save embed limit, which is given in MB
921+
if embed_limit is None:
922+
self._bytes_limit = rcParams['animation.embed_limit']
923+
else:
924+
self._bytes_limit = embed_limit
925+
926+
# Convert from MB to bytes
927+
self._bytes_limit *= 1024 * 1024
928+
919929
if self.default_mode not in ['loop', 'once', 'reflect']:
920930
self.default_mode = 'loop'
921931
import warnings
922932
warnings.warn("unrecognized default_mode: using 'loop'")
923933

924934
self._saved_frames = list()
935+
self._total_bytes = 0
936+
self._hit_limit = False
925937
super(HTMLWriter, self).__init__(fps, codec, bitrate,
926938
extra_args, metadata)
927939

@@ -943,13 +955,27 @@ def setup(self, fig, outfile, dpi, frame_dir=None):
943955

944956
def grab_frame(self, **savefig_kwargs):
945957
if self.embed_frames:
958+
# Just stop processing if we hit the limit
959+
if self._hit_limit:
960+
return
946961
suffix = '.' + self.frame_format
947962
f = InMemory()
948963
self.fig.savefig(f, format=self.frame_format,
949964
dpi=self.dpi, **savefig_kwargs)
950965
f.seek(0)
951966
imgdata64 = encodebytes(f.read()).decode('ascii')
952-
self._saved_frames.append(imgdata64)
967+
self._total_bytes += len(imgdata64)
968+
if self._total_bytes >= self._bytes_limit:
969+
warnings.warn("Animation size has reached {0._total_bytes} "
970+
"bytes, exceeding the limit of "
971+
"{0._bytes_limit}. If you're sure you want "
972+
"a larger animation embedded, set the "
973+
"animation.embed_limit rc parameter to a "
974+
"larger value (in MB). This and further frames"
975+
" will be dropped.".format(self))
976+
self._hit_limit = True
977+
else:
978+
self._saved_frames.append(imgdata64)
953979
else:
954980
return super(HTMLWriter, self).grab_frame(**savefig_kwargs)
955981

@@ -1354,7 +1380,7 @@ def _end_redraw(self, evt):
13541380
self._resize_id = self._fig.canvas.mpl_connect('resize_event',
13551381
self._handle_resize)
13561382

1357-
def to_html5_video(self):
1383+
def to_html5_video(self, embed_limit=None):
13581384
'''Returns animation as an HTML5 video tag.
13591385
13601386
This saves the animation as an h264 video, encoded in base64
@@ -1369,6 +1395,13 @@ def to_html5_video(self):
13691395
</video>'''
13701396
# Cache the rendering of the video as HTML
13711397
if not hasattr(self, '_base64_video'):
1398+
# Save embed limit, which is given in MB
1399+
if embed_limit is None:
1400+
embed_limit = rcParams['animation.embed_limit']
1401+
1402+
# Convert from MB to bytes
1403+
embed_limit *= 1024 * 1024
1404+
13721405
# First write the video to a tempfile. Set delete to False
13731406
# so we can re-open to read binary data.
13741407
with tempfile.NamedTemporaryFile(suffix='.m4v',
@@ -1384,22 +1417,36 @@ def to_html5_video(self):
13841417
# Now open and base64 encode
13851418
with open(f.name, 'rb') as video:
13861419
vid64 = encodebytes(video.read())
1387-
self._base64_video = vid64.decode('ascii')
1388-
self._video_size = 'width="{0}" height="{1}"'.format(
1389-
*writer.frame_size)
1420+
vid_len = len(vid64)
1421+
if vid_len >= embed_limit:
1422+
warnings.warn("Animation movie is {} bytes, exceeding "
1423+
"the limit of {}. If you're sure you want a "
1424+
"large animation embedded, set the "
1425+
"animation.embed_limit rc parameter to a "
1426+
"larger value (in MB).".format(vid_len,
1427+
embed_limit))
1428+
else:
1429+
self._base64_video = vid64.decode('ascii')
1430+
self._video_size = 'width="{}" height="{}"'.format(
1431+
*writer.frame_size)
13901432

13911433
# Now we can remove
13921434
os.remove(f.name)
13931435

1394-
# Default HTML5 options are to autoplay and to display video controls
1395-
options = ['controls', 'autoplay']
1436+
# If we exceeded the size, this attribute won't exist
1437+
if hasattr(self, '_base64_video'):
1438+
# Default HTML5 options are to autoplay and display video controls
1439+
options = ['controls', 'autoplay']
13961440

1397-
# If we're set to repeat, make it loop
1398-
if self.repeat:
1399-
options.append('loop')
1400-
return VIDEO_TAG.format(video=self._base64_video,
1401-
size=self._video_size,
1402-
options=' '.join(options))
1441+
# If we're set to repeat, make it loop
1442+
if hasattr(self, 'repeat') and self.repeat:
1443+
options.append('loop')
1444+
1445+
return VIDEO_TAG.format(video=self._base64_video,
1446+
size=self._video_size,
1447+
options=' '.join(options))
1448+
else:
1449+
return 'Video too large to embed.'
14031450

14041451
def to_jshtml(self, fps=None, embed_frames=True, default_mode=None):
14051452
"""Generate HTML representation of the animation"""

lib/matplotlib/rcsetup.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,6 +1380,9 @@ def _validate_linestyle(ls):
13801380

13811381
# Animation settings
13821382
'animation.html': ['none', validate_movie_html_fmt],
1383+
# Limit, in MB, of size of base64 encoded animation in HTML
1384+
# (i.e. IPython notebook)
1385+
'animation.embed_limit': [20, validate_float],
13831386
'animation.writer': ['ffmpeg', validate_movie_writer],
13841387
'animation.codec': ['h264', six.text_type],
13851388
'animation.bitrate': [-1, validate_int],

0 commit comments

Comments
 (0)