vidformer 0.1.0__py3-none-any.whl → 0.3.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vidformer
3
- Version: 0.1.0
3
+ Version: 0.3.0
4
4
  Summary: A Python library for creating and viewing videos with vidformer.
5
5
  Author-email: Dominik Winecki <dominikwinecki@gmail.com>
6
6
  Requires-Python: >=3.8
@@ -13,3 +13,10 @@ Requires-Dist: numpy
13
13
 
14
14
  # vidformer-py
15
15
 
16
+ ## Publish
17
+
18
+ ```bash
19
+ export FLIT_USERNAME='__token__' FLIT_PASSWORD='<token>'
20
+ flit publish
21
+ ```
22
+
@@ -0,0 +1,4 @@
1
+ vidformer.py,sha256=hnFxf85IBD1dqaflKYcIE7bp4lYzg5jOCISSyXrvpZs,24520
2
+ vidformer-0.3.0.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
3
+ vidformer-0.3.0.dist-info/METADATA,sha256=38wZlgJ2NUTDZgsegDsJrfF6uLrxRK00Pn9IBRAjEUQ,523
4
+ vidformer-0.3.0.dist-info/RECORD,,
vidformer.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """A Python library for creating and viewing videos with vidformer."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.3.0"
4
4
 
5
5
  import subprocess
6
6
  from fractions import Fraction
@@ -13,6 +13,8 @@ import sys
13
13
  import multiprocessing
14
14
  import uuid
15
15
  import threading
16
+ import gzip
17
+ import base64
16
18
 
17
19
  import requests
18
20
  import msgpack
@@ -33,12 +35,12 @@ def _check_hls_link_exists(url, max_attempts=150, delay=0.1):
33
35
  try:
34
36
  response = requests.get(url)
35
37
  if response.status_code == 200:
36
- return True
38
+ return response.text.strip()
37
39
  else:
38
40
  time.sleep(delay)
39
41
  except requests.exceptions.RequestException as e:
40
42
  time.sleep(delay)
41
- return False
43
+ return None
42
44
 
43
45
 
44
46
  class Spec:
@@ -75,15 +77,15 @@ class Spec:
75
77
  frames.append(frame)
76
78
  return {"frames": frames}, s, f
77
79
 
78
- def play(self, server, keep_spec=False):
80
+ def play(self, server, method="html"):
79
81
  """Play the video live in the notebook."""
80
82
 
81
- from IPython.display import HTML
83
+ from IPython.display import IFrame, HTML
82
84
 
83
- spec_pth = f"spec-{str(uuid.uuid4())}.json"
84
- with open(spec_pth, "w") as outfile:
85
- spec, sources, filters = self._to_json_spec()
86
- outfile.write(json.dumps(spec))
85
+ spec, sources, filters = self._to_json_spec()
86
+ spec_json_bytes = json.dumps(spec).encode("utf-8")
87
+ spec_obj_json_gzip = gzip.compress(spec_json_bytes, compresslevel=1)
88
+ spec_obj_json_gzip_b64 = base64.b64encode(spec_obj_json_gzip).decode("utf-8")
87
89
 
88
90
  sources = [
89
91
  {
@@ -103,18 +105,18 @@ class Spec:
103
105
  }
104
106
  arrays = []
105
107
 
106
- print("Sending to server")
107
- resp = server._new(spec_pth, sources, filters, arrays, self._fmt)
108
+ print(f"Sending to server. Spec is {len(spec_obj_json_gzip_b64)} bytes")
109
+ resp = server._new(spec_obj_json_gzip_b64, sources, filters, arrays, self._fmt)
108
110
  hls_video_url = resp["stream_url"]
111
+ hls_player_url = resp["player_url"]
109
112
  namespace = resp["namespace"]
110
-
111
- if not keep_spec:
112
- os.remove(spec_pth)
113
-
114
113
  hls_js_url = server.hls_js_url()
115
114
 
116
- # We add a namespace to the video element to avoid conflicts with other videos
117
- html_code = f"""
115
+ if method == "iframe":
116
+ return IFrame(hls_player_url, width=1280, height=720)
117
+ if method == "html":
118
+ # We add a namespace to the video element to avoid conflicts with other videos
119
+ html_code = f"""
118
120
  <!DOCTYPE html>
119
121
  <html>
120
122
  <head>
@@ -138,15 +140,23 @@ class Spec:
138
140
  </body>
139
141
  </html>
140
142
  """
141
- return HTML(data=html_code)
143
+ return HTML(data=html_code)
144
+ else:
145
+ return hls_player_url
142
146
 
143
- def save(self, server, pth, keep_spec=False):
147
+ def save(self, server, pth, encoder=None, encoder_opts=None):
144
148
  """Save the video to a file."""
145
149
 
146
- spec_pth = f"spec-{str(uuid.uuid4())}.json"
147
- with open(spec_pth, "w") as outfile:
148
- spec, sources, filters = self._to_json_spec()
149
- outfile.write(json.dumps(spec))
150
+ assert encoder is None or type(encoder) == str
151
+ assert encoder_opts is None or type(encoder_opts) == dict
152
+ if encoder_opts is not None:
153
+ for k, v in encoder_opts:
154
+ assert type(k) == str and type(v) == str
155
+
156
+ spec, sources, filters = self._to_json_spec()
157
+ spec_json_bytes = json.dumps(spec).encode("utf-8")
158
+ spec_obj_json_gzip = gzip.compress(spec_json_bytes, compresslevel=1)
159
+ spec_obj_json_gzip_b64 = base64.b64encode(spec_obj_json_gzip).decode("utf-8")
150
160
 
151
161
  sources = [
152
162
  {
@@ -166,10 +176,16 @@ class Spec:
166
176
  }
167
177
  arrays = []
168
178
 
169
- resp = server._export(pth, spec_pth, sources, filters, arrays, self._fmt)
170
-
171
- if not keep_spec:
172
- os.remove(spec_pth)
179
+ resp = server._export(
180
+ pth,
181
+ spec_obj_json_gzip_b64,
182
+ sources,
183
+ filters,
184
+ arrays,
185
+ self._fmt,
186
+ encoder,
187
+ encoder_opts,
188
+ )
173
189
 
174
190
  return resp
175
191
 
@@ -245,7 +261,7 @@ class Spec:
245
261
  out["dve2_create_spec"] = end_t - start_t
246
262
 
247
263
  start = time.time()
248
- resp = server._export(pth, sources, filters, arrays, self._fmt)
264
+ resp = server._export(pth, sources, filters, arrays, self._fmt, None, None)
249
265
  end = time.time()
250
266
  out["dve2_exec"] = end - start
251
267
  return out
@@ -270,11 +286,19 @@ class YrdenServer:
270
286
  cmd = [bin, "yrden", "--port", str(self._port)]
271
287
  if _in_notebook:
272
288
  # We need to print the URL in the notebook
273
- # This is also a trick to get VS Code to forward the port
289
+ # This is a trick to get VS Code to forward the port
274
290
  cmd += ["--print-url"]
275
291
  self._proc = subprocess.Popen(cmd)
276
292
 
277
- assert _check_hls_link_exists(f"http://{self._domain}:{self._port}/")
293
+ version = _check_hls_link_exists(f"http://{self._domain}:{self._port}/")
294
+ if version is None:
295
+ raise Exception("Failed to connect to server")
296
+
297
+ expected_version = f"vidformer-yrden v{__version__}"
298
+ if version != expected_version:
299
+ print(
300
+ f"Warning: Expected version `{expected_version}`, got `{version}`. API may not be compatible!"
301
+ )
278
302
 
279
303
  def _source(self, name: str, path: str, stream: int, service):
280
304
  r = requests.post(
@@ -310,9 +334,9 @@ class YrdenServer:
310
334
 
311
335
  return r.json()
312
336
 
313
- def _export(self, pth, spec_pth, sources, filters, arrays, fmt):
337
+ def _export(self, pth, spec, sources, filters, arrays, fmt, encoder, encoder_opts):
314
338
  req = {
315
- "spec": spec_pth,
339
+ "spec": spec,
316
340
  "sources": sources,
317
341
  "filters": filters,
318
342
  "arrays": arrays,
@@ -320,6 +344,8 @@ class YrdenServer:
320
344
  "height": fmt["height"],
321
345
  "pix_fmt": fmt["pix_fmt"],
322
346
  "output_path": pth,
347
+ "encoder": encoder,
348
+ "encoder_opts": encoder_opts,
323
349
  }
324
350
 
325
351
  r = requests.post(f"http://{self._domain}:{self._port}/export", json=req)
@@ -345,9 +371,9 @@ class SourceExpr:
345
371
 
346
372
  def __repr__(self):
347
373
  if self._is_iloc:
348
- return f"{self._source.name}.iloc[{self._idx}]"
374
+ return f"{self._source._name}.iloc[{self._idx}]"
349
375
  else:
350
- return f"{self._source.name}[{self._idx}]"
376
+ return f"{self._source._name}[{self._idx}]"
351
377
 
352
378
  def _to_json_spec(self):
353
379
  if self._is_iloc:
@@ -511,6 +537,8 @@ class FilterExpr:
511
537
 
512
538
 
513
539
  class UDF:
540
+ """User-defined filter superclass"""
541
+
514
542
  def __init__(self, name: str):
515
543
  self._name = name
516
544
  self._socket_path = None
@@ -1,4 +0,0 @@
1
- vidformer.py,sha256=bLGZ4zQVkU6etFj0jWuJM8h0sJqcZXwkqA0VfdtCyk0,23257
2
- vidformer-0.1.0.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
3
- vidformer-0.1.0.dist-info/METADATA,sha256=2YNtgTcee8N9LM8lzMvNFLndbcNy88Fqk6mwFYhz8pQ,427
4
- vidformer-0.1.0.dist-info/RECORD,,