diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2021-03-16 16:38:07 +0100 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2021-03-16 16:38:07 +0100 |
| commit | a9d86650f40a82a64d1fd8e0525c26422d314d3a (patch) | |
| tree | 6afbd4a520f557b377096e6bb8a0838c0b725f7e | |
| parent | 6e18c6e4ef6344f92fed99dad9c83484487c32d7 (diff) | |
make uploads like animism. start audio stuff
| -rw-r--r-- | cli/app/controllers/upload_controller.py | 75 | ||||
| -rw-r--r-- | cli/app/sql/models/upload.py | 17 | ||||
| -rw-r--r-- | cli/app/sql/versions/202103161637_make_uploads_like_on_animism.py | 31 | ||||
| -rw-r--r-- | data_store/uploads/.gitkeep | 0 | ||||
| -rw-r--r-- | frontend/app/api/crud.upload.js | 14 | ||||
| -rw-r--r-- | frontend/app/common/app.css | 10 | ||||
| -rw-r--r-- | frontend/app/common/form.component.js | 2 | ||||
| -rw-r--r-- | frontend/app/views/graph/components/graph.header.js | 3 | ||||
| -rw-r--r-- | frontend/app/views/graph/components/page.form.js | 27 | ||||
| -rw-r--r-- | frontend/app/views/page/components/page.header.js | 3 | ||||
| -rw-r--r-- | frontend/app/views/page/components/tile.edit.js | 4 | ||||
| -rw-r--r-- | frontend/app/views/page/components/tile.handle.js | 1 | ||||
| l--------- | static/uploads | 1 |
13 files changed, 137 insertions, 51 deletions
diff --git a/cli/app/controllers/upload_controller.py b/cli/app/controllers/upload_controller.py index 86f9f29..94a7fd1 100644 --- a/cli/app/controllers/upload_controller.py +++ b/cli/app/controllers/upload_controller.py @@ -15,18 +15,22 @@ from app.server.decorators import APIError class UploadView(FlaskView): def index(self): """ - List all uploaded files. - - * Query string params: offset, limit, sort (id, date), order (asc, desc) + List all uploads """ session = Session() - uploads = session.query(Upload).all() - response = { + query = session.query(Upload) + graph_id = args.get('graph_id', default=None) + if graph_id is not None: + query = query.filter(Upload.graph_id == int(graph_id)) + + items = query.all() + + res = { 'status': 'ok', - 'res': [ upload.toJSON() for upload in uploads ], + 'res': [ item.toJSON() for item in items ], } session.close() - return jsonify(response) + return jsonify(res) def get(self, id): """ @@ -50,14 +54,31 @@ class UploadView(FlaskView): try: username = request.form.get('username') + # print(username) except: raise APIError('No username specified') - param_name = 'image' - if param_name not in request.files: - raise APIError('No file uploaded') + try: + tag = request.form.get('tag') + # print(tag) + except: + raise APIError('No tag specified') - file = request.files[param_name] + try: + graph_id = request.form.get('graph_id') + # print(graph_id) + except: + raise APIError('No graph_id specified') + + if 'image' in request.files: + file = request.files['image'] + # print(fn) + elif 'file' in request.files: + file = request.files['file'] + # print(request.form.get('__image_filename')) + # print(fn) + else: + raise APIError('No file uploaded') # get sha256 sha256 = sha256_stream(file) @@ -65,42 +86,34 @@ class UploadView(FlaskView): if ext == '.jpeg': ext = '.jpg' - # TODO: here check sha256 - # upload = Upload.query.get(id) - - if ext[1:] not in VALID_IMAGE_EXTS: - return jsonify({ 'status': 'error', 'error': 'Not a valid image' }) + ext = ext[1:] - # convert string of image data to uint8 file.seek(0) - nparr = np.fromstring(file.read(), np.uint8) - # decode image - try: - im = Image.fromarray(nparr) - except: - return jsonify({ 'status': 'error', 'error': 'Image parse error' }) + uploaded_im_fn = secure_filename(file.filename) + uploaded_im_abspath = os.path.join(app_cfg.DIR_UPLOADS, str(graph_id), tag) + uploaded_im_fullpath = os.path.join(uploaded_im_abspath, uploaded_im_fn) session = Session() upload = session.query(Upload).filter_by(sha256=sha256).first() if upload is not None: - print("Already uploaded image") + print("Already uploaded file") + if not os.path.exists(uploaded_im_fullpath): + # if we got in some weird state where the record wasnt deleted.... + os.makedirs(uploaded_im_abspath, exist_ok=True) + file.save(uploaded_im_fullpath) response = { 'status': 'ok', - 'notes': 'Image already uploaded', + 'notes': 'File already uploaded', 'res': upload.toJSON(), } session.close() return jsonify(response) - uploaded_im_fn = secure_filename(sha256 + ext) - uploaded_im_abspath = os.path.join(app_cfg.DIR_UPLOADS, sha256_tree(sha256)) - uploaded_im_fullpath = os.path.join(uploaded_im_abspath, uploaded_im_fn) - os.makedirs(uploaded_im_abspath, exist_ok=True) - nparr.tofile(uploaded_im_fullpath) + file.save(uploaded_im_fullpath) - upload = Upload(username=username, sha256=sha256, ext=ext) + upload = Upload(username=username, tag=tag, fn=uploaded_im_fn, sha256=sha256, ext=ext, graph_id=graph_id) session.add(upload) session.commit() response = { diff --git a/cli/app/sql/models/upload.py b/cli/app/sql/models/upload.py index 5863b07..87f758a 100644 --- a/cli/app/sql/models/upload.py +++ b/cli/app/sql/models/upload.py @@ -14,31 +14,28 @@ class Upload(Base): """Table for storing references to various media""" __tablename__ = 'upload' id = Column(Integer, primary_key=True) + graph_id = Column(Integer) sha256 = Column(String(256), nullable=False) fn = Column(String(256), nullable=False) ext = Column(String(4, convert_unicode=True), nullable=False) + tag = Column(String(64, convert_unicode=True), nullable=True) username = Column(String(16, convert_unicode=True), nullable=False) created_at = Column(UtcDateTime(), default=utcnow()) def toJSON(self): return { 'id': self.id, + 'graph_id': self.graph_id, 'sha256': self.sha256, 'fn': self.fn, 'ext': self.ext, + 'tag': self.tag, 'username': self.username, 'url': self.url(), 'created_at': self.created_at, } - def filename(self): - return "{}{}".format(self.fn) - - def filepath(self): - return join(app_cfg.DIR_UPLOADS, sha256_tree(self.sha256)) - - def fullpath(self): - return join(self.filepath(), self.filename()) - def url(self): - return join(app_cfg.URL_UPLOADS, sha256_tree(self.sha256), self.filename()) + if self.tag: + return join('/static/data_store/uploads', str(self.graph_id), self.tag, self.fn) + return join('/static/data_store/uploads', str(self.graph_id), self.fn) diff --git a/cli/app/sql/versions/202103161637_make_uploads_like_on_animism.py b/cli/app/sql/versions/202103161637_make_uploads_like_on_animism.py new file mode 100644 index 0000000..18bf0bc --- /dev/null +++ b/cli/app/sql/versions/202103161637_make_uploads_like_on_animism.py @@ -0,0 +1,31 @@ +"""make uploads like on animism + +Revision ID: 645f315e651d +Revises: d929da3e398b +Create Date: 2021-03-16 16:37:08.985792 + +""" +from alembic import op +import sqlalchemy as sa +import sqlalchemy_utc + + +# revision identifiers, used by Alembic. +revision = '645f315e651d' +down_revision = 'd929da3e398b' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('upload', sa.Column('graph_id', sa.Integer(), nullable=True)) + op.add_column('upload', sa.Column('tag', sa.String(length=64, _expect_unicode=True), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('upload', 'tag') + op.drop_column('upload', 'graph_id') + # ### end Alembic commands ### diff --git a/data_store/uploads/.gitkeep b/data_store/uploads/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/data_store/uploads/.gitkeep diff --git a/frontend/app/api/crud.upload.js b/frontend/app/api/crud.upload.js index 8c1b265..2837dd4 100644 --- a/frontend/app/api/crud.upload.js +++ b/frontend/app/api/crud.upload.js @@ -1,4 +1,5 @@ import { as_type } from 'app/api/crud.types' +import { session } from 'app/session' export function crud_upload(type, data, dispatch) { return new Promise( (resolve, reject) => { @@ -6,9 +7,17 @@ export function crud_upload(type, data, dispatch) { const { id } = data const fd = new FormData() + if (!data.tag) { + data.tag = 'misc' + } Object.keys(data).forEach(key => { - if (key !== 'id') { + if (key.indexOf('__') !== -1) return + if (key === 'id') return + const fn_key = `__${key}_filename` + if (fn_key in data) { + fd.append(key, data[key], data[fn_key]) + } else { fd.append(key, data[key]) } }) @@ -23,12 +32,11 @@ export function crud_upload(type, data, dispatch) { xhr.addEventListener("error", uploadFailed, false) xhr.addEventListener("abort", uploadCancelled, false) xhr.open("POST", url) + xhr.setRequestHeader("Authorization", "Bearer " + session.get("access_token")) xhr.send(fd) dispatch && dispatch({ type: as_type(type, 'upload_loading')}) - let complete = false - function uploadProgress (e) { if (e.lengthComputable) { const percent = Math.round(e.loaded * 100 / e.total) || 0 diff --git a/frontend/app/common/app.css b/frontend/app/common/app.css index d9f9946..2e9dc4e 100644 --- a/frontend/app/common/app.css +++ b/frontend/app/common/app.css @@ -147,6 +147,16 @@ header a:active { header a.navbar-brand { font-size: .8rem; } +header .arrow { + padding: 0.5rem 0.5rem 0.5rem 0.5rem; + margin-left: -0.5rem; + margin-right: 0.25rem; + transition: background 0.2s; + border-radius: 4px; +} +header .arrow:hover { + background: rgba(0,0,255,0.5); +} header .username { cursor: pointer; diff --git a/frontend/app/common/form.component.js b/frontend/app/common/form.component.js index cf3e466..927b89d 100644 --- a/frontend/app/common/form.component.js +++ b/frontend/app/common/form.component.js @@ -76,7 +76,7 @@ export const Checkbox = props => ( type="checkbox" name={props.name} value={1} - checked={props.checked} + checked={!!props.checked} onChange={(e) => props.onChange(props.name, e.target.checked)} /> <span>{props.label}</span> diff --git a/frontend/app/views/graph/components/graph.header.js b/frontend/app/views/graph/components/graph.header.js index 46ad962..b969400 100644 --- a/frontend/app/views/graph/components/graph.header.js +++ b/frontend/app/views/graph/components/graph.header.js @@ -9,7 +9,8 @@ function GraphHeader(props) { return ( <header> <div> - <Link to="/" className="logo"><b>{props.site.siteTitle}</b></Link> + <Link to="/" className="logo arrow">{"◁ "}</Link> + <b>{props.site.siteTitle}</b> </div> <div> <button onClick={() => props.graphActions.toggleAddPageForm()}>+ Add page</button> diff --git a/frontend/app/views/graph/components/page.form.js b/frontend/app/views/graph/components/page.form.js index 8fc00b0..2c283aa 100644 --- a/frontend/app/views/graph/components/page.form.js +++ b/frontend/app/views/graph/components/page.form.js @@ -3,7 +3,7 @@ import { Link } from 'react-router-dom' import { session } from 'app/session' -import { TextInput, ColorInput, LabelDescription, TextArea, Checkbox, SubmitButton, Loader } from 'app/common' +import { TextInput, ColorInput, Checkbox, LabelDescription, TextArea, SubmitButton, Loader } from 'app/common' const newPage = (data) => ({ path: '', @@ -14,6 +14,8 @@ const newPage = (data) => ({ x: 0.05, y: 0.05, background_color: '#000000', + audio: "", + restartAudio: false, }, ...data, }) @@ -76,6 +78,10 @@ export default class PageForm extends Component { handleSettingsChange(e) { const { name, value } = e.target + this.handleSettingsSelect(name, value) + } + + handleSettingsSelect(name, value) { this.setState({ data: { ...this.state.data, @@ -147,7 +153,7 @@ export default class PageForm extends Component { autoComplete="off" /> <ColorInput - title='BG' + title='BG Color' name='background_color' data={data.settings} onChange={this.handleSettingsChange.bind(this)} @@ -159,6 +165,23 @@ export default class PageForm extends Component { data={data} onChange={this.handleChange.bind(this)} /> + + <TextInput + title="Background Audio URL" + name="audio" + required + data={data.settings} + onChange={this.handleSettingsChange.bind(this)} + autoComplete="off" + /> + <Checkbox + label="Restart audio on load" + name="restartAudio" + checked={data.settings.restartAudio} + onChange={this.handleSettingsSelect.bind(this)} + autoComplete="off" + /> + <div className='row buttons'> <SubmitButton title={submitTitle} diff --git a/frontend/app/views/page/components/page.header.js b/frontend/app/views/page/components/page.header.js index eb1c3b9..998572a 100644 --- a/frontend/app/views/page/components/page.header.js +++ b/frontend/app/views/page/components/page.header.js @@ -10,7 +10,8 @@ function PageHeader(props) { return ( <header> <div> - <Link to={props.graph.show.res ? "/" + props.graph.show.res.path : "/"} className="logo"><b>{props.site.siteTitle}</b></Link> + <Link to={props.graph.show.res ? "/" + props.graph.show.res.path : "/"} className="logo arrow">{"◁"}</Link> + <b>{props.site.siteTitle}</b> </div> <div> <button onClick={() => props.pageActions.toggleAddTileForm()}>+ Add tile</button> diff --git a/frontend/app/views/page/components/tile.edit.js b/frontend/app/views/page/components/tile.edit.js index 2ea09d1..cae9f73 100644 --- a/frontend/app/views/page/components/tile.edit.js +++ b/frontend/app/views/page/components/tile.edit.js @@ -29,7 +29,9 @@ class TileEdit extends Component { load() { const { currentEditTileId } = this.props.page.editor - const tile = this.props.page.show.res.tiles.filter(tile => tile.id === currentEditTileId)[0] + const { tiles } = this.props.page.show.res + if (!tiles) return + const tile = tiles.filter(tile => tile.id === currentEditTileId)[0] console.log('edit', currentEditTileId) this.setState({ tile }) } diff --git a/frontend/app/views/page/components/tile.handle.js b/frontend/app/views/page/components/tile.handle.js index 9331cb3..f47c3cd 100644 --- a/frontend/app/views/page/components/tile.handle.js +++ b/frontend/app/views/page/components/tile.handle.js @@ -151,7 +151,6 @@ const generateTransform = (tile, box) => { if (xalign === 'center') { transform.push('translateX(-50%)') } - console.log(units) // if (x % 2 == 1) x += 0.5 // if (y % 2 == 1) y += 0.5 transform.push('translateX(' + x + units + ')') diff --git a/static/uploads b/static/uploads new file mode 120000 index 0000000..79c5899 --- /dev/null +++ b/static/uploads @@ -0,0 +1 @@ +../data_store/uploads
\ No newline at end of file |
