mirror of
https://github.com/Alexander-D-Karpov/akarpov
synced 2024-11-25 11:43:44 +03:00
added audio, video previews, fixed image previews
This commit is contained in:
parent
8a995a8ca5
commit
e7d78d59ec
|
@ -1,3 +1,20 @@
|
||||||
from . import image, video
|
from . import audio, image, video
|
||||||
|
|
||||||
previews = {"video": {"mp4": video.mp4.view}, "image": {"jpeg": image.jpeg.view}}
|
previews = {
|
||||||
|
"audio": {
|
||||||
|
"aac": audio.basic.view,
|
||||||
|
"mpeg": audio.basic.view,
|
||||||
|
"ogg": audio.basic.view,
|
||||||
|
"opus": audio.basic.view,
|
||||||
|
"wav": audio.basic.view,
|
||||||
|
"webm": audio.basic.view,
|
||||||
|
},
|
||||||
|
"video": {"mp4": video.mp4.view},
|
||||||
|
"image": {
|
||||||
|
"jpeg": image.basic.view,
|
||||||
|
"png": image.basic.view,
|
||||||
|
"avif": image.basic.view,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
extensions = {"mp4": video.mp4.view, "mp3": audio.basic.view, "avif": image.basic.view}
|
||||||
|
|
1
akarpov/files/previews/audio/__init__.py
Normal file
1
akarpov/files/previews/audio/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
from . import basic # noqa
|
61
akarpov/files/previews/audio/basic.py
Normal file
61
akarpov/files/previews/audio/basic.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
from akarpov.files.models import File
|
||||||
|
|
||||||
|
|
||||||
|
def view(file: File) -> (str, str):
|
||||||
|
static = """
|
||||||
|
"""
|
||||||
|
content = (
|
||||||
|
"""
|
||||||
|
<div id="waveform">
|
||||||
|
</div>
|
||||||
|
<div id="wave-timeline"> </div>
|
||||||
|
|
||||||
|
<div class="controls mt-4 d-flex align-items-center justify-content-center text-center">
|
||||||
|
<button class="btn btn-primary" data-action="play">
|
||||||
|
<i class="glyphicon glyphicon-play"></i>
|
||||||
|
Play
|
||||||
|
/
|
||||||
|
<i class="glyphicon glyphicon-pause"></i>
|
||||||
|
Pause
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<script src="https://unpkg.com/wavesurfer.js@6.6.3/dist/wavesurfer.js"></script>
|
||||||
|
<script src="https://unpkg.com/wavesurfer.js@6.6.3/dist/plugin/wavesurfer.timeline.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/wavesurfer.js@6.6.3/dist/plugin/wavesurfer.regions.min.js"></script>
|
||||||
|
<script>
|
||||||
|
window.onload = function() {
|
||||||
|
wavesurfer = WaveSurfer.create({
|
||||||
|
container: document.querySelector('#waveform'),
|
||||||
|
waveColor: '#D9DCFF',
|
||||||
|
progressColor: '#4353FF',
|
||||||
|
cursorColor: '#4353FF',
|
||||||
|
barWidth: 3,
|
||||||
|
barRadius: 3,
|
||||||
|
cursorWidth: 1,
|
||||||
|
height: 200,
|
||||||
|
barGap: 3,
|
||||||
|
plugins: [
|
||||||
|
WaveSurfer.regions.create({}),
|
||||||
|
WaveSurfer.timeline.create({
|
||||||
|
container: "#wave-timeline"
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
wavesurfer.on('error', function(e) {
|
||||||
|
console.warn(e);
|
||||||
|
});
|
||||||
|
"""
|
||||||
|
+ f"""
|
||||||
|
wavesurfer.load('{file.file.url}');
|
||||||
|
"""
|
||||||
|
+ """
|
||||||
|
document
|
||||||
|
.querySelector('[data-action="play"]')
|
||||||
|
.addEventListener('click', wavesurfer.playPause.bind(wavesurfer));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
return static, content
|
102
akarpov/files/previews/audio/waves.py
Normal file
102
akarpov/files/previews/audio/waves.py
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
from akarpov.files.models import File
|
||||||
|
|
||||||
|
|
||||||
|
def view(file: File) -> (str, str):
|
||||||
|
static = """
|
||||||
|
<style>
|
||||||
|
#canvas {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
"""
|
||||||
|
content = (
|
||||||
|
"""
|
||||||
|
<div id="container">
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
<audio id="audio"></audio>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
+ """
|
||||||
|
<script>
|
||||||
|
const container = document.getElementById("container");
|
||||||
|
container.addEventListener("click", function () {
|
||||||
|
let audio1 = new Audio();
|
||||||
|
"""
|
||||||
|
+ f"""
|
||||||
|
audio1.src = "{file.file.url}";
|
||||||
|
"""
|
||||||
|
+ """
|
||||||
|
audio1.crossOrigin = "anonymous";
|
||||||
|
const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); // for safari browser
|
||||||
|
|
||||||
|
const container = document.getElementById("container");
|
||||||
|
const canvas = document.getElementById("canvas");
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
let audioSource = null;
|
||||||
|
let analyser = null;
|
||||||
|
|
||||||
|
audio1.play();
|
||||||
|
audioSource = audioCtx.createMediaElementSource(audio1);
|
||||||
|
analyser = audioCtx.createAnalyser();
|
||||||
|
audioSource.connect(analyser);
|
||||||
|
analyser.connect(audioCtx.destination);
|
||||||
|
analyser.fftSize = 128;
|
||||||
|
const bufferLength = analyser.frequencyBinCount;
|
||||||
|
const dataArray = new Uint8Array(bufferLength);
|
||||||
|
|
||||||
|
const barWidth = canvas.width / 2 / bufferLength;
|
||||||
|
|
||||||
|
let x = 0;
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
x = 0;
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height); // clears the canvas
|
||||||
|
analyser.getByteFrequencyData(dataArray);
|
||||||
|
drawVisualizer({ bufferLength, dataArray, barWidth });
|
||||||
|
requestAnimationFrame(animate); // calls the animate function again. This method is built in
|
||||||
|
}
|
||||||
|
|
||||||
|
const drawVisualizer = ({ bufferLength, dataArray, barWidth }) => {
|
||||||
|
let barHeight;
|
||||||
|
for (let i = 0; i < bufferLength; i++) {
|
||||||
|
barHeight = dataArray[i];
|
||||||
|
const red = (i * barHeight) / 10;
|
||||||
|
const green = i * 4;
|
||||||
|
const blue = barHeight / 4 - 12;
|
||||||
|
ctx.fillStyle = `rgb(${red}, ${green}, ${blue})`;
|
||||||
|
ctx.fillRect(
|
||||||
|
canvas.width / 2 - x, // this will start the bars at the center of the canvas and move from right to left
|
||||||
|
canvas.height - barHeight,
|
||||||
|
barWidth,
|
||||||
|
barHeight
|
||||||
|
);
|
||||||
|
x += barWidth; // increases the x value by the width of the bar
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < bufferLength; i++) {
|
||||||
|
barHeight = dataArray[i];
|
||||||
|
const red = (i * barHeight) / 10;
|
||||||
|
const green = i * 4;
|
||||||
|
const blue = barHeight / 4 - 12;
|
||||||
|
ctx.fillStyle = `rgb(${red}, ${green}, ${blue})`;
|
||||||
|
ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight);
|
||||||
|
x += barWidth; // increases the x value by the width of the bar
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
animate();
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
return static, content
|
|
@ -1 +1 @@
|
||||||
from . import jpeg # noqa
|
from . import basic # noqa
|
||||||
|
|
|
@ -10,6 +10,8 @@ def view(file: File) -> (str, str):
|
||||||
<div>
|
<div>
|
||||||
<img id="image" class="img-fluid" src="{file.file.url}" alt="Picture">
|
<img id="image" class="img-fluid" src="{file.file.url}" alt="Picture">
|
||||||
</div>
|
</div>
|
||||||
|
<div id="images">
|
||||||
|
</div
|
||||||
<script src="/static/js/jquery.js"></script>
|
<script src="/static/js/jquery.js"></script>
|
||||||
"""
|
"""
|
||||||
+ """
|
+ """
|
||||||
|
@ -23,10 +25,7 @@ def view(file: File) -> (str, str):
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get the Viewer.js instance after initialized
|
|
||||||
var viewer = $image.data('viewer');
|
var viewer = $image.data('viewer');
|
||||||
|
|
||||||
// View a list of images
|
|
||||||
$('#images').viewer();
|
$('#images').viewer();
|
||||||
</script>
|
</script>
|
||||||
"""
|
"""
|
|
@ -9,7 +9,7 @@ def view(file: File) -> (str, str):
|
||||||
content = f"""
|
content = f"""
|
||||||
|
|
||||||
<video id="my_video_1" class="video-js vjs-default-skin" height="500px"
|
<video id="my_video_1" class="video-js vjs-default-skin" height="500px"
|
||||||
controls poster='{file.preview.url}'
|
controls poster='{file.preview.url if file.preview else ''}'
|
||||||
data-setup='{data}'>
|
data-setup='{data}'>
|
||||||
<source src="{file.file.url}" type='video/mp4' />
|
<source src="{file.file.url}" type='video/mp4' />
|
||||||
</video>
|
</video>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
ChunkedUploadView,
|
ChunkedUploadView,
|
||||||
)
|
)
|
||||||
from akarpov.files.models import File, Folder
|
from akarpov.files.models import File, Folder
|
||||||
from akarpov.files.previews import previews
|
from akarpov.files.previews import extensions, previews
|
||||||
|
|
||||||
|
|
||||||
class TopFolderView(LoginRequiredMixin, ListView):
|
class TopFolderView(LoginRequiredMixin, ListView):
|
||||||
|
@ -41,10 +41,15 @@ def get_context_data(self, **kwargs):
|
||||||
static = ""
|
static = ""
|
||||||
content = ""
|
content = ""
|
||||||
if self.object.file_type:
|
if self.object.file_type:
|
||||||
t1, t2 = self.object.file_type.split("/")
|
if self.object.file_type == "application/octet-stream":
|
||||||
if t1 in previews:
|
extension = self.object.file.path.split(".")[-1]
|
||||||
if t2 in previews[t1]:
|
if extension in extensions:
|
||||||
static, content = previews[t1][t2](self.object)
|
static, content = extensions[extension](self.object)
|
||||||
|
else:
|
||||||
|
t1, t2 = self.object.file_type.split("/")
|
||||||
|
if t1 in previews:
|
||||||
|
if t2 in previews[t1]:
|
||||||
|
static, content = previews[t1][t2](self.object)
|
||||||
context["preview_static"] = static
|
context["preview_static"] = static
|
||||||
context["preview_content"] = content
|
context["preview_content"] = content
|
||||||
return context
|
return context
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row m-2">
|
<div class="row m-2">
|
||||||
<div class="col-auto">
|
<div class="col-md-4 col-sm-6">
|
||||||
<h1 class="fs-1">{{ file.name }}</h1>
|
<h1 class="fs-1">{{ file.name }}</h1>
|
||||||
{% if not has_perm %}
|
{% if not has_perm %}
|
||||||
<p>Uploaded by: <a href="{% url 'users:detail' file.user.username %}">
|
<p>Uploaded by: <a href="{% url 'users:detail' file.user.username %}">
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
<p>{{ file.description }}</p>
|
<p>{{ file.description }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto">
|
<div class="col-md-8 col-sm-10">
|
||||||
{% autoescape off %}
|
{% autoescape off %}
|
||||||
{{ preview_content }}
|
{{ preview_content }}
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
|
|
|
@ -7,4 +7,4 @@ dpkg -i draw.io-amd64-13.0.3.deb
|
||||||
rm draw.io-amd64-13.0.3.deb
|
rm draw.io-amd64-13.0.3.deb
|
||||||
apt-get purge -y --auto-remove -o APT:AutoRemove:RecommendsImportant=false && \
|
apt-get purge -y --auto-remove -o APT:AutoRemove:RecommendsImportant=false && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
preview --check-dependencies``
|
preview --check-dependencies
|
||||||
|
|
Loading…
Reference in New Issue
Block a user