Switched to dropzone

This commit is contained in:
Tris 2020-09-10 20:27:38 +10:00
parent ef988708d8
commit 665694e305
11 changed files with 154 additions and 122 deletions

5
.gitignore vendored
View File

@ -2,4 +2,7 @@ __pycache__
*.pyc
db.sqlite3
credentials
polyphonic/settings.py
polyphonic/settings.py
env
text.*
static

View File

@ -1,12 +1,27 @@
DROPZONE=https://github.com/enyo/dropzone/archive/v5.7.0.zip
PYTHON=env/bin/python
DROPZONE=5.7.0
dev-setup:
pip install -r requirements.txt
pip install -r dev-requirements.txt
./manage.py migrate
./manage.py createsuperuser --username admin --email admin@localhost
env/bin/pip install -r requirements.txt
env/bin/pip install -r dev-requirements.txt
${PYTHON} manage.py migrate
${PYTHON} manage.py createsuperuser --username admin --email admin@localhost
interface/static/dropzone/dropzone.js:
wget -O dropzone.zip ${DROPZONE}
unzip -d interface/static dropzone.zip
upgrade:
${PYTHON} manage.py migrate
${PYTHON} manage.py collectstatic
${MAKE} libraries
libraries: static/dropzone static/fonts/Quicksand_Book.otf
static/dropzone:
wget -O dropzone-${DROPZONE}.zip https://github.com/enyo/dropzone/archive/v${DROPZONE}.zip
unzip dropzone-${DROPZONE}.zip
mv dropzone-${DROPZONE}/dist static/dropzone
rm -rf dropzone-${DROPZONE} dropzone-${DROPZONE}.zip
static/fonts/Quicksand_Book.otf:
wget -O quicksand.zip https://dl.dafont.com/dl/?f=quicksand
mkdir -p static/fonts
unzip -d static/fonts quicksand.zip
rm quicksand.zip

View File

@ -0,0 +1,18 @@
# Generated by Django 3.1.1 on 2020-09-10 10:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('interface', '0015_resource_media_type'),
]
operations = [
migrations.AlterField(
model_name='resource',
name='media_type',
field=models.CharField(choices=[('audio', 'Audio'), ('video', 'Video'), ('general', 'General')], default='*', max_length=10),
),
]

View File

@ -18,7 +18,7 @@ BUCKET = settings.AWS_BUCKET
MEDIA_TYPES = [
('audio', "Audio"),
('video', "Video"),
('*', "General"),
('general', "General"),
]
def generate_code(length=9):
@ -63,7 +63,12 @@ class Resource(models.Model):
media_type = models.CharField(max_length=10, choices=MEDIA_TYPES, default='*')
def key_template(self):
return "{}_${{filename}}".format(slugify(self.name))
return "{}/${{filename}}".format(slugify(self.name))
def accept(self):
if self.media_type == 'general':
return ".*"
return f"{self.media_type}/*"
def presigned_url(self):
if not self.key:

View File

@ -5,11 +5,6 @@
--light-blue: #c5eff7;
}
@font-face {
font-family: 'DreamOrphans';
src: url('../../fonts/dream orphans.ttf') format('truetype');
}
@font-face {
font-family: 'Quicksand';
src: url('../../fonts/Quicksand_Book.otf');
@ -102,7 +97,7 @@ UL.nav-buttons > LI {
/* FORMS */
FORM {
FORM.vertical {
display: flex;
flex-direction: column;
max-width: 400px;
@ -221,4 +216,8 @@ TABLE.horizontal TH {
.resource-player {
width: 100%;
border-radius: 10px;
}
.dz-clickable {
text-align: center;
}

View File

@ -4,7 +4,7 @@
<div class="narrow">
<h3>{{ title }}</h3>
<p>{{ instructions }}</p>
<form method="POST">
<form class="vertical" method="POST">
{% csrf_token %}
{{ form }}
<div class="form-actions">

View File

@ -11,12 +11,12 @@
{{ resource.name }}
{% if download %}
<a href="{{ download }}">
<i class="fas fa-download"></i>
<i class="fas fa-download"></i> Download
</a>
{% endif %}
{% if request.user.is_authenticated %}
<a href="{% url 'resource_upload' project=project.pk pk=resource.pk %}">
<i class="fas fa-upload"></i>
<i class="fas fa-upload"></i> Upload
</a>
{% endif %}
</h3>

View File

@ -0,0 +1,82 @@
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="narrow">
<h3 id="status">Select a file for upload</h3>
<form method="POST" action="{{ upload.url }}" enctype="multipart/form-data" id="item-upload" class="dropzone">
<div class="fallback">
{% for field, value in upload.fields.items %}
<input type="hidden" name="{{ field }}" value="{{ value }}" />
{% endfor %}
<input name="file" type="file" accept="{{ media_type }}/*" id="fallback-file"/>
<input type="submit" value="Send" id="fallback-upload"/>
</div>
</form>
<div class="form-actions text-right">
<a class=""
href="{{ cancel_url }}">Cancel</a>
&nbsp;
<button type="submit" id="upload" disabled>Upload</button>
</div>
</div>
{% endblock %}
{% block scripts %}
<link rel="stylesheet" href="{% static 'dropzone/dropzone.css' %}"/>
<script src="{% static 'dropzone/dropzone.js' %}"></script>
{{ ajax_upload|json_script:"dropzone-data" }}
<script type="text/javascript">
const dzData = JSON.parse(document.getElementById('dropzone-data').textContent);
console.log(dzData);
let status = $('#status');
let uploadBtn = $('#upload');
$('#fallback-upload').on('click', function() {
status.html('Uploading - please wait...');
}).attr('disabled', true);
$('#fallback-file').on('change', function() {
status.html('Ready to upload');
$('#fallback-upload').attr('disabled', false);
});
Dropzone.options.itemUpload = {
params: dzData['fields'],
forceFallback: {{ request.GET.fallback|yesno:"true,false,false" }},
autoProcessQueue: false,
createImageThumbnails: false,
maxFiles: 1,
addRemoveLinks: true,
maxFilesize: 500,
dictDefaultMessage: "Select a file to upload (max 500Mb)",
dictFallbackMessage: "Your browser is old!, using alternative method",
init: function() {
let dz = this;
uploadBtn.on('click', function() {
dz.processQueue();
});
this.on('addedfile', function(f) {
status.html('Ready to upload...');
uploadBtn.attr('disabled', false);
});
this.on('sending', function(f) {
status.html('Uploading - please wait...');
});
this.on('success', function(f) {
status.html("Upload successfull");
//if (! confirm("Upload complete - redirect?")) return;
let s3_location = f.xhr.getResponseHeader("Location");
location.href = "{{ success_url }}?location=" + s3_location;
});
}
};
</script>
{% endblock %}

View File

@ -9,7 +9,7 @@
</p>
</div>
<div>
<form action="" method="POST" enctype="multipart/form-data">
<form class="vertical" action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
<div class="form-actions">

View File

@ -1,99 +0,0 @@
{% extends "base.html" %}
{% load static %}
{% block content %}
<div class="narrow">
<h3>Ready to upload file</h3>
<form method="POST" action="{{ upload.url }}" enctype="multipart/form-data" id="video-upload">
{% for field, value in upload.fields.items %}
<input type="hidden" name="{{ field }}" value="{{ value }}" />
{% endfor %}
<input name="file" type="file" accept="video/*" />
<div class="progress">
<div class="progress-bar"></div>
</div>
<div class="form-actions text-right">
<a class=""
href="{{ cancel_url }}">Cancel</a>
&nbsp;
<button type="submit" id="upload">Upload</button>
</div>
<div id="status">Select a file to upload</div>
</form>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.form/4.3.0/jquery.form.min.js" integrity="sha384-qlmct0AOBiA2VPZkMY3+2WqkHtIQ9lSdAsAn5RUJD/3vA5MKDgSGcdmIv4ycVxyn" crossorigin="anonymous"></script>
<script>
function uploadReady(e) {
$('#upload').attr('disabled', false);
$('#status').html('Ready to upload');
}
function startUpload(e) {
var bar = $('.progress-bar');
var percent = $('.percent');
var status = $('#status');
console.log("STARTING");
// disable file select and submit
e.target.disabled = true;
//$('input[name="file"]').attr('disabled', true);
// patch form to use our ajax policy
$('input[name="policy"]').val("{{ ajax_upload.fields.policy }}");
$('input[name="signature"]').val("{{ ajax_upload.fields.signature }}");
$('input[name="success_action_redirect"]').remove();
$('form').ajaxForm({
beforeSend: function() {
status.html("Starting upload");
var percentVal = '0%';
bar.width(percentVal)
percent.html(percentVal);
},
uploadProgress: function(event, position, total, percentComplete) {
var percentVal = percentComplete + '%';
bar.width(percentVal)
percent.html(percentVal);
},
success: function() {
console.log("success");
var percentVal = '100%';
bar.width(percentVal)
percent.html(percentVal);
},
complete: function(xhr) {
if (xhr.status == 204) {
status.html("Finished upload");
let s3_location = xhr.getResponseHeader("Location");
location.href = "{{ success_url }}?location=" + s3_location;
} else {
status.html("Something went wrong");
console.log(xhr.responseText);
}
}
});
}
// enable ajax upload
$('input[name="file"]').on('change', uploadReady);
$('#upload').on('click', startUpload).attr('disabled', true);
</script>
{% endblock %}

View File

@ -46,6 +46,10 @@ class ProjectMixin(EnsembleMixin):
return context
class S3UploadMixin(ProjectMixin):
media_type = ''
def get_media_type(self):
return self.media_type
def get_cancel_url(self):
return self.cancel_url
@ -64,6 +68,7 @@ class S3UploadMixin(ProjectMixin):
context['ajax_upload'] = project.presigned_post(key_template)
context['success_url'] = success_url
context['cancel_url'] = self.get_cancel_url()
context['media_type'] = self.media_type
return context
class S3CompleteMixin(View):
@ -176,6 +181,7 @@ class SubmissionDetailView(ProjectMixin, S3CompleteMixin, DetailView):
class SubmissionUploadView(S3UploadMixin, DetailView):
template_name = 'interface/s3_upload.html'
model = models.Submission
media_type = "video"
def get_success_url(self):
return resolve_url('submission_detail', **self.kwargs)
@ -209,7 +215,10 @@ class ResourceCreateView(ProjectMixin, CreateView):
class ResourceUploadView(S3UploadMixin, DetailView):
model = models.Resource
template_name = 'interface/submission_upload.html'
template_name = 'interface/s3_upload.html'
def get_media_type(self):
return self.object.accept()
def get_success_url(self):
return resolve_url('resource_complete', **self.kwargs)