updated previews, fixed short links, added og tags, download delete

This commit is contained in:
Alexander Karpov 2023-04-13 00:54:44 +03:00
parent e7d78d59ec
commit d4f68935a1
14 changed files with 141 additions and 31 deletions

View File

@ -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,
}

View File

@ -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 = (
""" """

View File

@ -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">

View File

@ -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"""

View File

@ -1 +1 @@
from . import mp4 # noqa from . import basic, mp4 # noqa

View 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

View File

@ -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>

View File

@ -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

View File

@ -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"),
] ]

View File

@ -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

View File

@ -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>

View File

@ -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 %}

View File

@ -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):

View File

@ -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
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------