feat: add conversion page
This commit is contained in:
parent
4a4949b474
commit
460ae94925
67
src/public/conversion/index.html
Normal file
67
src/public/conversion/index.html
Normal file
@ -0,0 +1,67 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Melies - Conversion</title>
|
||||
<link rel="stylesheet" href="/static/css/base.css">
|
||||
<link rel="stylesheet" href="/static/css/conversion.css">
|
||||
<link rel="shortcut icon" href="/static/images/icon3.svg" type="image/svg+xml">
|
||||
<script src="/static/js/conversion.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Media Conversion</h1>
|
||||
</header>
|
||||
<main>
|
||||
<button id="folder-template" class="template file folder">
|
||||
<img src="/static/images/collection.svg">
|
||||
<div class="name"></div>
|
||||
<div class="children"><span class="num"></span> element(s)</div>
|
||||
</button>
|
||||
<button id="media-template" class="template file media">
|
||||
<img src="/static/images/film.svg">
|
||||
<div class="name"></div>
|
||||
<div class="info"><span class="size"></span> / <span class="ext"></span></div>
|
||||
</button>
|
||||
<div id="selected-template" class="template file">
|
||||
<button class="deselect">Deselect</button>
|
||||
<div class="name"></div>
|
||||
</div>
|
||||
<label id="agent-template" class="template agent">
|
||||
<input type="radio" name="selected-agent">
|
||||
<div class="container">
|
||||
<div class="icon"></div>
|
||||
<div class="name"></div>
|
||||
</div>
|
||||
</label>
|
||||
<div id="to-convert-panel" class="panel">
|
||||
<h2 class="title">To convert</h2>
|
||||
<button id="up">Parent directory</button>
|
||||
<div id="no-file" class="empty-msg">Director is empty</div>
|
||||
<div id="files"></div>
|
||||
</div>
|
||||
<div id="selected-panel" class="panel">
|
||||
<h2 class="title">Selected</h2>
|
||||
<div class="toolbar">
|
||||
<button id="show-files" class="hidden"><img src="/static/images/prev.svg">Back to files</button>
|
||||
<button id="deselect-all">Deselect All</button>
|
||||
<button id="continue" disabled>
|
||||
<div class="disabled">Select a media</div>
|
||||
<div class="enabled">Continue<img src="/static/images/next.svg"></div>
|
||||
</button>
|
||||
</div>
|
||||
<div id="selected"></div>
|
||||
</div>
|
||||
<div id="agents-panel" class="panel hidden">
|
||||
<h2 class="title">Agents</h2>
|
||||
<div id="no-agent" class="empty-msg show">No registered agent</div>
|
||||
<div id="agents"></div>
|
||||
<button id="convert" disabled>
|
||||
<div class="disabled">Select an agent</div>
|
||||
<div class="enabled">Convert</div>
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
@ -6,6 +6,7 @@
|
||||
<title>Melies - Metadata Editor</title>
|
||||
<link rel="stylesheet" href="/static/css/base.css">
|
||||
<link rel="stylesheet" href="/static/css/edit.css">
|
||||
<link rel="shortcut icon" href="/static/images/icon3.svg" type="image/svg+xml">
|
||||
<script src="/static/js/edit.mjs" type="module"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
265
src/public/static/css/conversion.css
Normal file
265
src/public/static/css/conversion.css
Normal file
@ -0,0 +1,265 @@
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
main {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1em;
|
||||
flex: 1;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0.8em;
|
||||
border: solid black 2px;
|
||||
overflow: hidden;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0.4em 0;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
padding: 0.4em 0.8em;
|
||||
border-radius: 0.4em;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
border: solid #c5c5c5 2px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
|
||||
&:hover {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
}
|
||||
|
||||
#up {
|
||||
width: 100%;
|
||||
padding: 0.8em 1.6em;
|
||||
border-radius: 1.2em;
|
||||
|
||||
&:not(.show) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#files {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 12em);
|
||||
grid-auto-rows: 12em;
|
||||
gap: 0.8em;
|
||||
place-items: center;
|
||||
padding: 0.8em 0;
|
||||
justify-content: space-evenly;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.file {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
padding: 0.4em;
|
||||
border-radius: 1.2em;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
gap: 0.2em;
|
||||
border: solid transparent 2px;
|
||||
|
||||
&.selected {
|
||||
border-color: rgb(153, 226, 153);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 5em;
|
||||
height: 5em;
|
||||
}
|
||||
|
||||
.name {
|
||||
overflow-wrap: anywhere;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
flex-shrink: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.info, .children {
|
||||
font-size: 80%;
|
||||
color: rgb(75, 75, 75);
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#selected {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.file {
|
||||
display: flex;
|
||||
padding: 0.4em 0.8em;
|
||||
gap: 0.4em;
|
||||
align-items: center;
|
||||
|
||||
&:nth-child(even) {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.deselect {
|
||||
background-color: #ffd8c6;
|
||||
|
||||
&:hover {
|
||||
background-color: #ffc3a7;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
gap: 0.4em;
|
||||
|
||||
button, button .enabled {
|
||||
display: flex;
|
||||
gap: 0.2em;
|
||||
align-items: center;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 1.4em;
|
||||
height: 1.4em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-msg {
|
||||
color: #4d4d4d;
|
||||
font-style: italic;
|
||||
font-size: 120%;
|
||||
text-align: center;
|
||||
padding: 1.2em 0.4em;
|
||||
|
||||
&:not(.show) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#agents {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 12em);
|
||||
grid-auto-rows: 12em;
|
||||
gap: 0.8em;
|
||||
place-items: center;
|
||||
padding: 0.8em 0;
|
||||
justify-content: space-evenly;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.agent {
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
padding: 0.4em;
|
||||
border-radius: 1.2em;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
gap: 0.2em;
|
||||
border: none;
|
||||
|
||||
&.selected {
|
||||
border-color: #99e299;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 8em;
|
||||
height: 8em;
|
||||
mask-image: url("/static/images/agent.svg");
|
||||
mask-size: contain;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
.name {
|
||||
overflow-wrap: anywhere;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 120%;
|
||||
flex-shrink: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
input:checked + .container {
|
||||
.icon {
|
||||
background-color: #1bb11b;
|
||||
}
|
||||
|
||||
.name {
|
||||
color: #1bb11b;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#convert {
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
#continue, #convert {
|
||||
&:not(:disabled) {
|
||||
.disabled {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
.enabled {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
498
src/public/static/images/agent.svg
Normal file
498
src/public/static/images/agent.svg
Normal file
@ -0,0 +1,498 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="128"
|
||||
height="128"
|
||||
viewBox="0 0 128 128"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="agent.svg"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="true"
|
||||
inkscape:zoom="5.7798541"
|
||||
inkscape:cx="66.610678"
|
||||
inkscape:cy="60.468655"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1016"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1">
|
||||
<inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingx="1"
|
||||
spacingy="1"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="16"
|
||||
enabled="true"
|
||||
visible="true" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs1">
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect17"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect16"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,2.3893809,0,1 @ F,0,1,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect15"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,1,1,0,0,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,1.9863443,0,1 @ F,0,1,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,3.1885909,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,3.1885909,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,3.1885909,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,3.1885909,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,3.1885909,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,3.1885909,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="copy_rotate"
|
||||
starting_point="80,71.686292"
|
||||
origin="64,71.686292"
|
||||
id="path-effect8"
|
||||
is_visible="true"
|
||||
lpeversion="1.2"
|
||||
lpesatellites=""
|
||||
method="fuse_paths"
|
||||
num_copies="8"
|
||||
starting_angle="0"
|
||||
rotation_angle="45"
|
||||
gap="-0.01"
|
||||
copies_to_360="true"
|
||||
mirror_copies="false"
|
||||
split_items="false"
|
||||
link_styles="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect7"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1 @ F,0,1,1,0,2.8348774,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect4"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,3,0,1 @ F,0,1,1,0,3,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect3"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,9,0,1 @ F,0,1,1,0,9,0,1 @ F,0,1,1,0,9,0,1 @ F,0,1,1,0,9,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect4-7"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,3,0,1 @ F,0,1,1,0,3,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect4-2"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,3,0,1 @ F,0,1,1,0,3,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect4-0"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,3,0,1 @ F,0,1,1,0,3,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect4-7-9"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,3,0,1 @ F,0,1,1,0,3,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect4-2-3"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,3,0,1 @ F,0,1,1,0,3,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect4-79"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,3,0,1 @ F,0,1,1,0,3,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect4-7-2"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,3,0,1 @ F,0,1,1,0,3,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect4-2-0"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,3,0,1 @ F,0,1,1,0,3,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect4-8"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,3,0,1 @ F,0,1,1,0,3,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect4-7-97"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,3,0,1 @ F,0,1,1,0,3,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect4-2-36"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,3,0,1 @ F,0,1,1,0,3,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 32,41 v 46 a 9,9 45 0 0 9,9 h 46 a 9,9 135 0 0 9,-9 V 41 A 9,9 45 0 0 87,32 H 41 a 9,9 135 0 0 -9,9 z"
|
||||
id="path3"
|
||||
inkscape:path-effect="#path-effect3"
|
||||
inkscape:original-d="M 32,32 V 96 H 96 V 32 Z" />
|
||||
<g
|
||||
id="g4">
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 32,45 H 19 a 3,3 135 0 0 -3,3 3,3 45 0 0 3,3 h 13"
|
||||
id="path4"
|
||||
inkscape:path-effect="#path-effect4"
|
||||
inkscape:original-d="M 32,45 H 16 v 6 h 16"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 32,45 H 19 a 3,3 135 0 0 -3,3 3,3 45 0 0 3,3 h 13"
|
||||
id="path4-3"
|
||||
inkscape:path-effect="#path-effect4-7"
|
||||
inkscape:original-d="M 32,45 H 16 v 6 h 16"
|
||||
sodipodi:nodetypes="cccc"
|
||||
transform="translate(0,16)" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 32,45 H 19 a 3,3 135 0 0 -3,3 3,3 45 0 0 3,3 h 13"
|
||||
id="path4-1"
|
||||
inkscape:path-effect="#path-effect4-2"
|
||||
inkscape:original-d="M 32,45 H 16 v 6 h 16"
|
||||
sodipodi:nodetypes="cccc"
|
||||
transform="translate(0,32)" />
|
||||
</g>
|
||||
<g
|
||||
id="g4-0"
|
||||
transform="matrix(-1,0,0,1,128,0)">
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 32,45 H 19 a 3,3 135 0 0 -3,3 3,3 45 0 0 3,3 h 13"
|
||||
id="path4-6"
|
||||
inkscape:path-effect="#path-effect4-0"
|
||||
inkscape:original-d="M 32,45 H 16 v 6 h 16"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 32,45 H 19 a 3,3 135 0 0 -3,3 3,3 45 0 0 3,3 h 13"
|
||||
id="path4-3-2"
|
||||
inkscape:path-effect="#path-effect4-7-9"
|
||||
inkscape:original-d="M 32,45 H 16 v 6 h 16"
|
||||
sodipodi:nodetypes="cccc"
|
||||
transform="translate(0,16)" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 32,45 H 19 a 3,3 135 0 0 -3,3 3,3 45 0 0 3,3 h 13"
|
||||
id="path4-1-6"
|
||||
inkscape:path-effect="#path-effect4-2-3"
|
||||
inkscape:original-d="M 32,45 H 16 v 6 h 16"
|
||||
sodipodi:nodetypes="cccc"
|
||||
transform="translate(0,32)" />
|
||||
</g>
|
||||
<g
|
||||
id="g4-3"
|
||||
transform="rotate(90,64,64)">
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 32,45 H 19 a 3,3 135 0 0 -3,3 3,3 45 0 0 3,3 h 13"
|
||||
id="path4-7"
|
||||
inkscape:path-effect="#path-effect4-79"
|
||||
inkscape:original-d="M 32,45 H 16 v 6 h 16"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 32,45 H 19 a 3,3 135 0 0 -3,3 3,3 45 0 0 3,3 h 13"
|
||||
id="path4-3-5"
|
||||
inkscape:path-effect="#path-effect4-7-2"
|
||||
inkscape:original-d="M 32,45 H 16 v 6 h 16"
|
||||
sodipodi:nodetypes="cccc"
|
||||
transform="translate(0,16)" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 32,45 H 19 a 3,3 135 0 0 -3,3 3,3 45 0 0 3,3 h 13"
|
||||
id="path4-1-9"
|
||||
inkscape:path-effect="#path-effect4-2-0"
|
||||
inkscape:original-d="M 32,45 H 16 v 6 h 16"
|
||||
sodipodi:nodetypes="cccc"
|
||||
transform="translate(0,32)" />
|
||||
</g>
|
||||
<g
|
||||
id="g4-2"
|
||||
transform="rotate(-90,64,64)">
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 32,45 H 19 a 3,3 135 0 0 -3,3 3,3 45 0 0 3,3 h 13"
|
||||
id="path4-9"
|
||||
inkscape:path-effect="#path-effect4-8"
|
||||
inkscape:original-d="M 32,45 H 16 v 6 h 16"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 32,45 H 19 a 3,3 135 0 0 -3,3 3,3 45 0 0 3,3 h 13"
|
||||
id="path4-3-3"
|
||||
inkscape:path-effect="#path-effect4-7-97"
|
||||
inkscape:original-d="M 32,45 H 16 v 6 h 16"
|
||||
sodipodi:nodetypes="cccc"
|
||||
transform="translate(0,16)" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 32,45 H 19 a 3,3 135 0 0 -3,3 3,3 45 0 0 3,3 h 13"
|
||||
id="path4-1-1"
|
||||
inkscape:path-effect="#path-effect4-2-36"
|
||||
inkscape:original-d="M 32,45 H 16 v 6 h 16"
|
||||
sodipodi:nodetypes="cccc"
|
||||
transform="translate(0,32)" />
|
||||
</g>
|
||||
<path
|
||||
id="path15"
|
||||
style="baseline-shift:baseline;display:inline;overflow:visible;fill:#000000;fill-opacity:0.33900854;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;enable-background:accumulate;stop-color:#000000;stop-opacity:1;stroke-opacity:1;stroke-width:2;stroke-dasharray:none"
|
||||
d="M 63.496094 38.013672 A 4.2237119 4.2237119 0 0 0 59.345703 41.453125 L 58.523438 45.792969 A 2.2921583 2.2921583 0 0 1 54.980469 47.261719 L 51.337891 44.777344 A 4.2259625 4.2259625 0 0 0 45.970703 45.28125 L 45.267578 45.982422 A 4.2239053 4.2239053 0 0 0 44.765625 51.349609 L 47.251953 54.998047 A 2.292144 2.292144 0 0 1 45.785156 58.541016 L 41.453125 59.363281 A 4.2252807 4.2252807 0 0 0 38.013672 63.513672 L 38.013672 64.505859 A 4.2247307 4.2247307 0 0 0 41.453125 68.65625 L 45.792969 69.478516 A 2.2921527 2.2921527 0 0 1 47.261719 73.021484 L 44.777344 76.664062 A 4.2252132 4.2252132 0 0 0 45.28125 82.03125 L 45.982422 82.732422 A 4.2246351 4.2246351 0 0 0 51.349609 83.234375 L 54.998047 80.748047 A 2.292138 2.292138 0 0 1 58.541016 82.214844 L 59.363281 86.546875 A 4.2263602 4.2263602 0 0 0 63.513672 89.986328 L 64.503906 89.988281 A 4.2236546 4.2236546 0 0 0 68.654297 86.548828 L 69.476562 82.208984 A 2.2921525 2.2921525 0 0 1 73.019531 80.740234 L 76.662109 83.224609 A 4.2259565 4.2259565 0 0 0 82.029297 82.720703 L 82.732422 82.019531 A 4.2239049 4.2239049 0 0 0 83.234375 76.652344 L 80.748047 73.003906 A 2.292144 2.292144 0 0 1 82.214844 69.460938 L 86.546875 68.638672 A 4.2252807 4.2252807 0 0 0 89.986328 64.488281 L 89.986328 63.496094 A 4.2247307 4.2247307 0 0 0 86.546875 59.345703 L 82.207031 58.523438 A 2.2921527 2.2921527 0 0 1 80.738281 54.980469 L 83.222656 51.337891 A 4.2259563 4.2259563 0 0 0 82.71875 45.970703 L 82.017578 45.267578 A 4.2239034 4.2239034 0 0 0 76.650391 44.765625 L 73.001953 47.251953 A 2.2922228 2.2922228 0 0 1 69.457031 45.785156 L 68.638672 41.455078 A 4.226441 4.226441 0 0 0 64.486328 38.015625 L 63.496094 38.013672 z M 64 54 A 10 10 0 0 1 74 64 A 10 10 0 0 1 64 74 A 10 10 0 0 1 54 64 A 10 10 0 0 1 64 54 z " />
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="display:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path8"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:sides="8"
|
||||
sodipodi:cx="64"
|
||||
sodipodi:cy="64"
|
||||
sodipodi:r1="16"
|
||||
sodipodi:r2="14.782073"
|
||||
sodipodi:arg1="0"
|
||||
sodipodi:arg2="0.39269908"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="M 80,64 75.313708,75.313708 64,80 52.686292,75.313708 48,64 52.686292,52.686292 64,48 75.313708,52.686292 Z"
|
||||
transform="rotate(-22.5,64,64)" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 21 KiB |
231
src/public/static/js/conversion.js
Normal file
231
src/public/static/js/conversion.js
Normal file
@ -0,0 +1,231 @@
|
||||
let selected = []
|
||||
let currentPath = []
|
||||
|
||||
const SIZES = ["", "K", "M", "G", "T"]
|
||||
|
||||
function formatSize(bytes) {
|
||||
let order = Math.floor(Math.log10(bytes) / 3)
|
||||
if (bytes > Math.pow(10, (order + 1) * 3) / 2) {
|
||||
order += 1
|
||||
}
|
||||
let size = bytes / Math.pow(10, order * 3)
|
||||
size = Math.round(size * 10) / 10
|
||||
const prefix = SIZES[order]
|
||||
return `${size}${prefix}B`
|
||||
}
|
||||
|
||||
function makeFolder(meta) {
|
||||
const file = document.getElementById("folder-template").cloneNode(true)
|
||||
file.querySelector(".name").innerText = meta.filename
|
||||
file.querySelector(".children .num").innerText = meta.elements
|
||||
file.addEventListener("dblclick", () => {
|
||||
currentPath.push(meta.filename)
|
||||
navigate()
|
||||
})
|
||||
return file
|
||||
}
|
||||
|
||||
function makeMedia(meta) {
|
||||
const file = document.getElementById("media-template").cloneNode(true)
|
||||
file.querySelector(".name").innerText = meta.filename.split(".").slice(0, -1).join(".")
|
||||
file.querySelector(".size").innerText = formatSize(meta.size)
|
||||
file.querySelector(".ext").innerText = meta.filename.split(".").slice(-1)[0].toUpperCase()
|
||||
return file
|
||||
}
|
||||
|
||||
function makeFile(meta) {
|
||||
let file
|
||||
switch (meta.type) {
|
||||
case "folder":
|
||||
file = makeFolder(meta)
|
||||
break
|
||||
case "media":
|
||||
file = makeMedia(meta)
|
||||
break
|
||||
default:
|
||||
throw new Error(`Invalid file type '${meta.type}'`)
|
||||
}
|
||||
|
||||
file.title = meta.filename
|
||||
file.id = null
|
||||
file.dataset.path = meta.path
|
||||
file.classList.remove("template")
|
||||
if (selected.includes(meta.path)) {
|
||||
file.classList.add("selected")
|
||||
}
|
||||
|
||||
file.addEventListener("click", e => {
|
||||
if (e.ctrlKey) {
|
||||
e.preventDefault()
|
||||
toggleSelectFile(meta)
|
||||
}
|
||||
})
|
||||
return file
|
||||
}
|
||||
|
||||
function makeAgent(meta) {
|
||||
const agent = document.getElementById("agent-template").cloneNode(true)
|
||||
agent.classList.remove("template")
|
||||
agent.removeAttribute("id")
|
||||
agent.dataset.uuid = meta.uuid
|
||||
agent.querySelector(".name").innerText = meta.name
|
||||
const input = agent.querySelector("input[type='radio']")
|
||||
input.value = meta.uuid
|
||||
input.addEventListener("change", () => updateConvertBtn())
|
||||
return agent
|
||||
}
|
||||
|
||||
function toggleSelectFile(meta) {
|
||||
if (selected.includes(meta.path)) {
|
||||
deselectFile(meta.path)
|
||||
} else {
|
||||
selectFile(meta)
|
||||
}
|
||||
}
|
||||
|
||||
function selectFile(meta) {
|
||||
selected.push(meta.path)
|
||||
document.querySelector(`.file[data-path='${meta.path}']`)?.classList?.add("selected")
|
||||
const list = document.getElementById("selected")
|
||||
const line = document.getElementById("selected-template").cloneNode(true)
|
||||
line.classList.remove("template")
|
||||
line.id = null
|
||||
line.querySelector(".name").innerText = meta.path
|
||||
line.dataset.path = meta.path
|
||||
line.querySelector(".deselect").addEventListener("click", () => {
|
||||
deselectFile(meta.path)
|
||||
})
|
||||
list.appendChild(line)
|
||||
|
||||
const continueBtn = document.getElementById("continue")
|
||||
continueBtn.disabled = false
|
||||
}
|
||||
|
||||
function deselectFile(path) {
|
||||
selected = selected.filter(p => p !== path)
|
||||
document.querySelector(`#files .file[data-path='${path}']`)?.classList?.remove("selected")
|
||||
document.querySelector(`#selected .file[data-path='${path}']`)?.remove()
|
||||
|
||||
const continueBtn = document.getElementById("continue")
|
||||
continueBtn.disabled = selected.length === 0
|
||||
}
|
||||
|
||||
function deselectAll() {
|
||||
selected.forEach(path => {
|
||||
deselectFile(path)
|
||||
})
|
||||
showFiles()
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object[]} files
|
||||
*/
|
||||
function addFiles(files) {
|
||||
const emptyMsg = document.getElementById("no-file")
|
||||
if (files.length === 0) {
|
||||
emptyMsg.classList.add("show")
|
||||
} else {
|
||||
emptyMsg.classList.remove("show")
|
||||
}
|
||||
const list = document.getElementById("files")
|
||||
const list2 = document.createElement("div")
|
||||
list2.innerHTML = ""
|
||||
const filenames = files.map(meta => meta.filename)
|
||||
// Copy array because sort changes it in place
|
||||
Array.from(filenames).sort().forEach(filename => {
|
||||
const i = filenames.indexOf(filename)
|
||||
const meta = files[i]
|
||||
const file = makeFile(meta)
|
||||
list2.appendChild(file)
|
||||
})
|
||||
list.replaceChildren(...list2.children)
|
||||
|
||||
const upBtn = document.getElementById("up")
|
||||
if (currentPath.length === 0) {
|
||||
upBtn.classList.remove("show")
|
||||
} else {
|
||||
upBtn.classList.add("show")
|
||||
}
|
||||
}
|
||||
|
||||
function navigate() {
|
||||
const url = new URL("/api/files/to_convert", window.location.origin)
|
||||
url.searchParams.set("f", currentPath.join("/"))
|
||||
|
||||
fetch(url.href).then(res => {
|
||||
return res.json()
|
||||
}).then(files => {
|
||||
addFiles(files)
|
||||
})
|
||||
}
|
||||
|
||||
function showFiles() {
|
||||
document.getElementById("show-files").classList.add("hidden")
|
||||
document.getElementById("continue").classList.remove("hidden")
|
||||
document.getElementById("agents-panel").classList.add("hidden")
|
||||
document.getElementById("to-convert-panel").classList.remove("hidden")
|
||||
}
|
||||
|
||||
function showAgents() {
|
||||
document.getElementById("continue").classList.add("hidden")
|
||||
document.getElementById("show-files").classList.remove("hidden")
|
||||
document.getElementById("to-convert-panel").classList.add("hidden")
|
||||
document.getElementById("agents-panel").classList.remove("hidden")
|
||||
}
|
||||
|
||||
function updateConvertBtn() {
|
||||
const agent = document.querySelector("#agents .agent input:checked")
|
||||
const convertBtn = document.getElementById("convert")
|
||||
if (agent) {
|
||||
convertBtn.disabled = false
|
||||
} else {
|
||||
convertBtn.disabled = true
|
||||
}
|
||||
}
|
||||
|
||||
function addAgents(agents) {
|
||||
const emptyMsg = document.getElementById("no-agent")
|
||||
if (agents.length === 0) {
|
||||
emptyMsg.classList.add("show")
|
||||
} else {
|
||||
emptyMsg.classList.remove("show")
|
||||
}
|
||||
const selectedAgent = document.querySelector("#agents .agent input:checked")?.parentElement
|
||||
|
||||
const list = document.getElementById("agents")
|
||||
const list2 = document.createElement("div")
|
||||
list2.innerHTML = ""
|
||||
const agentNames = agents.map(meta => meta.name)
|
||||
// Copy array because sort changes it in place
|
||||
Array.from(agentNames).sort().forEach(name => {
|
||||
const i = agentNames.indexOf(name)
|
||||
const meta = agents[i]
|
||||
const agent = makeAgent(meta)
|
||||
if (selectedAgent?.dataset?.uuid === meta.uuid) {
|
||||
agent.querySelector("input").checked = true
|
||||
}
|
||||
list2.appendChild(agent)
|
||||
})
|
||||
list.replaceChildren(...list2.children)
|
||||
updateConvertBtn()
|
||||
}
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
document.getElementById("up").addEventListener("dblclick", () => {
|
||||
if (currentPath.length !== 0) {
|
||||
currentPath = currentPath.slice(0, -1)
|
||||
navigate()
|
||||
}
|
||||
})
|
||||
document.getElementById("show-files").addEventListener("click", () => {
|
||||
showFiles()
|
||||
})
|
||||
document.getElementById("deselect-all").addEventListener("click", () => {
|
||||
deselectAll()
|
||||
})
|
||||
document.getElementById("continue").addEventListener("click", () => {
|
||||
showAgents()
|
||||
})
|
||||
navigate()
|
||||
})
|
@ -94,7 +94,7 @@ function sortFiles() {
|
||||
}
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
fetch("/api/files").then(res => {
|
||||
fetch("/api/files/metadata").then(res => {
|
||||
return res.json()
|
||||
}).then(files => {
|
||||
addFiles(files)
|
||||
|
113
src/server.py
113
src/server.py
@ -41,11 +41,14 @@ class EnvDefault(argparse.Action):
|
||||
|
||||
class HTTPHandler(SimpleHTTPRequestHandler):
|
||||
SERVER: MeliesServer = None
|
||||
CACHE = {}
|
||||
METADATA_CACHE = {}
|
||||
TO_CONVERT_CACHE = {}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.MAX_PAYLOAD_SIZE: int = self.SERVER.max_payload_size
|
||||
self.DATA_DIR: str = self.SERVER.metadata_dir
|
||||
self.TO_CONVERT_DIR: str = self.SERVER.to_convert_dir
|
||||
self.CONVERTED_DIR: str = self.SERVER.converted_dir
|
||||
self.METADATA_DIR: str = self.SERVER.metadata_dir
|
||||
|
||||
super().__init__(
|
||||
*args,
|
||||
@ -78,16 +81,20 @@ class HTTPHandler(SimpleHTTPRequestHandler):
|
||||
return True
|
||||
|
||||
def do_GET(self):
|
||||
self.path = unquote(self.path)
|
||||
self.query = parse_qs(urlparse(self.path).query)
|
||||
parsed = urlparse(unquote(self.path))
|
||||
self.path = parsed.path
|
||||
self.query = parse_qs(parsed.query)
|
||||
|
||||
if self.path.startswith("/api/"):
|
||||
self.handle_api_get(self.path.removeprefix("/api/").removesuffix("/"))
|
||||
return
|
||||
super().do_GET()
|
||||
|
||||
def do_POST(self):
|
||||
self.path = unquote(self.path)
|
||||
self.query = parse_qs(urlparse(self.path).query)
|
||||
parsed = urlparse(unquote(self.path))
|
||||
self.path = parsed.path
|
||||
self.query = parse_qs(parsed.query)
|
||||
|
||||
if self.path.startswith("/api/"):
|
||||
self.handle_api_post(self.path.removeprefix("/api/").removesuffix("/"))
|
||||
return
|
||||
@ -95,9 +102,14 @@ class HTTPHandler(SimpleHTTPRequestHandler):
|
||||
|
||||
def handle_api_get(self, path: str):
|
||||
self.log_message(f"API request at {path}")
|
||||
if path == "files":
|
||||
files: list[str] = self.get_files_meta()
|
||||
if path == "files/to_convert":
|
||||
files: list[str] = self.get_to_convert_files_meta(self.query.get("f", [""])[0])
|
||||
self.send_json(files)
|
||||
|
||||
elif path == "files/metadata":
|
||||
files: list[str] = self.get_metadata_files_meta()
|
||||
self.send_json(files)
|
||||
|
||||
elif path.startswith("file"):
|
||||
filename: str = path.split("/", 1)[1]
|
||||
data = self.read_file(filename)
|
||||
@ -126,49 +138,78 @@ class HTTPHandler(SimpleHTTPRequestHandler):
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps(data).encode("utf-8"))
|
||||
|
||||
def get_files(self):
|
||||
return os.listdir(self.DATA_DIR)
|
||||
def get_to_convert_files(self, base_path: str):
|
||||
root_path: str = os.path.abspath(self.TO_CONVERT_DIR)
|
||||
full_path: str = os.path.join(root_path, base_path)
|
||||
full_path = os.path.abspath(full_path)
|
||||
common_prefix: str = os.path.commonprefix([full_path, root_path])
|
||||
|
||||
if common_prefix != root_path:
|
||||
return []
|
||||
|
||||
return os.listdir(full_path)
|
||||
|
||||
def get_metadata_files(self):
|
||||
return os.listdir(self.METADATA_DIR)
|
||||
|
||||
def read_file(self, filename: str) -> Optional[dict|list]:
|
||||
if filename not in self.get_files():
|
||||
if filename not in self.get_metadata_files():
|
||||
return None
|
||||
with open(os.path.join(self.DATA_DIR, filename), "r") as f:
|
||||
with open(os.path.join(self.METADATA_DIR, filename), "r") as f:
|
||||
data = json.load(f)
|
||||
return data
|
||||
|
||||
def write_file(self, filename: str, data: dict|list) -> bool:
|
||||
if filename not in self.get_files():
|
||||
if filename not in self.get_metadata_files():
|
||||
self.send_error(HTTPStatus.NOT_FOUND)
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(os.path.join(self.DATA_DIR, filename), "w", encoding="utf-8") as f:
|
||||
with open(os.path.join(self.METADATA_DIR, filename), "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
except:
|
||||
self.send_error(HTTPStatus.INTERNAL_SERVER_ERROR)
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_files_meta(self):
|
||||
files: list[str] = self.get_files()
|
||||
def get_to_convert_files_meta(self, base_path: str):
|
||||
files: list[str] = self.get_to_convert_files(base_path)
|
||||
files = [os.path.join(self.TO_CONVERT_DIR, base_path, f) for f in files]
|
||||
files_meta: list[dict] = []
|
||||
|
||||
deleted = set(self.CACHE.keys()) - set(files)
|
||||
for filename in deleted:
|
||||
del self.CACHE[deleted]
|
||||
deleted = set(self.TO_CONVERT_CACHE.keys()) - set(files)
|
||||
for path in deleted:
|
||||
del self.TO_CONVERT_CACHE[path]
|
||||
|
||||
for filename in files:
|
||||
path: str = os.path.join(self.DATA_DIR, filename)
|
||||
for path in files:
|
||||
last_modified: float = os.path.getmtime(path)
|
||||
if filename not in self.CACHE or self.CACHE[filename]["ts"] < last_modified:
|
||||
self.update_file_meta(filename)
|
||||
if path not in self.TO_CONVERT_CACHE or self.TO_CONVERT_CACHE[path]["ts"] < last_modified:
|
||||
self.update_to_convert_file_meta(path)
|
||||
|
||||
files_meta.append(self.CACHE[filename])
|
||||
files_meta.append(self.TO_CONVERT_CACHE[path])
|
||||
|
||||
return files_meta
|
||||
|
||||
def update_file_meta(self, filename: str):
|
||||
path: str = os.path.join(self.DATA_DIR, filename)
|
||||
def get_metadata_files_meta(self):
|
||||
files: list[str] = self.get_metadata_files()
|
||||
files_meta: list[dict] = []
|
||||
|
||||
deleted = set(self.METADATA_CACHE.keys()) - set(files)
|
||||
for filename in deleted:
|
||||
del self.METADATA_CACHE[filename]
|
||||
|
||||
for filename in files:
|
||||
path: str = os.path.join(self.METADATA_DIR, filename)
|
||||
last_modified: float = os.path.getmtime(path)
|
||||
if filename not in self.METADATA_CACHE or self.METADATA_CACHE[filename]["ts"] < last_modified:
|
||||
self.update_metadata_file_meta(filename)
|
||||
|
||||
files_meta.append(self.METADATA_CACHE[filename])
|
||||
|
||||
return files_meta
|
||||
|
||||
def update_metadata_file_meta(self, filename: str):
|
||||
path: str = os.path.join(self.METADATA_DIR, filename)
|
||||
|
||||
meta = {
|
||||
"filename": filename,
|
||||
@ -185,7 +226,25 @@ class HTTPHandler(SimpleHTTPRequestHandler):
|
||||
else:
|
||||
meta["title"] = data["title"]
|
||||
|
||||
self.CACHE[filename] = meta
|
||||
self.METADATA_CACHE[filename] = meta
|
||||
|
||||
def update_to_convert_file_meta(self, path: str):
|
||||
filename: str = os.path.basename(path)
|
||||
|
||||
is_dir: bool = os.path.isdir(path)
|
||||
meta = {
|
||||
"path": os.path.relpath(path, self.TO_CONVERT_DIR),
|
||||
"filename": filename,
|
||||
"ts": os.path.getmtime(path),
|
||||
"size": os.path.getsize(path),
|
||||
"type": "folder" if is_dir else "media"
|
||||
}
|
||||
if is_dir:
|
||||
meta["elements"] = len(os.listdir(path))
|
||||
if not meta["path"].endswith("/"):
|
||||
meta["path"] += "/"
|
||||
|
||||
self.TO_CONVERT_CACHE[path] = meta
|
||||
|
||||
|
||||
class MeliesServer(FileSystemEventHandler):
|
||||
|
Loading…
x
Reference in New Issue
Block a user