Browse Source

upload source

master
龚敬洋 4 years ago
commit
9feec0da40
51 changed files with 686 additions and 0 deletions
  1. BIN
      .DS_Store
  2. +157
    -0
      README.md
  3. BIN
      deploy/.DS_Store
  4. +119
    -0
      deploy/.gitignore
  5. +172
    -0
      deploy/app.py
  6. +62
    -0
      deploy/code_generator.py
  7. BIN
      deploy/database/.DS_Store
  8. +51
    -0
      deploy/manim_utils.py
  9. +14
    -0
      deploy/schema.sql
  10. BIN
      deploy/static/.DS_Store
  11. BIN
      deploy/static/M.jpg
  12. +2
    -0
      deploy/static/css/app.00d3e5ab0c8ff68c4d80fa9fef504276.css
  13. +1
    -0
      deploy/static/css/app.00d3e5ab0c8ff68c4d80fa9fef504276.css.map
  14. +2
    -0
      deploy/static/css/app.0262e48e0178e703c95cb55853f483b8.css
  15. +1
    -0
      deploy/static/css/app.0262e48e0178e703c95cb55853f483b8.css.map
  16. +2
    -0
      deploy/static/css/app.100150391159bf7906c7bd7273b231f3.css
  17. +1
    -0
      deploy/static/css/app.100150391159bf7906c7bd7273b231f3.css.map
  18. +2
    -0
      deploy/static/css/app.38f08d6cf96cab8c33f1bd4fb26d9434.css
  19. +1
    -0
      deploy/static/css/app.38f08d6cf96cab8c33f1bd4fb26d9434.css.map
  20. +2
    -0
      deploy/static/css/app.50f6ae3a5f7ccb19d97421bbbce6b070.css
  21. +1
    -0
      deploy/static/css/app.50f6ae3a5f7ccb19d97421bbbce6b070.css.map
  22. +2
    -0
      deploy/static/css/app.93d4c713b30138127fe8f7f0d237ecd6.css
  23. +1
    -0
      deploy/static/css/app.93d4c713b30138127fe8f7f0d237ecd6.css.map
  24. +2
    -0
      deploy/static/css/app.e3820924ee579536348e1dd361736c70.css
  25. +1
    -0
      deploy/static/css/app.e3820924ee579536348e1dd361736c70.css.map
  26. BIN
      deploy/static/fonts/element-icons.535877f.woff
  27. BIN
      deploy/static/fonts/element-icons.732389d.ttf
  28. BIN
      deploy/static/img/background.3ab2329.jpg
  29. BIN
      deploy/static/img/manim.452da8b.jpg
  30. +2
    -0
      deploy/static/js/app.125d8e1c1f6635f75758.js
  31. +1
    -0
      deploy/static/js/app.125d8e1c1f6635f75758.js.map
  32. +2
    -0
      deploy/static/js/app.408eaaf9dca7fc80cc53.js
  33. +1
    -0
      deploy/static/js/app.408eaaf9dca7fc80cc53.js.map
  34. +2
    -0
      deploy/static/js/app.7ae722821be0ba88f848.js
  35. +1
    -0
      deploy/static/js/app.7ae722821be0ba88f848.js.map
  36. +2
    -0
      deploy/static/js/app.91cf074aafe97effc4ff.js
  37. +1
    -0
      deploy/static/js/app.91cf074aafe97effc4ff.js.map
  38. +2
    -0
      deploy/static/js/app.c3f7bbbebc661eea46e6.js
  39. +1
    -0
      deploy/static/js/app.c3f7bbbebc661eea46e6.js.map
  40. +2
    -0
      deploy/static/js/app.d987415e474510f5328b.js
  41. +1
    -0
      deploy/static/js/app.d987415e474510f5328b.js.map
  42. +2
    -0
      deploy/static/js/app.e7c331c73aa63c8e4a0f.js
  43. +1
    -0
      deploy/static/js/app.e7c331c73aa63c8e4a0f.js.map
  44. +2
    -0
      deploy/static/js/manifest.3ad1d5771e9b13dbdad2.js
  45. +1
    -0
      deploy/static/js/manifest.3ad1d5771e9b13dbdad2.js.map
  46. +27
    -0
      deploy/static/js/vendor.b1e79088973ebca1a446.js
  47. +1
    -0
      deploy/static/js/vendor.b1e79088973ebca1a446.js.map
  48. BIN
      deploy/templates/.DS_Store
  49. +1
    -0
      deploy/templates/index.html
  50. +12
    -0
      deploy/templates/video_presentator.html
  51. +25
    -0
      deploy/video_generator.py

BIN
.DS_Store View File


+ 157
- 0
README.md View File

@ -0,0 +1,157 @@
# ManimOnline
## 简介
**Manim**(**Mathematical Animation Engine**)是由知名Youtube频道3Blue1Brown的运营者**Grant Sanderson**开发的一个数学动画制作引擎。它可以让用户通过编程的方式精准的生成解释性的数学动 画,以帮助观众更加直观的理解晦涩难懂的数学概念。
随着Manim功能的扩展,现如今许多科普视频作者都会使用Manim工具制作动画来帮助更直观的解释专 业的知识。然而,使用Manim工具需要通过**Python**语言编写脚本来控制动画中的一切元素与逻辑,这 就使得该工具具有一定的使用⻔槛。此外,Manim渲染和生成动画需要占用一定的系统资源,当画面中元素较为复杂时,就会对用户的硬件系统造成不小的压力。本项目旨在通过网⻚UI交互的形式,让用户能够直观的创建动画中所需的元素,并通过云端渲染快速得到最终的科普动画。
## 部署方式
### 使用UCloud镜像部署
UCloud镜像:
| 名称 | ManimOnline |
| ------ | --------------- |
| ID | uimage-d5ups52d |
| 用户名 | ubuntu |
| 密码 | abcd123456 |
若要使用镜像部署本项目,请将镜像恢复到一台云主机上,随后输入如下命令启动服务:**(服务器镜像中装有screen工具,可创建一个独立的screen使得服务可以在后台持续运行)**
```shell
> sudo su
> cd /home/ubuntu/manim_backend/
> export FLASK_APP=app.py
> export FLASK_ENV=development
> flask run --host 0.0.0.0 --port 80
```
### 自行部署
若要在云主机上部署本项目,请使用Ubuntu或其他带有apt-get工具的系统。
由于本项目使用了Manim作为动画生成工具,因此请先安装必要的系统依赖库:
```shell
> apt install sox ffmpeg libcairo2 libcairo2-dev texlive-full
```
请确保系统中装有python环境及pip工具,并通过pip工具安装Flask环境及Manim库:
```shell
> pip3 install Flask
> pip3 install Flask-Cors
> pip3 install manimlib
```
请使用如下命令启动服务:
```shell
> export FLASK_RUN=app.py
> export FLASK_ENV=development
> flask run --host 0.0.0.0 --port 80
```
## 使用说明
若要使用**交互式方法**生成动画,您可以通过界面上半部分的组件来创建动画逻辑。为了演示起见,我们在网页中预设了一组动画生成逻辑,您只需要在对应位置**输入画布名称**并**选择画质**即可直接提交生成。随后您需要稍等片刻,网站会自动跳转到视频播放界面**(后台可以看到视频生成进度)**。
若要直接使用**Python代码**生成动画,您可以直接在界面下半部分的代码框中输入Python代码,输入画布名称并选择画质即可提交生成**(请注意,画布名称需与场景类名一致!)**。随后稍等片刻,网站同样会自动跳转到对应的视频播放界面。
您可以使用如下的Python代码进行较为复杂的动画生成测试:**(该场景元素较为复杂,在常规云主机上可能需要花费1~2分钟左右进行视频生成,此时前端无任何反应属正常现象,请耐心等待,同时也可以通过后台追踪生成进度)**
```python
from manimlib.imports import *
# Test即为画布名
class Test(Scene):
def construct(self):
title = TextMobject("Hello World!") # 文字
basel = TexMobject( # 公式
"\\sigma (x) = \\frac{1}{1+e^{-x}}"
)
VGroup(title, basel).arrange(DOWN) # 集合到一起后排列位置
self.play(
Write(title), # "写"出title文字
FadeInFrom(basel, UP), # 将basel公式从上方淡入
)
self.wait() # 停顿一秒
transform_title = TextMobject("That was a transform")
transform_title.to_corner(UP + LEFT) # 放到最左上角
self.play(
Transform(title, transform_title), # 将title变换为transform_title
LaggedStart(*map(FadeOutAndShiftDown, basel)), # 将basel公式中的每个字符依次从下方淡出
)
self.wait() # 停顿一秒
grid = NumberPlane() # 构建一个坐标平面
grid_title = TextMobject("This is a grid")
grid_title.scale(1.5)
grid_title.move_to(transform_title)
self.add(grid, grid_title) # 确保grid_title在grid上方
self.play(
FadeOut(title), # 淡出title
FadeInFromDown(grid_title), # 从下方淡入grid_title
ShowCreation(grid, run_time=3, lag_ratio=0.1), # 创建grid的动画,时长为3,延迟为10%
)
self.wait()
grid_transform_title = TextMobject(
"That was a non-linear function \\\\"
"applied to the grid"
)
grid_transform_title.move_to(grid_title, UL)
grid.prepare_for_nonlinear_transform() # 让grid准备进行非线性变换
self.play(
grid.apply_function, # 对grid施加一个函数,实现非线性变换
lambda p: p + np.array([ # 输入值为一个点,返回值也为一个点
np.sin(p[1]),
np.sin(p[0]),
0,
]),
run_time=3,
)
self.wait()
self.play(
Transform(grid_title, grid_transform_title)
)
self.wait()
```
## 开发环境配置
### 后端
本项目使用Flask作为后端框架,因此仅需使用带有Flask的python3环境打开deploy目录即可构建后端开发环境。其他系统及库依赖与部署环境一致。
### 前端
本项目使用了Vue Cli作为前端生成框架,若要搭建前端开发环境,请先在系统中安装Vue-Cli 3脚手架及相应的Node JS工具。随后在系统中使用如下命令安装必要的依赖库:
```shell
> npm install axios --save-dev
> npm install element-ui -S
```
如若该vue文件还未部署到服务器上,需把**src/components**中所有涉及到的地址与**axios请求**中的url前加上后端地址,这样就可以与后端产生交互。
请使用如下命令生成dist文件夹,并将其中对应的文件放入后端对应的**templates**及**static**目录内,即可将构建完成的前端网页部署到网站中:
```shell
> npm run build
```
## 开发者说明
本项目尚处于开发初级阶段,网站中可能存在些许BUG(尤其是前端交互界面),还请多多谅解!
如有任何问题或建议,请联系我们:
Github:[GONGGONGJOHN](https://github.com/gonggongjohn)
WeChat: gonggongjohn

BIN
deploy/.DS_Store View File


+ 119
- 0
deploy/.gitignore View File

@ -0,0 +1,119 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
/test/
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
.idea/
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/

+ 172
- 0
deploy/app.py View File

@ -0,0 +1,172 @@
from flask import Flask
from flask import request
from flask import render_template
from flask_cors import cross_origin
from manim_utils import ManimHelper
from code_generator import CodeGenerator
from video_generator import generate_video
import json
import sqlite3
import os
app = Flask(__name__)
DATABASE = 'database/om.db'
@app.before_first_request
def create_db():
if not os.path.exists(DATABASE):
connection = sqlite3.connect(DATABASE)
cursor = connection.cursor()
with app.open_resource('schema.sql') as f:
cursor.executescript(f.read().decode('utf8'))
connection.commit()
def get_db():
db = sqlite3.connect(DATABASE)
db.row_factory = sqlite3.Row
return db
def db_operate(command):
db = get_db()
cur = db.execute(command)
db.commit()
result = cur.fetchall()
db.close()
return result
@app.route('/')
@cross_origin()
def root():
return render_template('index.html')
@app.route('/register')
@cross_origin()
def register():
username = request.args.get('username')
password = request.args.get('password')
sql_user_str = 'SELECT username FROM user WHERE username=\'{0}\''.format(username)
result = db_operate(sql_user_str)
if len(result) == 0:
sql_str = 'INSERT INTO user (username, password) VALUES (\'{0}\', \'{1}\')'.format(username, password)
db_operate(sql_str)
return json.dumps({'status': 'successful'})
else:
return json.dumps({'status': 'fail'})
@app.route('/login')
@cross_origin()
def login():
username = request.args.get('username')
password = request.args.get('password')
sql_str = 'SELECT password FROM user WHERE username=\'{0}\''.format(username)
result = db_operate(sql_str)
if len(result) != 0:
if password == result[0][0]:
sql_dir_str = 'SELECT name FROM directory WHERE owner=\'{0}\''.format(username)
result_dir = db_operate(sql_dir_str)
name_list = []
for name in result_dir:
name_list.append(name[0])
print(result_dir)
return json.dumps({'status': 'successful', 'dir': name_list})
else:
return json.dumps({'status': 'fail'})
else:
return json.dumps({'status': 'fail'})
@app.route('/class')
@cross_origin()
def get_class():
manim_helper = ManimHelper()
class_list = manim_helper.get_class_list()
return json.dumps(class_list)
@app.route('/param')
@cross_origin()
def get_param():
function = request.args.get('function')
manim_helper = ManimHelper()
arguments = manim_helper.get_arguments(function)
return json.dumps(arguments)
@app.route('/function')
@cross_origin()
def get_function():
class_name = request.args.get('class')
manim_helper = ManimHelper()
functions = manim_helper.get_function(str(class_name))
return json.dumps(functions)
@app.route('/generate', methods=['POST'])
@cross_origin()
def generate():
codes = json.loads(request.get_data())
username = codes['username']
scene_name = codes['scene_name']
quality = codes['quality']
sql_str = 'SELECT id FROM directory WHERE owner=\'{0}\' AND name=\'{1}\''.format(username, scene_name)
result = db_operate(sql_str)
if len(result) == 0:
sql_create_str = 'INSERT INTO directory (owner, name) VALUES (\'{0}\', \'{1}\')'.format(username, scene_name)
db_operate(sql_create_str)
result = db_operate(sql_str)
directory_name = result[0][0]
code_generator = CodeGenerator()
if codes['mode'] == 'interactive':
code_generator.assemble_code(codes)
# Put to user directory
directory_path = 'static/{0}/'.format(directory_name)
code_file_path = 'static/{0}/{0}.py'.format(directory_name)
video_file_path = 'static/{0}/'.format(directory_name)
asset_file_path = 'static/{0}/'.format(directory_name)
video_name = scene_name
if codes['mode'] == 'interactive':
code_generator.to_file(directory_path, code_file_path)
elif codes['mode'] == 'python':
code_generator.py_to_file(codes['codes'], directory_path, code_file_path)
w, h = generate_video(code_file_path, asset_file_path, video_file_path, video_name, quality)
return json.dumps({'status': 'successful'})
@app.route('/video')
@cross_origin()
def show_video():
username = request.args.get('username')
scene_name = request.args.get('scene_name')
quality = request.args.get('quality')
sql_str = 'SELECT id FROM directory WHERE owner=\'{0}\' AND name=\'{1}\''.format(username, scene_name)
result = db_operate(sql_str)
if len(result) == 0:
return json.dumps({'status': 'fail'})
directory_name = result[0][0]
video_file_path = 'static/{0}/'.format(directory_name)
video_name = scene_name
w = 2560
h = 1440
if quality == 'w':
w = 2560
h = 1440
elif quality == 'h':
w = 1920
h = 1080
elif quality == 'm':
w = 1280
h = 720
elif quality == 'l':
w = 854
h = 480
return render_template('video_presentator.html', width=w, height=h, video=video_file_path + video_name + '.mp4')
if __name__ == '__main__':
app.run()

+ 62
- 0
deploy/code_generator.py View File

@ -0,0 +1,62 @@
import json
import os
class CodeGenerator:
def __init__(self):
self.import_str = 'from manimlib.imports import *'
self.class_declare_template = 'class {0}(Scene):'
self.construct_str = 'def construct(self):'
self.indent = ' '
self.code_list = []
self.wait_str = 'self.wait()'
def assemble_code(self, codes):
self.code_list.append(self.import_str)
class_declare = self.class_declare_template.format(codes['scene_name'])
self.code_list.append(class_declare)
self.code_list.append(self.indent + self.construct_str)
indent_cur = 2
for item in codes['codes']:
param_list = []
for param in item['params']:
if param == 'varargs':
vararg_list = []
for sub_param in item['params'][param]:
if sub_param['type'] == 'string':
vararg_list.append('\"' + sub_param['content'] + '\"')
elif sub_param['type'] == 'instance':
vararg_list.append(sub_param['content'])
param_list.extend(vararg_list)
else:
if item['params'][param]['type'] == 'string':
param_display = '\'' + item['params'][param]['content'] + '\''
elif item['params'][param]['type'] == 'constant':
param_display = item['params'][param]['content']
else:
param_display = str(item['params'][param]['content'])
param_item = param + '=' + param_display
param_list.append(param_item)
param_str = ', '.join(param_list)
if item['type'] == 'create_instance':
construct_str = item['class'] + '(' + param_str + ')'
line = self.indent * indent_cur + item['name'] + ' = ' + construct_str
self.code_list.append(line)
elif item['type'] == 'call_function':
function_str = item['function'] + '(' + param_str + ')'
line = self.indent * indent_cur + item['instance'] + '.' + function_str
self.code_list.append(line)
self.code_list.append(self.indent * indent_cur + self.wait_str)
def to_file(self, directory_path, file_path):
if not os.path.exists(directory_path):
os.mkdir(directory_path)
with open(file_path, 'w') as file:
for code in self.code_list:
file.write(code + '\n')
def py_to_file(self, code_str, directory_path, file_path):
if not os.path.exists(directory_path):
os.mkdir(directory_path)
with open(file_path, 'w') as file:
file.write(code_str)

BIN
deploy/database/.DS_Store View File


+ 51
- 0
deploy/manim_utils.py View File

@ -0,0 +1,51 @@
import manimlib.imports
import inspect
import json
class ManimHelper:
def hierarchical_phase(self, class_dict, module_list, class_name):
if len(module_list) == 1:
if module_list[0] not in class_dict:
class_dict[module_list[0]] = []
class_dict[module_list[0]].append(class_name)
else:
if module_list[0] not in class_dict:
class_dict[module_list[0]] = {}
tmp_key = module_list.pop(0)
class_dict[tmp_key] = self.hierarchical_phase(class_dict[tmp_key], module_list, class_name)
return class_dict
def get_class_list(self):
members = inspect.getmembers(manimlib.imports, inspect.isclass)
class_dict = {}
for (member_name, member_type) in members:
module_list = str(member_type.__module__).split('.')
if module_list[0] == 'manimlib':
class_dict = self.hierarchical_phase(class_dict, module_list, member_name)
return class_dict
def get_arguments(self, name):
arg_spec = inspect.getfullargspec(eval(name))
print(arg_spec)
arg_name = arg_spec.args
if arg_name[0] == 'self':
arg_name.pop(0)
arg_default = arg_spec.defaults
arguments = []
for i in range(0, len(arg_name)):
instance_dict = {'name': arg_name[i], 'default': 0}
arguments.append(instance_dict)
return {'arguments': arguments}
def get_function(self, class_name):
functions = inspect.getmembers(eval(class_name), inspect.isfunction)
function_list = []
for (function, _) in functions:
if function[0:2] != '__':
function_list.append(function)
return {'functions': function_list}
# manim_helper = ManimHelper()
# print(manim_helper.get_function('manimlib.mobject.svg.tex_mobject.TextMobject'))

+ 14
- 0
deploy/schema.sql View File

@ -0,0 +1,14 @@
DROP TABLE IF EXISTS user;
DROP TABLE IF EXISTS directory;
CREATE TABLE user(
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);
CREATE TABLE directory(
id INTEGER PRIMARY KEY AUTOINCREMENT,
owner TEXT NOT NULL,
name TEXT NOT NULL
);

BIN
deploy/static/.DS_Store View File


BIN
deploy/static/M.jpg View File

Before After
Width: 500  |  Height: 600  |  Size: 36 KiB

+ 2
- 0
deploy/static/css/app.00d3e5ab0c8ff68c4d80fa9fef504276.css
File diff suppressed because it is too large
View File


+ 1
- 0
deploy/static/css/app.00d3e5ab0c8ff68c4d80fa9fef504276.css.map
File diff suppressed because it is too large
View File


+ 2
- 0
deploy/static/css/app.0262e48e0178e703c95cb55853f483b8.css
File diff suppressed because it is too large
View File


+ 1
- 0
deploy/static/css/app.0262e48e0178e703c95cb55853f483b8.css.map
File diff suppressed because it is too large
View File


+ 2
- 0
deploy/static/css/app.100150391159bf7906c7bd7273b231f3.css
File diff suppressed because it is too large
View File


+ 1
- 0
deploy/static/css/app.100150391159bf7906c7bd7273b231f3.css.map
File diff suppressed because it is too large
View File


+ 2
- 0
deploy/static/css/app.38f08d6cf96cab8c33f1bd4fb26d9434.css
File diff suppressed because it is too large
View File


+ 1
- 0
deploy/static/css/app.38f08d6cf96cab8c33f1bd4fb26d9434.css.map
File diff suppressed because it is too large
View File


+ 2
- 0
deploy/static/css/app.50f6ae3a5f7ccb19d97421bbbce6b070.css
File diff suppressed because it is too large
View File


+ 1
- 0
deploy/static/css/app.50f6ae3a5f7ccb19d97421bbbce6b070.css.map
File diff suppressed because it is too large
View File


+ 2
- 0
deploy/static/css/app.93d4c713b30138127fe8f7f0d237ecd6.css
File diff suppressed because it is too large
View File


+ 1
- 0
deploy/static/css/app.93d4c713b30138127fe8f7f0d237ecd6.css.map
File diff suppressed because it is too large
View File


+ 2
- 0
deploy/static/css/app.e3820924ee579536348e1dd361736c70.css
File diff suppressed because it is too large
View File


+ 1
- 0
deploy/static/css/app.e3820924ee579536348e1dd361736c70.css.map
File diff suppressed because it is too large
View File


BIN
deploy/static/fonts/element-icons.535877f.woff View File


BIN
deploy/static/fonts/element-icons.732389d.ttf View File


BIN
deploy/static/img/background.3ab2329.jpg View File

Before After
Width: 2560  |  Height: 1440  |  Size: 457 KiB

BIN
deploy/static/img/manim.452da8b.jpg View File

Before After
Width: 1900  |  Height: 684  |  Size: 119 KiB

+ 2
- 0
deploy/static/js/app.125d8e1c1f6635f75758.js
File diff suppressed because it is too large
View File


+ 1
- 0
deploy/static/js/app.125d8e1c1f6635f75758.js.map
File diff suppressed because it is too large
View File


+ 2
- 0
deploy/static/js/app.408eaaf9dca7fc80cc53.js
File diff suppressed because it is too large
View File


+ 1
- 0
deploy/static/js/app.408eaaf9dca7fc80cc53.js.map
File diff suppressed because it is too large
View File


+ 2
- 0
deploy/static/js/app.7ae722821be0ba88f848.js
File diff suppressed because it is too large
View File


+ 1
- 0
deploy/static/js/app.7ae722821be0ba88f848.js.map
File diff suppressed because it is too large
View File


+ 2
- 0
deploy/static/js/app.91cf074aafe97effc4ff.js
File diff suppressed because it is too large
View File


+ 1
- 0
deploy/static/js/app.91cf074aafe97effc4ff.js.map
File diff suppressed because it is too large
View File


+ 2
- 0
deploy/static/js/app.c3f7bbbebc661eea46e6.js
File diff suppressed because it is too large
View File


+ 1
- 0
deploy/static/js/app.c3f7bbbebc661eea46e6.js.map
File diff suppressed because it is too large
View File


+ 2
- 0
deploy/static/js/app.d987415e474510f5328b.js
File diff suppressed because it is too large
View File


+ 1
- 0
deploy/static/js/app.d987415e474510f5328b.js.map
File diff suppressed because it is too large
View File


+ 2
- 0
deploy/static/js/app.e7c331c73aa63c8e4a0f.js
File diff suppressed because it is too large
View File


+ 1
- 0
deploy/static/js/app.e7c331c73aa63c8e4a0f.js.map
File diff suppressed because it is too large
View File


+ 2
- 0
deploy/static/js/manifest.3ad1d5771e9b13dbdad2.js View File

@ -0,0 +1,2 @@
!function(r){var n=window.webpackJsonp;window.webpackJsonp=function(e,u,c){for(var f,i,p,a=0,l=[];a<e.length;a++)i=e[a],o[i]&&l.push(o[i][0]),o[i]=0;for(f in u)Object.prototype.hasOwnProperty.call(u,f)&&(r[f]=u[f]);for(n&&n(e,u,c);l.length;)l.shift()();if(c)for(a=0;a<c.length;a++)p=t(t.s=c[a]);return p};var e={},o={2:0};function t(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return r[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=r,t.c=e,t.d=function(r,n,e){t.o(r,n)||Object.defineProperty(r,n,{configurable:!1,enumerable:!0,get:e})},t.n=function(r){var n=r&&r.__esModule?function(){return r.default}:function(){return r};return t.d(n,"a",n),n},t.o=function(r,n){return Object.prototype.hasOwnProperty.call(r,n)},t.p="./",t.oe=function(r){throw console.error(r),r}}([]);
//# sourceMappingURL=manifest.3ad1d5771e9b13dbdad2.js.map

+ 1
- 0
deploy/static/js/manifest.3ad1d5771e9b13dbdad2.js.map View File

@ -0,0 +1 @@
{"version":3,"sources":["webpack:///webpack/bootstrap 33d76d614ad607f8d328"],"names":["parentJsonpFunction","window","chunkIds","moreModules","executeModules","moduleId","chunkId","result","i","resolves","length","installedChunks","push","Object","prototype","hasOwnProperty","call","modules","shift","__webpack_require__","s","installedModules","2","exports","module","l","m","c","d","name","getter","o","defineProperty","configurable","enumerable","get","n","__esModule","object","property","p","oe","err","console","error"],"mappings":"aACA,IAAAA,EAAAC,OAAA,aACAA,OAAA,sBAAAC,EAAAC,EAAAC,GAIA,IADA,IAAAC,EAAAC,EAAAC,EAAAC,EAAA,EAAAC,KACQD,EAAAN,EAAAQ,OAAoBF,IAC5BF,EAAAJ,EAAAM,GACAG,EAAAL,IACAG,EAAAG,KAAAD,EAAAL,GAAA,IAEAK,EAAAL,GAAA,EAEA,IAAAD,KAAAF,EACAU,OAAAC,UAAAC,eAAAC,KAAAb,EAAAE,KACAY,EAAAZ,GAAAF,EAAAE,IAIA,IADAL,KAAAE,EAAAC,EAAAC,GACAK,EAAAC,QACAD,EAAAS,OAAAT,GAEA,GAAAL,EACA,IAAAI,EAAA,EAAYA,EAAAJ,EAAAM,OAA2BF,IACvCD,EAAAY,IAAAC,EAAAhB,EAAAI,IAGA,OAAAD,GAIA,IAAAc,KAGAV,GACAW,EAAA,GAIA,SAAAH,EAAAd,GAGA,GAAAgB,EAAAhB,GACA,OAAAgB,EAAAhB,GAAAkB,QAGA,IAAAC,EAAAH,EAAAhB,IACAG,EAAAH,EACAoB,GAAA,EACAF,YAUA,OANAN,EAAAZ,GAAAW,KAAAQ,EAAAD,QAAAC,IAAAD,QAAAJ,GAGAK,EAAAC,GAAA,EAGAD,EAAAD,QAKAJ,EAAAO,EAAAT,EAGAE,EAAAQ,EAAAN,EAGAF,EAAAS,EAAA,SAAAL,EAAAM,EAAAC,GACAX,EAAAY,EAAAR,EAAAM,IACAhB,OAAAmB,eAAAT,EAAAM,GACAI,cAAA,EACAC,YAAA,EACAC,IAAAL,KAMAX,EAAAiB,EAAA,SAAAZ,GACA,IAAAM,EAAAN,KAAAa,WACA,WAA2B,OAAAb,EAAA,SAC3B,WAAiC,OAAAA,GAEjC,OADAL,EAAAS,EAAAE,EAAA,IAAAA,GACAA,GAIAX,EAAAY,EAAA,SAAAO,EAAAC,GAAsD,OAAA1B,OAAAC,UAAAC,eAAAC,KAAAsB,EAAAC,IAGtDpB,EAAAqB,EAAA,KAGArB,EAAAsB,GAAA,SAAAC,GAA8D,MAApBC,QAAAC,MAAAF,GAAoBA","file":"static/js/manifest.3ad1d5771e9b13dbdad2.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tvar parentJsonpFunction = window[\"webpackJsonp\"];\n \twindow[\"webpackJsonp\"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [], result;\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n \t\tif(executeModules) {\n \t\t\tfor(i=0; i < executeModules.length; i++) {\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = executeModules[i]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t};\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// objects to store loaded and loading chunks\n \tvar installedChunks = {\n \t\t2: 0\n \t};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"./\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 33d76d614ad607f8d328"],"sourceRoot":""}

+ 27
- 0
deploy/static/js/vendor.b1e79088973ebca1a446.js
File diff suppressed because it is too large
View File


+ 1
- 0
deploy/static/js/vendor.b1e79088973ebca1a446.js.map
File diff suppressed because it is too large
View File


BIN
deploy/templates/.DS_Store View File


+ 1
- 0
deploy/templates/index.html View File

@ -0,0 +1 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>demo</title><link rel="shortcut icon" href=./M.jpg><link href=./static/css/app.e3820924ee579536348e1dd361736c70.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.3ad1d5771e9b13dbdad2.js></script><script type=text/javascript src=./static/js/vendor.b1e79088973ebca1a446.js></script><script type=text/javascript src=./static/js/app.7ae722821be0ba88f848.js></script></body></html>

+ 12
- 0
deploy/templates/video_presentator.html View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>视频推送</title>
</head>
<body>
<video width="{{ width }}" height="{{ height }}" controls="controls">
<source src="{{video}}" type="video/mp4" />
</video>
</body>
</html>

+ 25
- 0
deploy/video_generator.py View File

@ -0,0 +1,25 @@
import os
def generate_video(source_path, target_dir, video_dir, scene_name, quality):
command_template = 'manim {0} {1} --media_dir {2} --video_output_dir {3} {4}'
width = 2560
height = 1440
q_str = '-w'
if quality == 'w':
q_str = '-w'
elif quality == 'h':
q_str = '--high_quality'
width = 1920
height = 1080
elif quality == 'm':
q_str = '-m'
width = 1280
height = 720
elif quality == 'l':
q_str = '-l'
width = 854
height = 480
command = command_template.format(source_path, scene_name, target_dir, video_dir, q_str)
os.system(command)
return width, height

Loading…
Cancel
Save