mirror of
https://github.com/Alexander-D-Karpov/akarpov
synced 2024-11-22 10:56:39 +03:00
updated previews, fixed short links, added og tags, download delete
This commit is contained in:
parent
e7d78d59ec
commit
d4f68935a1
|
@ -9,7 +9,7 @@
|
||||||
"wav": audio.basic.view,
|
"wav": audio.basic.view,
|
||||||
"webm": audio.basic.view,
|
"webm": audio.basic.view,
|
||||||
},
|
},
|
||||||
"video": {"mp4": video.mp4.view},
|
"video": {"mp4": video.mp4.view, "quicktime": video.basic.view},
|
||||||
"image": {
|
"image": {
|
||||||
"jpeg": image.basic.view,
|
"jpeg": image.basic.view,
|
||||||
"png": image.basic.view,
|
"png": image.basic.view,
|
||||||
|
@ -17,4 +17,9 @@
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
extensions = {"mp4": video.mp4.view, "mp3": audio.basic.view, "avif": image.basic.view}
|
extensions = {
|
||||||
|
"mp4": video.mp4.view,
|
||||||
|
"mp3": audio.basic.view,
|
||||||
|
"avif": image.basic.view,
|
||||||
|
"mov": video.basic.view,
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
|
|
||||||
def view(file: File) -> (str, str):
|
def view(file: File) -> (str, str):
|
||||||
static = """
|
static = f"""
|
||||||
|
<meta property="og:title" content="{file.name}" />
|
||||||
"""
|
"""
|
||||||
content = (
|
content = (
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
|
|
||||||
|
|
||||||
def view(file: File) -> (str, str):
|
def view(file: File) -> (str, str):
|
||||||
static = """
|
static = (
|
||||||
|
f"""
|
||||||
|
<meta property="og:title" content="{file.name}" />
|
||||||
|
"""
|
||||||
|
+ """
|
||||||
<style>
|
<style>
|
||||||
#canvas {
|
#canvas {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -13,6 +17,7 @@ def view(file: File) -> (str, str):
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
content = (
|
content = (
|
||||||
"""
|
"""
|
||||||
<div id="container">
|
<div id="container">
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
|
|
||||||
def view(file: File) -> (str, str):
|
def view(file: File) -> (str, str):
|
||||||
static = """
|
static = f"""
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/viewerjs/1.11.3/viewer.min.css" rel="stylesheet">
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/viewerjs/1.11.3/viewer.min.css" rel="stylesheet">
|
||||||
|
<meta property="og:title" content="{file.name}" />
|
||||||
"""
|
"""
|
||||||
content = (
|
content = (
|
||||||
f"""
|
f"""
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
from . import mp4 # noqa
|
from . import basic, mp4 # noqa
|
||||||
|
|
26
akarpov/files/previews/video/basic.py
Normal file
26
akarpov/files/previews/video/basic.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
from akarpov.files.models import File
|
||||||
|
|
||||||
|
|
||||||
|
def view(file: File) -> (str, str):
|
||||||
|
static = f"""
|
||||||
|
<meta property="og:title" content="{file.name}" />
|
||||||
|
<meta property="og:type" content="video.movie" />
|
||||||
|
<meta property="og:video" content="dev2.akarpov.ru/{file.file.url}" />
|
||||||
|
<meta property="og:video:type" content="{file.file_type}" />
|
||||||
|
<meta property="og:video:width" content="720" />
|
||||||
|
<meta property="og:video:height" content="480" />
|
||||||
|
<link href="https://vjs.zencdn.net/8.0.4/video-js.css" rel="stylesheet" />
|
||||||
|
"""
|
||||||
|
data = """"playbackRates": [0.5, 1, 1.5, 2], "responsive": true }"""
|
||||||
|
content = f"""
|
||||||
|
|
||||||
|
<video id="my_video_1" class="video-js vjs-default-skin" height="500px"
|
||||||
|
controls poster='{file.preview.url if file.preview else ''}'
|
||||||
|
data-setup='{data}'>
|
||||||
|
<source src="{file.file.url}" type='{file.file_type}' />
|
||||||
|
</video>
|
||||||
|
|
||||||
|
<script src="https://vjs.zencdn.net/8.0.4/video.min.js"></script>
|
||||||
|
"""
|
||||||
|
|
||||||
|
return static, content
|
|
@ -2,7 +2,13 @@
|
||||||
|
|
||||||
|
|
||||||
def view(file: File) -> (str, str):
|
def view(file: File) -> (str, str):
|
||||||
static = """
|
static = f"""
|
||||||
|
<meta property="og:title" content="{file.name}" />
|
||||||
|
<meta property="og:type" content="video.movie" />
|
||||||
|
<meta property="og:video" content="{file.file.url}" />
|
||||||
|
<meta property="og:video:type" content="{file.file_type}" />
|
||||||
|
<meta property="og:video:width" content="720" />
|
||||||
|
<meta property="og:video:height" content="480" />
|
||||||
<link href="https://vjs.zencdn.net/8.0.4/video-js.css" rel="stylesheet" />
|
<link href="https://vjs.zencdn.net/8.0.4/video-js.css" rel="stylesheet" />
|
||||||
"""
|
"""
|
||||||
data = """"playbackRates": [0.5, 1, 1.5, 2], "responsive": true }"""
|
data = """"playbackRates": [0.5, 1, 1.5, 2], "responsive": true }"""
|
||||||
|
@ -11,7 +17,7 @@ def view(file: File) -> (str, str):
|
||||||
<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 if file.preview else ''}'
|
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='{file.file_type}' />
|
||||||
</video>
|
</video>
|
||||||
|
|
||||||
<script src="https://vjs.zencdn.net/8.0.4/video.min.js"></script>
|
<script src="https://vjs.zencdn.net/8.0.4/video.min.js"></script>
|
||||||
|
|
|
@ -5,11 +5,7 @@
|
||||||
from django.core.files import File
|
from django.core.files import File
|
||||||
|
|
||||||
from akarpov.files.models import File as FileModel
|
from akarpov.files.models import File as FileModel
|
||||||
from akarpov.files.services.preview import (
|
from akarpov.files.services.preview import create_preview, get_file_mimetype
|
||||||
create_preview,
|
|
||||||
get_description,
|
|
||||||
get_file_mimetype,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = structlog.get_logger(__name__)
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
@ -32,18 +28,7 @@ def process_file(pk: int):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
file.file_type = get_file_mimetype(file.file.path)
|
file.file_type = get_file_mimetype(file.file.path)
|
||||||
descr = None
|
file.save(update_fields=["preview", "name", "file_type"])
|
||||||
try:
|
|
||||||
descr = get_description(file.file.path)
|
|
||||||
if descr:
|
|
||||||
with open(descr, encoding="utf-8") as f:
|
|
||||||
data = f.read()
|
|
||||||
file.description = data
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(e)
|
|
||||||
file.save(update_fields=["preview", "name", "file_type", "description"])
|
|
||||||
if pth and os.path.isfile(pth):
|
if pth and os.path.isfile(pth):
|
||||||
os.remove(pth)
|
os.remove(pth)
|
||||||
if descr and os.path.isfile(descr):
|
|
||||||
os.remove(descr)
|
|
||||||
return pk
|
return pk
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
MyChunkedUploadCompleteView,
|
MyChunkedUploadCompleteView,
|
||||||
MyChunkedUploadView,
|
MyChunkedUploadView,
|
||||||
TopFolderView,
|
TopFolderView,
|
||||||
|
delete_file_view,
|
||||||
files_view,
|
files_view,
|
||||||
folder_view,
|
folder_view,
|
||||||
)
|
)
|
||||||
|
@ -22,5 +23,6 @@
|
||||||
"api/chunked_upload/", MyChunkedUploadView.as_view(), name="api_chunked_upload"
|
"api/chunked_upload/", MyChunkedUploadView.as_view(), name="api_chunked_upload"
|
||||||
),
|
),
|
||||||
path("<str:slug>", files_view, name="view"),
|
path("<str:slug>", files_view, name="view"),
|
||||||
|
path("<str:slug>/delete", delete_file_view, name="delete"),
|
||||||
path("f/<str:slug>", folder_view, name="folder"),
|
path("f/<str:slug>", folder_view, name="folder"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.views.generic import DetailView, ListView
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.views.generic import DetailView, ListView, RedirectView
|
||||||
from django.views.generic.base import TemplateView
|
from django.views.generic.base import TemplateView
|
||||||
|
|
||||||
from akarpov.contrib.chunked_upload.exceptions import ChunkedUploadError
|
from akarpov.contrib.chunked_upload.exceptions import ChunkedUploadError
|
||||||
|
@ -58,6 +60,17 @@ def get_context_data(self, **kwargs):
|
||||||
files_view = FileView.as_view()
|
files_view = FileView.as_view()
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteFileView(LoginRequiredMixin, RedirectView):
|
||||||
|
def get_redirect_url(self, *args, **kwargs):
|
||||||
|
file = get_object_or_404(File, slug=kwargs["slug"])
|
||||||
|
if file.user == self.request.user:
|
||||||
|
file.delete()
|
||||||
|
return reverse("files:main")
|
||||||
|
|
||||||
|
|
||||||
|
delete_file_view = DeleteFileView.as_view()
|
||||||
|
|
||||||
|
|
||||||
class FileFolderView(DetailView):
|
class FileFolderView(DetailView):
|
||||||
template_name = "files/folder.html"
|
template_name = "files/folder.html"
|
||||||
model = Folder
|
model = Folder
|
||||||
|
|
|
@ -85,7 +85,7 @@
|
||||||
{% if ACCOUNT_ALLOW_REGISTRATION %}
|
{% if ACCOUNT_ALLOW_REGISTRATION %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'account_signup' %}" class="text-muted nav-link px-sm-0 px-2 {% active_link 'account_signup' %}">
|
<a href="{% url 'account_signup' %}" class="text-muted nav-link px-sm-0 px-2 {% active_link 'account_signup' %}">
|
||||||
<i class="fs-5 bi-person"></i><span class="ms-1 d-none d-sm-inline">Sign up</span> </a>
|
<i class="fs-5 bi-person"></i><span class="ms-1 d-none d-sm-inline">Register</span> </a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li>
|
<li>
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row m-2">
|
<div class="row m-2">
|
||||||
<div class="col-md-4 col-sm-6">
|
<h1 class="fs-1 text-break mb-4">{{ file.name }}</h1>
|
||||||
<h1 class="fs-1">{{ file.name }}</h1>
|
<div class="col-md-4 col-sm-6 col-xs-auto">
|
||||||
{% 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 %}">
|
||||||
{% if file.user.image_cropped %}<img class="rounded" width="20" src="{{ file.user.image_cropped.url }}" alt=""> {% endif %}
|
{% if file.user.image_cropped %}<img class="rounded" width="20" src="{{ file.user.image_cropped.url }}" alt=""> {% endif %}
|
||||||
|
@ -23,11 +23,30 @@
|
||||||
{% if file.description %}
|
{% if file.description %}
|
||||||
<p>{{ file.description }}</p>
|
<p>{{ file.description }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if file.private %}
|
||||||
|
<p>File is private</p>
|
||||||
|
{% else %}
|
||||||
|
<p>File is public{% if file.short_link %},
|
||||||
|
<a href="{{ file.short_link.get_absolute_url }}">short link</a><button class="btn" data-clipboard-text="{{ request.get_host }}{{ file.short_link.get_absolute_url }}">
|
||||||
|
<i style="font-size: 0.8em" class="bi bi-clipboard ml-2"></i>
|
||||||
|
</button>
|
||||||
|
{% endif %}</p>
|
||||||
|
{% endif %}
|
||||||
|
<div class="mt-4 text-center justify-content-sm-evenly justify-content-md-start gap-3 align-items-md-start align-items-sm-center d-flex">
|
||||||
|
<a class="btn btn-success fs-6" href="{{ file.file.url }}" download><i class="bi bi-download"></i> Download</a>
|
||||||
|
{% if has_perm %}
|
||||||
|
<a class="btn btn-danger fs-6" href="{% url 'files:delete' slug=file.slug %}"><i class="bi bi-trash"></i> Delete</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-8 col-sm-10">
|
<div class="col-md-8 col-sm-10 col-xs-auto">
|
||||||
{% autoescape off %}
|
{% autoescape off %}
|
||||||
{{ preview_content }}
|
{{ preview_content }}
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.10/clipboard.min.js"></script>
|
||||||
|
<script>
|
||||||
|
new ClipboardJS('.btn');
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -48,12 +48,57 @@ def create_model_link(sender, instance, created, **kwargs):
|
||||||
return
|
return
|
||||||
if hasattr(instance, "creator"):
|
if hasattr(instance, "creator"):
|
||||||
link.creator = instance.creator
|
link.creator = instance.creator
|
||||||
|
elif hasattr(instance, "user"):
|
||||||
|
link.creator = instance.user
|
||||||
|
elif hasattr(instance, "owner"):
|
||||||
|
link.creator = instance.owner
|
||||||
|
|
||||||
link.save()
|
link.save()
|
||||||
instance.short_link = link
|
instance.short_link = link
|
||||||
instance.save()
|
instance.save()
|
||||||
|
|
||||||
|
|
||||||
|
def update_model_link(sender, instance, **kwargs):
|
||||||
|
model = sender
|
||||||
|
if instance.id:
|
||||||
|
previous = model.objects.get(id=instance.id)
|
||||||
|
prev_private = False
|
||||||
|
cur_private = False
|
||||||
|
if hasattr(instance, "private"):
|
||||||
|
if instance.private:
|
||||||
|
cur_private = True
|
||||||
|
if hasattr(instance, "public"):
|
||||||
|
if not instance.public:
|
||||||
|
cur_private = True
|
||||||
|
if hasattr(previous, "private"):
|
||||||
|
if previous.private:
|
||||||
|
prev_private = True
|
||||||
|
if hasattr(previous, "public"):
|
||||||
|
if not previous.public:
|
||||||
|
prev_private = True
|
||||||
|
|
||||||
|
if prev_private != cur_private:
|
||||||
|
if prev_private:
|
||||||
|
# instance was private, public now, need to create short link
|
||||||
|
if hasattr(instance, "short_link"):
|
||||||
|
if not instance.short_link:
|
||||||
|
link = Link(source=instance.get_absolute_url())
|
||||||
|
if hasattr(instance, "creator"):
|
||||||
|
link.creator = instance.creator
|
||||||
|
elif hasattr(instance, "user"):
|
||||||
|
link.creator = instance.user
|
||||||
|
elif hasattr(instance, "owner"):
|
||||||
|
link.creator = instance.owner
|
||||||
|
link.save()
|
||||||
|
instance.short_link = link
|
||||||
|
else:
|
||||||
|
# instance was public, private now, need to delete short link
|
||||||
|
if hasattr(previous, "short_link"):
|
||||||
|
if previous.short_link:
|
||||||
|
previous.short_link.delete()
|
||||||
|
instance.short_link = None
|
||||||
|
|
||||||
|
|
||||||
class ShortLink(SlugModel):
|
class ShortLink(SlugModel):
|
||||||
short_link: Link | None = models.ForeignKey(
|
short_link: Link | None = models.ForeignKey(
|
||||||
"shortener.Link", blank=True, null=True, on_delete=models.SET_NULL
|
"shortener.Link", blank=True, null=True, on_delete=models.SET_NULL
|
||||||
|
@ -63,6 +108,7 @@ class ShortLink(SlugModel):
|
||||||
def __init_subclass__(cls, **kwargs):
|
def __init_subclass__(cls, **kwargs):
|
||||||
super().__init_subclass__(**kwargs)
|
super().__init_subclass__(**kwargs)
|
||||||
models.signals.post_save.connect(create_model_link, sender=cls)
|
models.signals.post_save.connect(create_model_link, sender=cls)
|
||||||
|
models.signals.pre_save.connect(update_model_link, sender=cls)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
default="Lm4alUqtub6qQT4MnV4NmtXQP02RCBtmGj1bJhyDho07Bkjk9WFZxGtwpnLNQGJQ",
|
default="Lm4alUqtub6qQT4MnV4NmtXQP02RCBtmGj1bJhyDho07Bkjk9WFZxGtwpnLNQGJQ",
|
||||||
)
|
)
|
||||||
# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
|
# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
|
||||||
ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1"]
|
ALLOWED_HOSTS = ["*"]
|
||||||
|
CSRF_TRUSTED_ORIGINS = ["http://127.0.0.1", "https://*.akarpov.ru"]
|
||||||
|
|
||||||
# CACHES
|
# CACHES
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in New Issue
Block a user