Commit 2c6cf866 authored by Michael Murtaugh's avatar Michael Murtaugh

changes to cgi

parent 58b09fa5
......@@ -12,6 +12,9 @@ from directory import *
from aa_password import PASSWORD
from login import authenticate
#DEBUG
# print ("Content-type: text/html; charset=utf8")
# print ()
def get_current_url (environ):
request_scheme = environ.get("REQUEST_SCHEME")
......@@ -94,6 +97,59 @@ elif f == "annotate":
# print ("Not authorized")
# cgi.print_environ()
messages = []
if method=="POST":
description = fs.getvalue("description")
#if os.path.isfile(fullpath):
# HANDLE UPLOADS
# FORMATS
files = fs["file"]
if not isinstance(files, list):
files = [files]
files = [x for x in files if x.filename]
if files:
# these get added as "formats" to the file
formatpath = file_formats_path(fullpath, ensureCreate=True)
# print ("files {0}".format(len(files)))
results = []
for f in files:
savepath = os.path.join(formatpath, f.filename)
count = upload(f, savepath)
results.append((f.filename, os.path.getsize(savepath)))
messages.append("Uploaded {0}".format(savepath))
messages.append("Uploaded {0} files".format(len(files)))
# UPLOADS (directory)
files = fs["upload"]
if not isinstance(files, list):
files = [files]
files = [x for x in files if x.filename]
if os.path.isdir(fullpath):
if files:
results = []
for f in files:
savepath = os.path.join(fullpath, f.filename)
count = upload(f, savepath)
results.append((f.filename, os.path.getsize(savepath)))
messages.append("Uploaded {0}".format(savepath))
messages.append("Uploaded {0} files".format(len(files)))
# HANDLE DELETIONS (selectedformats)
sff = fs.getvalue("selectedformatsfn")
if sff == "delete":
for sf_url in fs.getlist("selectedformats"):
sf_fullpath = os.path.join(docroot, urlunquote(sf_url).strip("/"))
if os.path.exists(sf_fullpath):
os.remove(sf_fullpath)
messages.append("deleted {0}".format(sf_url))
else:
messages.append("file not found {0}".format(sf_url))
# DEBUG
# print ("Content-type: text/html; charset=utf8")
# print ()
if os.path.isfile(fullpath):
parentpath = os.path.split(fullpath)[0]
user_data_path = index_json_path(parentpath)
......@@ -107,41 +163,12 @@ elif f == "annotate":
item = user_data
fs_data = listdir(fullpath, normalized_url)
fs_item = fs_data
# nb: user_data remains unchanged (what gets (potentially) stored below)
merge_data(fs_data, user_data)
if method=="POST":
print ("Content-type: text/html; charset=utf8")
print ()
# check for uploaded files
files = fs["file"]
if not isinstance(files, list):
files = [files]
files = [x for x in files if x.filename]
description = fs.getvalue("description")
messages = []
if os.path.isfile(fullpath):
# FILE
if files:
# these get added as "formats" to the file
formatpath = file_formats_path(fullpath, ensureCreate=True)
# print ("files {0}".format(len(files)))
results = []
for f in files:
savepath = os.path.join(formatpath, f.filename)
count = upload(f, savepath)
results.append((f.filename, os.path.getsize(savepath)))
messages.append("Uploaded {0} files".format(len(files)))
else:
# Upload files to directory
if files:
results = []
for f in files:
savepath = os.path.join(fullpath, f.filename)
count = upload(f, savepath)
results.append((f.filename, os.path.getsize(savepath)))
messages.append("Uploaded {0} files".format(len(files)))
# print ("Content-type: text/html; charset=utf8")
# print ()
# APPLY DATA AND SAVE BACK IF CHANGED
changed = False
if description and (item.get("description") != description):
......@@ -156,14 +183,17 @@ elif f == "annotate":
json.dump(user_data, f)
messages.append("Updated item")
print ("<div>post processed</div>")
for msg in messages:
print ("""<div class="message">{0}</div>""".format(msg))
if messages:
messages = "\n".join(["""<div class="message">{0}</div>""".format(msg) for msg in messages])
messages = """<div id="messages">{0}</div>""".format(messages)
else:
print ("Content-type: text/html; charset=utf8")
print ()
send_form(item, fs_item)
messages = ""
print ("Content-type: text/html; charset=utf8")
print ()
# nb fs_item represents the merged version
send_form(item, fs_item, messages)
# APPLY NEW DATA TO ITEM
# save the json
# evt. RECEIVE FILES FOR ITEM
......
import os, json
from urllib.parse import quote as urlquote, unquote as urlunquote
from urllib.parse import quote as urlquote, unquote as urlunquote, urlencode
# https://github.com/achillean/shodan-python/issues/39
def humanize_bytes(bytes, precision=1):
"""Return a humanized string representation of a number of bytes.
>>> humanize_bytes(1)
'1 byte'
>>> humanize_bytes(1024)
'1.0 kB'
>>> humanize_bytes(1024*123)
'123.0 kB'
>>> humanize_bytes(1024*12342)
'12.1 MB'
>>> humanize_bytes(1024*12342,2)
'12.05 MB'
>>> humanize_bytes(1024*1234,2)
'1.21 MB'
>>> humanize_bytes(1024*1234*1111,2)
'1.31 GB'
>>> humanize_bytes(1024*1234*1111,1)
'1.3 GB'
"""
if bytes == 1:
return '1 byte'
if bytes < 1024:
return '%.*f %s' % (precision, bytes, "bytes")
suffixes = ['KB', 'MB', 'GB', 'TB', 'PB']
multiple = 1024.0 #.0 force float on python 2
for suffix in suffixes:
bytes /= multiple
if bytes < multiple:
return '%.*f %s' % (precision, bytes, suffix)
def index_json_path (path):
return os.path.join(path, ".aa", "index.json")
......@@ -15,8 +47,11 @@ def load_index_json (path):
return {}
def file_formats_path (path, ensureCreate=False):
parent, filename = os.path.split(path)
ret = os.path.join(parent, ".aa", filename)
if os.path.isfile(path):
parent, filename = os.path.split(path)
ret = os.path.join(parent, ".aa", filename)
else:
ret = os.path.join(path, ".aa", ".files")
if ensureCreate:
try:
os.makedirs(ret)
......@@ -62,10 +97,15 @@ def item_datum (fullpath, normalized_url, filename, is_format=False):
item = {}
fp = os.path.join(fullpath, filename)
if os.path.isdir(fp):
item['url'] = normalized_url + filename + "/"
item['url'] = normalized_url + urlquote(filename) + "/"
item['type'] = "folder"
item['info'] = "/cgi-bin/directory.cgi?f=json&u="+item['url']
item['edit'] = "/cgi-bin/directory.cgi?f=annotate&u="+item['url']
item['info'] = "/cgi-bin/directory.cgi?"+urlencode({'f': 'json', 'u': item['url']})
item['edit'] = "/cgi-bin/directory.cgi?"+urlencode({'f': 'annotate', 'u': item['url']})
if not is_format:
formats = calc_formats_json(fp, item['url']+".files")
if formats:
item['formats'] = formats
# attempt to read data from inner user json file
ddata = load_index_json(fp)
if ddata:
......@@ -73,7 +113,8 @@ def item_datum (fullpath, normalized_url, filename, is_format=False):
if key != "children":
item[key] = ddata[key]
else:
item['url' ] = normalized_url + filename
item['url' ] = normalized_url + urlquote(filename)
item['edit'] = "/cgi-bin/directory.cgi?"+urlencode({'f': 'annotate', 'u': item['url']})
itype = guess_item_type(item['url'])
if itype:
item['type'] = itype
......@@ -89,6 +130,9 @@ def listdir (fullpath, normalized_url):
ret = {}
ret['url'] = normalized_url
ret['type'] = "folder"
formats = calc_formats_json(fullpath, normalized_url+".files")
if formats:
ret['formats'] = formats
items = os.listdir(fullpath)
items.sort()
items = [x for x in items if not x.startswith(".")]
......@@ -118,35 +162,70 @@ def merge_data (filesystem, user):
merge(achild, c)
def send_form(item, fs_item):
def send_form(item, fs_item, messages):
# # add this data to the appropriate index.json ... creating if necessary
# name = os.path.splitext(filename)[0].replace("_", " ")
# title = fs.getvalue("title", "")
upload = ""
if fs_item.get("type") == "folder":
label = "Upload files"
else:
label = "Add versions"
upload = """<div class="upload"><label for="upload">Add files to this folder:</label><input id="upload" type="file" name="upload" multiple="multiple"></div>"""
# label = "Upload files"
label = "Add"
edit_url = fs_item.get("url")
edit_filename = os.path.split(edit_url.rstrip("/"))[1].replace("_"," ")
description = item.get("description", "")
files = "None<br>"
if 'formats' in fs_item:
files += """<table id="fileformat">\n"""
formats = fs_item['formats']
formats.sort(key=lambda x: x['size'])
for f in formats:
filename = os.path.split(f['url'])[1]
files += """<tr class="file">
<td><input type="checkbox" name="selectedformats" value="{0[url]}"></a></td>
<td><a href="{0[url]}">{1}</a></td>
<td>{3}</td>
<td value="{0[size]}">{2}</td>
</tr>\n""".format(f, filename, humanize_bytes(f['size']), f.get("type", "&mdash;"))
files += """</table>"""
print ("""<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>edit description</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {{
margin: 1em;
}}
input {{
margin: 0;
}}
</style>
<link rel="stylesheet" type="text/css" href="/lib/directory/directory.css">
</head>
<body>
<body class="edit">
<form method="post" action="" enctype="multipart/form-data">
<textarea name="description" style="width: 95%; height: 4em;" placeholder="Description" autofocus>{0}</textarea>
<div><input type="submit" value="save" /> <label for="file">{1}:</label> <input id="file" type="file" name="file" multiple="multiple"></div>
<div id="form" class="grouper">
<div id="description" class="group">
<div class="title">{0[filename]}</div>
<textarea name="description" style="width: 320px; height: 4em;" placeholder="Description" autofocus>{0[description]}</textarea>
<div><input type="submit" value="save" /> </div>
{0[messages]}
</div>
<div class="filelisting group">
{0[upload]}
<div class="files">
<b>Versions &amp; thumbnails:</b><br>
{0[files]}
<select name="selectedformatsfn">
<option></option>
<option value="delete">delete selected</option>
</select>
<label for="file">Add:</label> <input id="file" type="file" name="file" multiple="multiple">
</div>
</div>
</div>
</form>
</body>
</html>
""".format(description, label))
""".format({
'description': description,
'files': files,
'filename': edit_filename,
'messages': messages,
'upload': upload}))
......@@ -7,8 +7,8 @@ dist/feedplayer.js: src/feedplayer.js src/*.js
upload: dist/directory.js dist/directory.css
# SERVER
# cp dist/directory.js dist/directory.css ~/mnt/1/public_html/lib/directory/
# cp cgi-bin/*.cgi ~/mnt/1/cgi-bin
cp dist/directory.js dist/directory.css ~/mnt/1/public_html/lib/directory/
cp cgi-bin/*.cgi cgi-bin/*.py ~/mnt/1/cgi-bin
# LOCAL
cp dist/directory.js dist/directory.css /var/www/html/lib/directory/
cp cgi-bin/*.cgi cgi-bin/*.py /usr/lib/cgi-bin
......@@ -22,10 +22,10 @@ function controller () {
playbutton = contents.append("button")
.text("play")
.on("click", d => {
if (audio.paused) {
audio.play();
if (current_media.paused) {
current_media.play();
} else {
audio.pause();
current_media.pause();
}
}),
timeslider = contents.append("input")
......@@ -35,8 +35,8 @@ function controller () {
.attr("step", 1)
.on("mousedown", x=> {
dragging = true;
if (!audio.paused) {
audio.pause();
if (!current_media.paused) {
current_media.pause();
playAfterDrag = true;
} else {
playAfterDrag = false;
......@@ -46,11 +46,11 @@ function controller () {
if (dragging) {
// console.log("stop dragging");
dragging = false;
if (audio.duration) {
var curtime = (timeslider.value / 10000) * audio.duration;
audio.currentTime = curtime;
if (current_media.duration) {
var curtime = (timeslider.node().value / 10000) * current_media.duration;
current_media.currentTime = curtime;
if (playAfterDrag) {
audio.play();
current_media.play();
}
}
}
......@@ -60,17 +60,31 @@ function controller () {
edit_button = edit_buttons.append("button")
.text("edit")
.on("click", d=> {
if (current_url) {
if (edit_iframe === undefined) { init_edit_iframe() };
console.log("edit_iframe", edit_iframe);
edit_iframe.attr("src", "/cgi-bin/description.cgi?path="+current_url);
edit_iframe_div.classed("open", true);
var open = edit_iframe_div.classed("open"),
newstate = !open;
if (newstate) {
edit_url(current_url ? current_url : window.location.pathname);
} else {
edit_iframe_div.classed("open", false);
}
}),
audio,
video,
current_media,
image,
dragging = false,
playAfterDrag = false,
current_url;
current_url,
editing_url;
function edit_url (use_url) {
if (use_url && editing_url != use_url) {
if (edit_iframe === undefined) { init_edit_iframe() };
edit_iframe.attr("src", "/cgi-bin/directory.cgi?f=annotate&u="+encodeURIComponent(use_url));
editing_url = use_url;
}
edit_iframe_div.classed("open", true);
}
function init_edit_iframe () {
edit_iframe = edit_iframe_div
......@@ -103,6 +117,35 @@ function controller () {
})
}
function init_video () {
video = document.createElement("video");
video.setAttribute("controls", "controls");
video.setAttribute("autoplay", "autoplay");
contents.node().appendChild(video);
video.addEventListener("play", x => {
playbutton.text("pause");
});
video.addEventListener("pause", x => {
playbutton.text("play");
});
video.addEventListener("timeupdate", x => {
// console.log("timeupdate", video.duration);
if (dragging) { return; }
if (video.duration) {
var curval = (video.currentTime / video.duration) * 10000;
timeslider.node().value = curval;
}
});
video.addEventListener("ended", x=> {
console.log("video.ended");
var next = d3.select("tr.file.playing + tr.file");
console.log("next");
if (next) {
play.call(next.node(), next.datum());
}
})
}
// login.addEventListener("click", x=> {
// loadscript.loadscript("/cgi-bin/aa/droptoupload.cgi?javascript").then(x=> {
// console.log("loaded droptoupload");
......@@ -111,22 +154,41 @@ function controller () {
// console.log("controller", div);
function play (d) {
current_url = window.location.pathname + d.url;
current_url = d.url;
if (edit_iframe_div.classed("open")) {
edit_url(current_url);
}
console.log("play", this, current_url, d);
d3.selectAll(".playing").classed("playing", false);
d3.select(this).classed("playing", true);
if (d.type == "audio") {
play_audio.call(this, d);
} else if (d.type == "video") {
play_video.call(this, d);
}
}
ret.play = play;
function play_audio (d) {
if (audio === undefined) { init_audio(); }
current_media = audio;
if (video && !video.paused) { video.pause() }
// hide the video?
audio.src = d.url;
audio.play();
}
ret.play_audio = play_audio;
function play_video (d) {
console.log("play_video", d);
if (video === undefined) { init_video(); }
current_media = video;
if (audio && !audio.paused) { audio.pause() }
video.src = d.url;
video.play();
}
ret.play_video = play_video;
return ret;
}
module.exports = controller;
......@@ -9,6 +9,12 @@ require("./trim.js");
var controller;
function normalize_url (url) {
// aaaaaaaaaaaaaargh, python capitalizes % encoded values
// apache directory listing seem to be lower cased
// RegEx to the rescue
return url.replace(new RegExp("%[a-f0-9][a-f0-9]", "i"), x => x.toUpperCase());
}
/* Binds initial data items to each tr in the table with url == a[href] */
function init_table_data() {
......@@ -16,12 +22,12 @@ function init_table_data() {
tr = tr.filter( (d, i) => (i>=3 && i < tr.size()-1) );
tr.attr("class", "file");
var data = tr.nodes().map(x => ({
'url' : x.querySelector("td:nth-child(2) a[href]").getAttribute("href"),
'url' : normalize_url(x.querySelector("td:nth-child(2) a[href]").pathname),
'last-modified': x.querySelector("td:nth-child(3)").textContent.trim(),
'size': x.querySelector("td:nth-child(4)").textContent.trim()
}));
tr.data(data);
console.log("init_table_data", data);
//console.log("init_table_data", data);
}
function set_default_data (selection) {
......@@ -38,43 +44,39 @@ function set_default_data (selection) {
})
}
async function read_metadata(json_url) {
function get_thumb_url (d) {
if (!d.links) return;
let links = d.links;
for (let i=0, l=links.length; i<l; i++) {
let link = links[i];
if (link.rel == "thumb") {
return link.url;
}
}
function get_thumb_url (d) {
if (!d.formats) return;
let links = d.formats
.filter(x => x.type=="image")
.sort((x, y) => (x.size - y.size));
if (links.length) {
return links[0].url;
}
}
async function read_metadata(json_url) {
try {
var resp = await fetch(json_url),
data = await resp.json();
console.log("got index.json", data);
var update = tr.data(data.children, d => d.url);
console.log("update", update, update.size());
console.log("enter", update.enter().size());
update.select("img").attr("src", d=>get_thumb_url(d));
update.select("a").on("click", function(d) {
d3.event.preventDefault();
console.log("play", this);
var tr = this.parentNode.parentNode,
td = tr.querySelector("td"),
img = td.querySelector("img"),
video = document.createElement("video-js"),
source = document.createElement("source");
console.log("tr", tr);
console.log("img", img);
source.setAttribute("src", d.url);
video.setAttribute("data-setup", "{}");
video.setAttribute("controls", "controls");
video.appendChild(source);
video.setAttribute("class", "video-js");
td.replaceChild(video, img);
videojs(video);
var tr = d3.selectAll("table tbody tr.file");
var update = tr.data(data.children, d => decodeURI(d.url)),
enter = update.enter(),
exit = update.exit();
// console.log("update", update, update.size());
// should be no enter/exits...
enter.each(function (d, i) { console.log("warning: enter", i, d.url); });
exit.each(function (d, i) { console.log("warning: exit", i, d.url); });
// Integrate data
update.each(function (d, i) {
if (d.description) {
d3.select(this).select("td:nth-child(5)").html(d.description);
}
});
update.filter(get_thumb_url).select("img").attr("src", d=>get_thumb_url(d)).classed("thumbnail", true);
return data;
} catch (error) {
console.log("error", error);
......@@ -94,38 +96,31 @@ function activate_table () {
async function init () {
var aa_auth = cookie.get("aa_auth");
console.log("aa_auth", aa_auth, aa_auth=="true");
// console.log("aa_auth", aa_auth, aa_auth=="true");
controller = make_controller();
// linkplayer({ selection: "a" });
// Initialize data, prepare for join
init_table_data();
set_default_data();
var files = d3.selectAll("table tbody tr.file");
var data = files.data();
console.log("enriched data", data);
// console.log("enriched data", data);
// read + join with stored metadata
data = await read_metadata(".aa/index.json");
if (!data) {
console.log("no data: TODO write init_table_data to .aa/index.json");
//data = await read_metadata(".aa/index.json");
data = await read_metadata("/cgi-bin/directory.cgi?f=json&u="+window.location.pathname);
if (get_thumb_url(data)) {
d3.select(document.body).insert("img", "table").attr("class", "thumbnail").attr("src", get_thumb_url(data));
}
var description = d3.select(document.body).insert("p", "table").attr("class", "description");
if (data.description) {
description.html(data.description);
}
activate_table();
//loadscript.loadscript("/lib/directory/video.js").then(x => {
// console.log("loaded video.js", videojs);
//});
//loadscript.loadstylesheet("/lib/directory/video-js.css", x => {
// console.log("loaded videojs.css");
//});
}
// <meta name="viewport" content="width=device-width, initial-scale=1.0">
document.addEventListener("DOMContentLoaded", init);
// https://github.com/marcj/css-element-queries
var ElementQueries = require('css-element-queries/src/ElementQueries');
//var ElementQueries = require('css-element-queries/src/ElementQueries');
// attaches to DOMLoadContent and does anything for you
ElementQueries.listen();
//ElementQueries.listen();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment