mirror of
https://github.com/Alexander-D-Karpov/akarpov
synced 2024-11-25 16:23:43 +03:00
added text previews, bug fixes
This commit is contained in:
parent
d4f68935a1
commit
4966884646
|
@ -1,6 +1,9 @@
|
|||
from . import audio, image, video
|
||||
from . import application, audio, image, text, video
|
||||
|
||||
previews = {
|
||||
"application": {
|
||||
"zip": application.zip.view,
|
||||
},
|
||||
"audio": {
|
||||
"aac": audio.basic.view,
|
||||
"mpeg": audio.basic.view,
|
||||
|
@ -14,12 +17,19 @@
|
|||
"jpeg": image.basic.view,
|
||||
"png": image.basic.view,
|
||||
"avif": image.basic.view,
|
||||
"bmp": image.basic.view,
|
||||
},
|
||||
"text": {"css": text.common.view, "plain": text.plain.view},
|
||||
}
|
||||
|
||||
source_code = {}
|
||||
for ext in text.common.language_previews.keys():
|
||||
source_code[ext] = text.common.view
|
||||
|
||||
extensions = {
|
||||
"mp4": video.mp4.view,
|
||||
"mp3": audio.basic.view,
|
||||
"avif": image.basic.view,
|
||||
"bmp": image.basic.view,
|
||||
"mov": video.basic.view,
|
||||
}
|
||||
} | source_code
|
||||
|
|
1
akarpov/files/previews/application/__init__.py
Normal file
1
akarpov/files/previews/application/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from . import zip # noqa
|
141
akarpov/files/previews/application/zip.py
Normal file
141
akarpov/files/previews/application/zip.py
Normal file
|
@ -0,0 +1,141 @@
|
|||
import zipfile
|
||||
from collections import defaultdict
|
||||
|
||||
from akarpov.files.models import File
|
||||
|
||||
FILE_MARKER = "<files>"
|
||||
|
||||
|
||||
def attach(branch, trunk):
|
||||
"""
|
||||
Insert a branch of directories on its trunk.
|
||||
"""
|
||||
parts = branch.split("/", 1)
|
||||
if len(parts) == 1: # branch is a file
|
||||
trunk[FILE_MARKER].append(parts[0])
|
||||
else:
|
||||
node, others = parts
|
||||
if node not in trunk:
|
||||
trunk[node] = defaultdict(dict, ((FILE_MARKER, []),))
|
||||
attach(others, trunk[node])
|
||||
|
||||
|
||||
def to_li(d, indent=0):
|
||||
"""
|
||||
Convert tree like structure to html ul li
|
||||
"""
|
||||
res = ""
|
||||
for key, value in d.items():
|
||||
if key != FILE_MARKER:
|
||||
if indent >= 2 or str(key).startswith("."):
|
||||
in_res = (
|
||||
"""
|
||||
<li data-jstree='{ "opened" : false }'>
|
||||
"""
|
||||
+ str(key)
|
||||
+ "<ul>"
|
||||
)
|
||||
else:
|
||||
in_res = (
|
||||
"""
|
||||
<li data-jstree='{ "opened" : true }'>
|
||||
"""
|
||||
+ str(key)
|
||||
+ "<ul>"
|
||||
)
|
||||
r_res = ""
|
||||
c_res = """</ul></li>"""
|
||||
if isinstance(value, dict):
|
||||
r_res += to_li(value, indent + 1)
|
||||
else:
|
||||
if value is not str and isinstance(value, list):
|
||||
for v in value:
|
||||
if v:
|
||||
r_res += (
|
||||
"""<li data-jstree='{ "opened" : true, "type" : "file" }'>"""
|
||||
+ str(v)
|
||||
+ "</li>"
|
||||
)
|
||||
else:
|
||||
if value:
|
||||
r_res += (
|
||||
"""<li data-jstree='{ "opened" : true, "type" : "file" }'>"""
|
||||
+ str(value)
|
||||
+ "</li>"
|
||||
)
|
||||
res += in_res + r_res + c_res
|
||||
else:
|
||||
if value is not str and isinstance(value, list):
|
||||
for v in value:
|
||||
if v:
|
||||
res += (
|
||||
"""<li data-jstree='{ "opened" : true, "type" : "file" }'>"""
|
||||
+ str(v)
|
||||
+ "</li>"
|
||||
)
|
||||
else:
|
||||
if value:
|
||||
res += (
|
||||
"""<li data-jstree='{ "opened" : true, "type" : "file" }'>"""
|
||||
+ str(value)
|
||||
+ "</li>"
|
||||
)
|
||||
return res
|
||||
|
||||
|
||||
def view(file: File) -> (str, str):
|
||||
zip = zipfile.ZipFile(file.file.path)
|
||||
|
||||
root = defaultdict(dict, ((FILE_MARKER, []),))
|
||||
for line in zip.namelist():
|
||||
attach(line, root)
|
||||
r = str(to_li(root))
|
||||
|
||||
static = """
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css" />
|
||||
"""
|
||||
content = (
|
||||
"""
|
||||
<div id="jstree_demo_div">
|
||||
<ul data-jstree='{ "opened" : true }>"""
|
||||
+ f"""
|
||||
{file.file.path.split("/")[-1]}
|
||||
"""
|
||||
+ """
|
||||
<li data-jstree='{ "opened" : true }>
|
||||
"""
|
||||
+ f"""
|
||||
{r}
|
||||
</li>
|
||||
</ul>
|
||||
</div> """
|
||||
+ """
|
||||
<script src="/static/js/jquery.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/jstree.min.js"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
|
||||
|
||||
$('#jstree_demo_div').jstree({
|
||||
"core" : {
|
||||
"themes" : {
|
||||
"responsive": false
|
||||
}
|
||||
},
|
||||
"types" : {
|
||||
"default" : {
|
||||
"icon" : "bi bi-folder"
|
||||
},
|
||||
"file" : {
|
||||
"icon" : "bi bi-file-earmark-fill"
|
||||
}
|
||||
},
|
||||
"plugins": ["types"]
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
"""
|
||||
)
|
||||
return static, content
|
|
@ -13,7 +13,7 @@ def view(file: File) -> (str, str):
|
|||
</div>
|
||||
<div id="images">
|
||||
</div
|
||||
<script src="/static/js/jquery.js"></script>
|
||||
<script src="/static/js/jquery.min.js"></script>
|
||||
"""
|
||||
+ """
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/viewerjs/1.11.3/viewer.min.js">
|
||||
|
|
1
akarpov/files/previews/text/__init__.py
Normal file
1
akarpov/files/previews/text/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from . import common, plain # noqa
|
64
akarpov/files/previews/text/common.py
Normal file
64
akarpov/files/previews/text/common.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
import html
|
||||
|
||||
from akarpov.files.models import File
|
||||
|
||||
language_previews = {
|
||||
"jsx": "javascript",
|
||||
"js": "javascript",
|
||||
"tsx": "typescript",
|
||||
"ts": "typescript",
|
||||
"css": "css",
|
||||
"py": "python",
|
||||
"go": "go",
|
||||
"java": "java",
|
||||
"php": "php",
|
||||
"cs": "csharp",
|
||||
"swift": "swift",
|
||||
"r": "r",
|
||||
"rb": "ruby",
|
||||
"c": "c",
|
||||
"cpp": "cpp",
|
||||
"mlx": "matlab",
|
||||
"scala": "scala",
|
||||
"sc": "scala",
|
||||
"sql": "sql",
|
||||
"html": "html",
|
||||
"rs": "rust",
|
||||
"pl": "perl",
|
||||
"PL": "perl",
|
||||
}
|
||||
|
||||
|
||||
def view(file: File) -> (str, str):
|
||||
extension = file.file.path.split(".")[-1]
|
||||
if extension in language_previews:
|
||||
extension = language_previews[extension]
|
||||
static = f"""
|
||||
<meta property="og:title" content="{file.name}" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/atom-one-light.min.css">
|
||||
"""
|
||||
content = "<pre>"
|
||||
with file.file.open("r") as f:
|
||||
lines = f.readlines()
|
||||
for line in lines:
|
||||
content += (
|
||||
f"""<div class='code language-{extension}'>{html.escape(line)}</div>"""
|
||||
)
|
||||
content += (
|
||||
"""</pre>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
|
||||
"""
|
||||
+ f"""
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/{extension}.min.js"></script>
|
||||
"""
|
||||
+ """
|
||||
<script>
|
||||
hljs.configure({ ignoreUnescapedHTML: true })
|
||||
document.querySelectorAll('div.code').forEach(el => {
|
||||
hljs.highlightElement(el);
|
||||
});
|
||||
</script>
|
||||
"""
|
||||
)
|
||||
|
||||
return static, content
|
23
akarpov/files/previews/text/plain.py
Normal file
23
akarpov/files/previews/text/plain.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
import html
|
||||
|
||||
from akarpov.files.models import File
|
||||
from akarpov.files.previews import text
|
||||
from akarpov.files.previews.text.common import language_previews
|
||||
|
||||
|
||||
def view(file: File) -> (str, str):
|
||||
extension = file.file.path.split(".")[-1]
|
||||
if hasattr(text, extension):
|
||||
return getattr(text, extension).view(file)
|
||||
elif extension in language_previews:
|
||||
return text.common.view(file)
|
||||
static = f"""
|
||||
<meta property="og:title" content="{file.name}" />
|
||||
"""
|
||||
content = "<pre>"
|
||||
with file.file.open("r") as f:
|
||||
lines = f.readlines()
|
||||
for line in lines:
|
||||
content += f"""<div class='code language-plaintext'>{html.escape(line)}</div>"""
|
||||
content += "</pre>"
|
||||
return static, content
|
|
@ -5,7 +5,7 @@ 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" 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" />
|
||||
|
|
|
@ -43,15 +43,15 @@ def get_context_data(self, **kwargs):
|
|||
static = ""
|
||||
content = ""
|
||||
if self.object.file_type:
|
||||
if self.object.file_type == "application/octet-stream":
|
||||
extension = self.object.file.path.split(".")[-1]
|
||||
if extension in extensions:
|
||||
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)
|
||||
t1, t2 = self.object.file_type.split("/")
|
||||
extension = self.object.file.path.split(".")[-1]
|
||||
loaded = False
|
||||
if t1 in previews:
|
||||
if t2 in previews[t1]:
|
||||
static, content = previews[t1][t2](self.object)
|
||||
loaded = True
|
||||
if not loaded and extension in extensions:
|
||||
static, content = extensions[extension](self.object)
|
||||
context["preview_static"] = static
|
||||
context["preview_content"] = content
|
||||
return context
|
||||
|
|
44
akarpov/static/css/tomorrow-night-eighties.css
Normal file
44
akarpov/static/css/tomorrow-night-eighties.css
Normal file
|
@ -0,0 +1,44 @@
|
|||
/* Tomorrow Night Eighties Theme */
|
||||
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
|
||||
/* https://jmblog.github.io/color-themes-for-google-code-highlightjs */
|
||||
.tomorrow-comment, pre .comment, pre .title {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.tomorrow-red, pre .variable, pre .attribute, pre .tag, pre .regexp, pre .ruby .constant, pre .xml .tag .title, pre .xml .pi, pre .xml .doctype, pre .html .doctype, pre .css .id, pre .css .class, pre .css .pseudo {
|
||||
color: #f2777a;
|
||||
}
|
||||
|
||||
.tomorrow-orange, pre .number, pre .preprocessor, pre .built_in, pre .literal, pre .params, pre .constant {
|
||||
color: #f99157;
|
||||
}
|
||||
|
||||
.tomorrow-yellow, pre .class, pre .ruby .class .title, pre .css .rules .attribute {
|
||||
color: #ffcc66;
|
||||
}
|
||||
|
||||
.tomorrow-green, pre .string, pre .value, pre .inheritance, pre .header, pre .ruby .symbol, pre .xml .cdata {
|
||||
color: #99cc99;
|
||||
}
|
||||
|
||||
.tomorrow-aqua, pre .css .hexcolor {
|
||||
color: #66cccc;
|
||||
}
|
||||
|
||||
.tomorrow-blue, pre .function, pre .python .decorator, pre .python .title, pre .ruby .function .title, pre .ruby .title .keyword, pre .perl .sub, pre .javascript .title, pre .coffeescript .title {
|
||||
color: #6699cc;
|
||||
}
|
||||
|
||||
.tomorrow-purple, pre .keyword, pre .javascript .function {
|
||||
color: #cc99cc;
|
||||
}
|
||||
|
||||
pre code {
|
||||
display: block;
|
||||
background: #2d2d2d;
|
||||
color: #cccccc;
|
||||
font-family: Menlo, Monaco, Consolas, monospace;
|
||||
line-height: 1.5;
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
}
|
2
akarpov/static/js/jquery.min.js
vendored
Normal file
2
akarpov/static/js/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -2,7 +2,7 @@
|
|||
{% load humanize static %}
|
||||
|
||||
{% block javascript %}
|
||||
<script src="{% static 'js/jquery.js' %}"></script>
|
||||
<script src="{% static 'js/jquery.min.js' %}"></script>
|
||||
<script src="{% static 'js/jquery.ui.widget.js' %}"></script>
|
||||
<!-- The Iframe Transport is required for browsers without support for XHR file uploads -->
|
||||
<script src="{% static 'js/jquery.iframe-transport.js' %}"></script>
|
||||
|
@ -36,7 +36,7 @@
|
|||
{% for folder in folders %}
|
||||
{{ folder.name }}
|
||||
{% endfor %}
|
||||
<div class="row">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-2 col-xxl-2 col-md-4 col-sm-6 col-xs-12 mb-3 m-3 d-flex align-items-stretch card">
|
||||
<div class="card-body d-flex flex-column justify-content-center align-items-center">
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{% load static %}
|
||||
|
||||
{% block javascript %}
|
||||
<script src="{% static 'js/jquery.js' %}"></script>
|
||||
<script src="{% static 'js/jquery.min.js' %}"></script>
|
||||
<script src="{% static 'js/jquery.ui.widget.js' %}"></script>
|
||||
<!-- The Iframe Transport is required for browsers without support for XHR file uploads -->
|
||||
<script src="{% static 'js/jquery.iframe-transport.js' %}"></script>
|
||||
|
|
Loading…
Reference in New Issue
Block a user