python-lottie  0.7.0+dev66cafb9
A framework to work with lottie files and telegram animated stickers (tgs)
tgs_validator.py
Go to the documentation of this file.
1 import os
2 import enum
3 import json
4 import inspect
5 
6 from ..parsers.tgs import parse_tgs
7 from ..objects.base import ObjectVisitor
8 from ..objects.animation import Animation
9 from ..objects import layers
10 from ..objects import shapes
11 from ..objects import helpers
12 
13 
14 class Severity(enum.Enum):
15  Note = enum.auto()
16  Warning = enum.auto()
17  Error = enum.auto()
18 
19 
20 class TgsError:
21  def __init__(self, message, target, severity=Severity.Warning):
22  self.messagemessage = message
23  self.targettarget = target
24  self.severityseverity = severity
25 
26  def target_id(self):
27  if isinstance(self.targettarget, str):
28  return self.targettarget
29  if getattr(self.targettarget, "name", ""):
30  return self.targettarget.name
31  return self.targettarget.__class__.__name__
32 
33  def __str__(self):
34  return "%s: on %s: %s" % (
35  self.severityseverity.name,
36  self.target_idtarget_id(),
37  self.messagemessage
38  )
39 
40 
42  def __init__(self, severity=Severity.Note):
43  self.errorserrors = []
44  self.severityseverity = severity
45 
46  def _check(self, expr, message, target, severity=Severity.Warning):
47  if severity.value >= self.severityseverity.value and not expr:
48  self.errorserrors.append(TgsError(message, target, severity))
49 
50  def check_file_size(self, filename):
51  return self.check_sizecheck_size(os.path.getsize(filename))
52 
53  def check_size(self, bytes, filename="file"):
54  size_k = bytes / 1024
55  self._check_check(
56  size_k <= 64,
57  "Invalid size (%.1fk), should be less than 64k" % size_k,
58  filename,
59  Severity.Error
60  )
61 
62  def check_file(self, filename):
63  self.check_file_sizecheck_file_size(filename)
64  try:
65  self(parse_tgs(filename))
66  except json.decoder.JSONDecodeError as e:
67  self._check_check(
68  False,
69  "Invalid JSON: %s" % e,
70  filename,
71  Severity.Error
72  )
73 
74  def visit(self, object):
75  for cls in inspect.getmro(object.__class__):
76  callback = "_visit_%s" % cls.__name__.lower()
77  if hasattr(self, callback):
78  getattr(self, callback)(object)
79 
80  def _visit_animation(self, o: Animation):
81  self._check_check(
82  o.frame_rate in {30, 60},
83  "Invalid framerate %s, should be 30 or 60" % o.frame_rate,
84  o,
85  Severity.Error
86  )
87  self._check_check(
88  o.width == 512,
89  "Invalid width %s, should be 512" % o.width,
90  o,
91  Severity.Error
92  )
93  self._check_check(
94  o.height == 512,
95  "Invalid height %s, should be 512" % o.height,
96  o,
97  Severity.Error
98  )
99  self._check_check(
100  (o.out_point-o.in_point) <= 180,
101  "Too many frames (%s), should be less than 180" % (o.out_point-o.in_point),
102  o,
103  Severity.Error
104  )
105 
106  def _visit_layer(self, o: layers.Layer):
107  self._check_check(
108  not getattr(o, "has_masks", None) and not o.masks,
109  "Masks are not officially supported",
110  o,
111  Severity.Note
112  )
113  self._check_check(
114  not o.effects,
115  "Effects are not supported",
116  o,
117  Severity.Warning
118  )
119  self._check_check(
120  not o.threedimensional,
121  "3D layers are not supported",
122  o,
123  Severity.Warning
124  )
125  self._check_check(
126  not isinstance(o, layers.TextLayer),
127  "Text layers are not supported",
128  o,
129  Severity.Warning
130  )
131  self._check_check(
132  not isinstance(o, layers.ImageLayer),
133  "Image layers are not supported",
134  o,
135  Severity.Warning
136  )
137  self._check_check(
138  not o.auto_orient,
139  "Auto-orient layers are not supported",
140  o,
141  Severity.Warning
142  )
143  self._check_check(
144  o.matte_mode in {None, layers.MatteMode.Normal},
145  "Mattes are not officially supported",
146  o,
147  Severity.Note
148  )
149 
150  def _visit_precomplayer(self, o: layers.PreCompLayer):
151  self._check_check(
152  o.time_remapping is None,
153  "Time remapping is not supported",
154  o,
155  Severity.Warning
156  )
157 
158  def _visit_merge(self, o: shapes.Merge):
159  self._check_check(
160  False,
161  "Merge paths are not supported",
162  o,
163  Severity.Warning
164  )
165 
166  def _visit_transform(self, o: helpers.Transform):
167  self._check_check(
168  o.skew is None or (
169  not o.skew.animated and o.skew.value == 0
170  ),
171  "Skew transforms are not supported",
172  o,
173  Severity.Warning
174  )
175 
176  def _visit_gradientstroke(self, o: shapes.GradientStroke):
177  self._check_check(
178  False,
179  "Gradient strokes are not officially supported",
180  o,
181  Severity.Note
182  )
183 
184  def _visit_star(self, o: shapes.Star):
185  self._check_check(
186  False,
187  "Star Shapes are not officially supported",
188  o,
189  Severity.Note
190  )
191 
192  def _visit_repeater(self, o: shapes.Repeater):
193  self._check_check(
194  False,
195  "Repeaters are not officially supported",
196  o,
197  Severity.Note
198  )
def __init__(self, message, target, severity=Severity.Warning)
def _check(self, expr, message, target, severity=Severity.Warning)
def check_size(self, bytes, filename="file")
def __init__(self, severity=Severity.Note)
def parse_tgs(filename, encoding="utf-8")
Reads both tgs and lottie files.
Definition: tgs.py:41