Compare commits

...

10 commits

Author SHA1 Message Date
296cf5550a Add .mka to audio types. 2023-07-04 11:37:50 -07:00
14b2849b39 Binary diffs. 2023-07-04 11:37:38 -07:00
160ec20fab Update opendir_image.js. 2022-12-02 17:37:09 -08:00
0a136250d9 Fix use of mebibyte. 2022-12-02 17:36:50 -08:00
eb4c48cbca
Fix whitespace. 2022-08-03 18:38:30 -07:00
d040c65af6
Add --no-directories option. 2022-05-07 10:33:35 -07:00
48552690dc
Raise the default ratelimits. 2022-05-07 10:33:22 -07:00
e92a2d9b57
Update copyright year. 2022-04-05 11:52:34 -07:00
d80abcc945
Remove flask template folder.
It's so far behind.
2022-03-14 21:02:18 -07:00
31de5d0dfa
Use new betterhelp. 2022-02-12 19:51:36 -08:00
35 changed files with 144 additions and 590 deletions

View file

@ -41,7 +41,7 @@ def decrypt_file(aes, input_handle, output_handle):
last_byte = chunk[-1]
while chunk and chunk[-1] == last_byte:
chunk = chunk[:-1]
if bytes_read % bytestring.MIBIBYTE == 0:
if bytes_read % bytestring.MEBIBYTE == 0:
print(bytestring.bytestring(bytes_read))
output_handle.write(chunk)
@ -59,7 +59,7 @@ def encrypt_file(aes, input_handle, output_handle):
chunk += pad_byte * (BLOCK_SIZE - len(chunk))
done = True
bytes_read += len(chunk)
if bytes_read % bytestring.MIBIBYTE == 0:
if bytes_read % bytestring.MEBIBYTE == 0:
print(bytestring.bytestring(bytes_read))
chunk = aes.encrypt(chunk)
output_handle.write(chunk)

View file

@ -9,7 +9,7 @@ var image_height = 200;
var video_height = 300;
var audio_width = 1000;
var IMAGE_TYPES = ["\\.jpg", "\\.jpeg", "\\.jpg", "\\.bmp", "\\.tiff", "\\.tif", "\\.bmp", "\\.gif", "\\.png", "reddituploads\.com", "\\.webp", "drscdn\\.500px\\.org\\/photo"].join("|");
var AUDIO_TYPES = ["\\.aac", "\\.flac", "\\.mp3", "\\.m4a", "\\.ogg", "\\.opus", "\\.wav"].join("|");
var AUDIO_TYPES = ["\\.aac", "\\.flac", "\\.mp3", "\\.m4a", "\\.mka", "\\.ogg", "\\.opus", "\\.wav"].join("|");
var VIDEO_TYPES = ["\\.mp4", "\\.m4v", "\\.mkv", "\\.webm", "\\.ogv"].join("|");
IMAGE_TYPES = new RegExp(IMAGE_TYPES, "i");
AUDIO_TYPES = new RegExp(AUDIO_TYPES, "i");
@ -18,6 +18,8 @@ VIDEO_TYPES = new RegExp(VIDEO_TYPES, "i");
var has_started = false;
var CSS = `
* { box-sizing: inherit; }
html { box-sizing: border-box; }
body { background-color: #fff; }
audio, video { display: block; }
audio { width: ${audio_width}px; max-width: 100% }
@ -30,7 +32,7 @@ a { color: #000 !important; }
.delete_button { color: #d00; font-family: Arial; font-size: 11px; left: 0; position: absolute; top: 0; width: 25px; }
.ingest { position:absolute; right: 5px; top: 5px; height: 100%; width: 30% }
.ingestbox { position:relative; height: 75%; width:100%; box-sizing: border-box; }
.urldumpbox { overflow-y: scroll; height: 300px; width: 90% }
.urldumpbox { overflow-y: scroll; height: 300px; width: 100% }
.load_button { position: absolute; top: 10%; width: 100%; height: 80%; word-wrap: break-word; }
.odi_anchor { display: block; }
.odi_image_div, .odi_media_div { display: inline-block; margin: 5px; float: left; position: relative; background-color: #aaa; }
@ -281,6 +283,7 @@ function create_odi_div(url)
button.onclick = function()
{
delete_odi_div(this);
dump_urls();
};
div.appendChild(button);
return div;
@ -332,7 +335,7 @@ function create_workspace()
var heightfilter = create_command_box_button("heightfilter", "min height", filter_height);
var widthfilter = create_command_box_button("widthfilter", "min width", filter_width);
var sorter = create_command_button("sort size", sort_size);
var dumper = create_command_button("dump urls", dump_urls);
/*var dumper = create_command_button("dump urls", dump_urls);*/
var ingest_box = document.createElement("textarea");
var ingest_button = create_command_button("ingest", ingest);
var start_button = create_command_button("load all", function(){start();});
@ -362,7 +365,7 @@ function create_workspace()
control_panel.appendChild(heightfilter);
control_panel.appendChild(widthfilter);
control_panel.appendChild(sorter);
control_panel.appendChild(dumper);
/*control_panel.appendChild(dumper);*/
control_panel.appendChild(ingest_div);
control_panel.appendChild(start_button);
document.body.appendChild(workspace);
@ -431,6 +434,7 @@ function filter_dimension(dimension, minimum)
continue;
}
}
dump_urls();
}
function filter_height(minimum)
@ -462,6 +466,32 @@ function filter_re(pattern, do_delete)
delete_odi_div(div);
}
}
dump_urls();
}
const REJECT_PATTERNS = [
"fuskator.com/images/",
"fuskator.com/small/",
"thumbs.redditmedia",
"redditstatic.com/mailgray.png",
"redditstatic.com/start_chat.png",
"preview.redd.it/award_images",
"redditstatic.com/gold/awards",
"pixel.reddit",
"/thumb/",
"/loaders/",
"memegen",
];
function match_any_reject(url)
{
for (const pattern of REJECT_PATTERNS)
{
if (url.match(pattern))
{
return true;
}
}
}
function get_all_urls()
@ -479,27 +509,11 @@ function get_all_urls()
if (seen_urls.has(url))
{continue;}
console.log(url);
if (url.indexOf("thumbs.redditmedia") != -1)
{console.log("Rejecting reddit thumb"); continue;}
if (url.indexOf("redditstatic.com/mailgray.png") != -1)
{console.log("Rejecting reddit icons"); continue;}
if (url.indexOf("redditstatic.com/start_chat.png") != -1)
{console.log("Rejecting reddit icons"); continue;}
if (url.indexOf("preview.redd.it/award_images") != -1)
{console.log("Rejecting reddit awards"); continue;}
if (url.indexOf("redditstatic.com/gold/awards") != -1)
{console.log("Rejecting reddit awards"); continue;}
if (url.indexOf("pixel.reddit") != -1 || url.indexOf("reddit.com/static/pixel") != -1)
{console.log("Rejecting reddit pixel"); continue}
if (url.indexOf("/thumb/") != -1)
{console.log("Rejecting /thumb/"); continue;}
if (url.indexOf("/loaders/") != -1)
{console.log("Rejecting loader"); continue;}
if (url.indexOf("memegen") != -1)
{console.log("Rejecting retardation"); continue;}
if (url.indexOf("4cdn") != -1 && url.indexOf("s.jpg") != -1)
{console.log("Rejecting 4chan thumb"); continue;}
if (match_any_reject(url))
{
console.log("Rejecting.");
continue;
}
sub_urls = normalize_url(url);
if (sub_urls == null)
{continue;}
@ -688,15 +702,19 @@ function lazy_load_one(element, comeback)
width = this.naturalWidth;
height = this.naturalHeight;
if (width == 161 && height == 81)
{delete_odi_div(this);}
{
delete_odi_div(this);
}
this.arealabel.innerHTML = width + " x " + height;
this.odi_div.style.minWidth = "0px";
if (comeback){lazy_load_all()};
dump_urls();
};
image.onerror = function()
{
delete_odi_div(this);
if (comeback){lazy_load_all()};
dump_urls();
};
/*console.log("Lazy loading " + element.lazy_src)*/
image.src = image.lazy_src;
@ -869,6 +887,7 @@ function main()
var divs = create_odi_divs(all_urls);
create_workspace();
fill_workspace(divs);
dump_urls();
}
main();

View file

@ -1,6 +1,6 @@
BSD 3-Clause License
Copyright (c) 2021, Ethan Dalool aka voussoir
Copyright (c) 2022, Ethan Dalool aka voussoir
All rights reserved.
Redistribution and use in source and binary forms, with or without

View file

@ -10,7 +10,7 @@ from voussoirkit import bytestring
from voussoirkit import downloady
from voussoirkit import pipeable
DEFAULT_PIECE_SIZE = bytestring.MIBIBYTE
DEFAULT_PIECE_SIZE = bytestring.MEBIBYTE
DEFAULT_THREAD_COUNT = 10
def init(url, localname=None, piece_size=DEFAULT_PIECE_SIZE):

View file

@ -18,7 +18,7 @@ from voussoirkit import passwordy
from voussoirkit import pathclass
from voussoirkit import ratelimiter
CHUNK_SIZE = bytestring.MIBIBYTE
CHUNK_SIZE = bytestring.MEBIBYTE
OPENDIR_TEMPLATE = '''
<html>
@ -203,9 +203,14 @@ class RequestHandler(http.server.BaseHTTPRequestHandler):
response = read_filebytes(path, range_min=range_min, range_max=range_max)
elif path.is_dir:
headers['Content-type'] = 'text/html'
response = generate_opendir(path, enable_zip=self.server.enable_zip)
response = response.encode('utf-8')
if self.server.enable_directory_listings:
headers['Content-type'] = 'text/html'
response = generate_opendir(path, enable_zip=self.server.enable_zip)
response = response.encode('utf-8')
else:
status_code = 404
self.send_error(status_code)
response = bytes()
elif self.path.endswith('.zip') and self.server.enable_zip:
path = url_to_path(self.path.rsplit('.zip', 1)[0])
@ -303,12 +308,14 @@ class SimpleServer:
password,
authorize_by_ip,
enable_zip,
enable_directory_listings,
overall_ratelimit,
individual_ratelimit,
):
self.port = port
self.password = password
self.authorize_by_ip = authorize_by_ip
self.enable_directory_listings = enable_directory_listings
self.enable_zip = enable_zip
self.overall_ratelimit = ratelimiter.Ratelimiter(overall_ratelimit)
self.individual_ratelimit = individual_ratelimit
@ -483,62 +490,97 @@ def zip_directory(path):
# COMMAND LINE #####################################################################################
DOCSTRING = '''
simpleserver
============
Run a simple file server from your computer.
> simpleserver port <flags>
port:
Port number, an integer.
flags:
--password X:
A password string. The user will be prompted to enter it before proceeding
to any URL. A token is stored in a cookie unless authorize_by_ip is used.
--authorize_by_ip:
After the user enters the password, their entire IP becomes authorized for
all future requests. This reduces security, because a single IP can be home
to many different people, but increases convenience, because the user can
use download managers / scripts where adding auth is not convenient.
--enable_zip:
Add a 'zip' link to every directory and allow the user to download the
entire directory as a zip file.
--overall_ratelimit X:
An integer number of bytes, the maximum bytes/sec of the server overall.
--individual_ratelimit X:
An integer number of bytes, the maximum bytes/sec for any single request.
'''
def simpleserver_argparse(args):
server = SimpleServer(
port=args.port,
password=args.password,
authorize_by_ip=args.authorize_by_ip,
enable_zip=args.enable_zip,
enable_directory_listings=args.enable_directory_listings,
overall_ratelimit=args.overall_ratelimit,
individual_ratelimit=args.individual_ratelimit,
)
server.start()
def main(argv):
parser = argparse.ArgumentParser(description=DOCSTRING)
parser.add_argument('port', nargs='?', type=int, default=40000)
parser.add_argument('--password', dest='password', default=None)
parser.add_argument('--authorize_by_ip', '--authorize-by-ip', dest='authorize_by_ip', action='store_true')
parser.add_argument('--enable_zip', '--enable-zip', dest='enable_zip', action='store_true')
parser.add_argument('--overall_ratelimit', '--overall-ratelimit', type=bytestring.parsebytes, default=20*bytestring.MIBIBYTE)
parser.add_argument('--individual_ratelimit', '--individual-ratelimit', type=bytestring.parsebytes, default=10*bytestring.MIBIBYTE)
parser = argparse.ArgumentParser(
description=
'''
Run a simple http file server from your computer.
''',
)
parser.examples = [
'4242 --password letmeinplease --authorize-by-ip --enable-zip',
'--individual-ratelimit 2m --overall-ratelimit 10m',
]
parser.add_argument(
'port',
nargs='?',
type=int,
default=40000,
)
parser.add_argument(
'--password',
dest='password',
type=str,
default=None,
help='''
A password string. The user will be prompted to enter it before proceeding
to any URL. A token is stored in a cookie unless authorize_by_ip is used.
''',
)
parser.add_argument(
'--authorize_by_ip',
'--authorize-by-ip',
action='store_true',
help='''
After the user enters the password, their entire IP becomes authorized for
all future requests. This reduces security, because a single IP can be home
to many different people, but increases convenience, because the user can
use download managers / scripts where adding auth is not convenient.
''',
)
parser.add_argument(
'--enable_zip',
'--enable-zip',
action='store_true',
help='''
Add a 'zip' link to every directory and allow the user to download the
entire directory as a zip file.
''',
)
parser.add_argument(
'--overall_ratelimit',
'--overall-ratelimit',
type=bytestring.parsebytes,
default=200*bytestring.MEBIBYTE,
help='''
The maximum bytes/sec of the server overall.
''',
)
parser.add_argument(
'--no_directories',
'--no-directories',
dest='enable_directory_listings',
action='store_false',
default=True,
help='''
Disable the generation of directory listing pages. They will return 404
instead. The visitor must have the exact link to a file.
''',
)
parser.add_argument(
'--individual_ratelimit',
'--individual-ratelimit',
type=bytestring.parsebytes,
default=100*bytestring.MEBIBYTE,
help='''
The maximum bytes/sec for any single request.
''',
)
parser.set_defaults(func=simpleserver_argparse)
return betterhelp.single_main(argv, parser, DOCSTRING)
return betterhelp.go(parser, argv)
if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:]))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 132 B

View file

@ -1,51 +0,0 @@
import flask
from flask import request
import functools
import time
import uuid
import warnings
def _generate_session_token():
token = str(uuid.uuid4())
#print('MAKE SESSION', token)
return token
def give_session_token(function):
@functools.wraps(function)
def wrapped(*args, **kwargs):
# Inject new token so the function doesn't know the difference
token = request.cookies.get('flasksite_session', None)
if not token:
token = _generate_session_token()
request.cookies = dict(request.cookies)
request.cookies['flasksite_session'] = token
ret = function(*args, **kwargs)
# Send the token back to the client
if not isinstance(ret, flask.Response):
ret = flask.Response(ret)
ret.set_cookie('flasksite_session', value=token, max_age=60)
return ret
return wrapped
def not_implemented(function):
'''
Decorator to remember what needs doing.
'''
warnings.warn('%s is not implemented' % function.__name__)
return function
def time_me(function):
'''
After the function is run, print the elapsed time.
'''
@functools.wraps(function)
def timed_function(*args, **kwargs):
start = time.time()
result = function(*args, **kwargs)
end = time.time()
print('%s: %0.8f' % (function.__name__, end-start))
return result
return timed_function

View file

@ -1,125 +0,0 @@
import flask
from flask import request
import json
import mimetypes
import os
import decorators
site = flask.Flask(__name__)
site.config.update(
SEND_FILE_MAX_AGE_DEFAULT=180,
TEMPLATES_AUTO_RELOAD=True,
)
site.jinja_env.add_extension('jinja2.ext.do')
site.debug = True
####################################################################################################
####################################################################################################
####################################################################################################
####################################################################################################
def make_json_response(j, *args, **kwargs):
dumped = json.dumps(j)
response = flask.Response(dumped, *args, **kwargs)
response.headers['Content-Type'] = 'application/json;charset=utf-8'
return response
def send_file(filepath):
'''
Range-enabled file sending.
'''
try:
file_size = os.path.getsize(filepath)
except FileNotFoundError:
flask.abort(404)
outgoing_headers = {}
mimetype = mimetypes.guess_type(filepath)[0]
if mimetype is not None:
if 'text/' in mimetype:
mimetype += '; charset=utf-8'
outgoing_headers['Content-Type'] = mimetype
if 'range' in request.headers:
desired_range = request.headers['range'].lower()
desired_range = desired_range.split('bytes=')[-1]
int_helper = lambda x: int(x) if x.isdigit() else None
if '-' in desired_range:
(desired_min, desired_max) = desired_range.split('-')
range_min = int_helper(desired_min)
range_max = int_helper(desired_max)
else:
range_min = int_helper(desired_range)
if range_min is None:
range_min = 0
if range_max is None:
range_max = file_size
# because ranges are 0-indexed
range_max = min(range_max, file_size - 1)
range_min = max(range_min, 0)
range_header = 'bytes {min}-{max}/{outof}'.format(
min=range_min,
max=range_max,
outof=file_size,
)
outgoing_headers['Content-Range'] = range_header
status = 206
else:
range_max = file_size - 1
range_min = 0
status = 200
outgoing_headers['Accept-Ranges'] = 'bytes'
outgoing_headers['Content-Length'] = (range_max - range_min) + 1
if request.method == 'HEAD':
outgoing_data = bytes()
else:
outgoing_data = helpers.read_filebytes(filepath, range_min=range_min, range_max=range_max)
response = flask.Response(
outgoing_data,
status=status,
headers=outgoing_headers,
)
return response
####################################################################################################
####################################################################################################
####################################################################################################
####################################################################################################
@site.route('/')
@decorators.give_session_token
def root():
return flask.render_template('root.html')
@site.route('/favicon.ico')
@site.route('/favicon.png')
def favicon():
filename = os.path.join('static', 'favicon.png')
return flask.send_file(filename)
@site.route('/static/<filename>')
def get_static(filename):
filename = filename.replace('\\', os.sep)
filename = filename.replace('/', os.sep)
filename = os.path.join('static', filename)
return flask.send_file(filename)
@site.route("/float/<float:value>")
def float_type(value):
print(value + 1)
return "correct"
if __name__ == '__main__':
#site.run(threaded=True)
pass

View file

@ -1,29 +0,0 @@
import gevent.monkey
gevent.monkey.patch_all()
import flasksite
import gevent.pywsgi
import gevent.wsgi
import sys
if len(sys.argv) == 2:
port = int(sys.argv[1])
else:
port = 5000
if port == 443:
http = gevent.pywsgi.WSGIServer(
listener=('0.0.0.0', port),
application=flasksite.site,
keyfile='https\\flasksite.key',
certfile='https\\flasksite.crt',
)
else:
http = gevent.pywsgi.WSGIServer(
listener=('0.0.0.0', port),
application=flasksite.site,
)
print('Starting server on port %d' % port)
http.serve_forever()

View file

@ -1,135 +0,0 @@
import math
FILE_READ_CHUNK = 512 * 1024
def chunk_sequence(sequence, chunk_length, allow_incomplete=True):
'''
Given a sequence, divide it into sequences of length `chunk_length`.
allow_incomplete:
If True, allow the final chunk to be shorter if the
given sequence is not an exact multiple of `chunk_length`.
If False, the incomplete chunk will be discarded.
'''
(complete, leftover) = divmod(len(sequence), chunk_length)
if not allow_incomplete:
leftover = 0
chunk_count = complete + min(leftover, 1)
chunks = []
for x in range(chunk_count):
left = chunk_length * x
right = left + chunk_length
chunks.append(sequence[left:right])
return chunks
def comma_split(s):
'''
Split the string apart by commas, discarding all extra whitespace and
blank phrases.
'''
if s is None:
return s
s = s.replace(' ', ',')
s = [x.strip() for x in s.split(',')]
s = [x for x in s if x]
return s
def edit_params(original, modifications):
'''
Given a dictionary representing URL parameters,
apply the modifications and return a URL parameter string.
{'a':1, 'b':2}, {'b':3} => ?a=1&b=3
'''
new_params = original.copy()
new_params.update(modifications)
if not new_params:
return ''
new_params = ['%s=%s' % (k, v) for (k, v) in new_params.items() if v]
new_params = '&'.join(new_params)
if new_params:
new_params = '?' + new_params
return new_params
def fit_into_bounds(image_width, image_height, frame_width, frame_height):
'''
Given the w+h of the image and the w+h of the frame,
return new w+h that fits the image into the frame
while maintaining the aspect ratio.
'''
ratio = min(frame_width/image_width, frame_height/image_height)
new_width = int(image_width * ratio)
new_height = int(image_height * ratio)
return (new_width, new_height)
def hms_to_seconds(hms):
'''
Convert hh:mm:ss string to an integer seconds.
'''
hms = hms.split(':')
seconds = 0
if len(hms) == 3:
seconds += int(hms[0])*3600
hms.pop(0)
if len(hms) == 2:
seconds += int(hms[0])*60
hms.pop(0)
if len(hms) == 1:
seconds += int(hms[0])
return seconds
def is_xor(*args):
'''
Return True if and only if one arg is truthy.
'''
return [bool(a) for a in args].count(True) == 1
def read_filebytes(filepath, range_min, range_max):
'''
Yield chunks of bytes from the file between the endpoints.
'''
range_span = range_max - range_min
#print('read span', range_min, range_max, range_span)
f = open(filepath, 'rb')
f.seek(range_min)
sent_amount = 0
with f:
while sent_amount < range_span:
#print(sent_amount)
chunk = f.read(FILE_READ_CHUNK)
if len(chunk) == 0:
break
yield chunk
sent_amount += len(chunk)
def seconds_to_hms(seconds):
'''
Convert integer number of seconds to an hh:mm:ss string.
Only the necessary fields are used.
'''
seconds = math.ceil(seconds)
(minutes, seconds) = divmod(seconds, 60)
(hours, minutes) = divmod(minutes, 60)
parts = []
if hours: parts.append(hours)
if hours or minutes: parts.append(minutes)
parts.append(seconds)
hms = ':'.join('%02d' % part for part in parts)
return hms
def truthystring(s):
if isinstance(s, (bool, int)) or s is None:
return s
s = s.lower()
if s in {'1', 'true', 't', 'yes', 'y', 'on'}:
return True
if s in {'null', 'none'}:
return None
return False

View file

@ -1,2 +0,0 @@
flask
gevent

View file

@ -1,32 +0,0 @@
body
{
display: flex;
flex-direction: column;
background-color:#fff;
margin: 8px;
}
#header
{
display: flex;
flex-direction: row;
justify-content: center;
align-content: center;
margin-bottom: 4px;
}
.header_element
{
display: flex;
justify-content: center;
flex: 1;
background-color: rgba(0, 0, 0, 0.1);
}
.header_element:hover
{
background-color: #f00;
}
#content_body
{
flex: 0 0 auto;
display: flex;
flex-direction: row;
}

View file

@ -1,78 +0,0 @@
function post_example(key, value)
{
var url = "/postexample";
data = new FormData();
data.append(key, value);
return post(url, data, callback);
}
function post(url, data, callback)
{
var request = new XMLHttpRequest();
request.answer = null;
request.onreadystatechange = function()
{
if (request.readyState == 4)
{
var text = request.responseText;
if (callback != null)
{
console.log(text);
callback(JSON.parse(text));
}
}
};
var asynchronous = true;
request.open("POST", url, asynchronous);
request.send(data);
}
function bind_box_to_button(box, button)
{
box.onkeydown=function()
{
if (event.keyCode == 13)
{
button.click();
}
};
}
function entry_with_history_hook(box, button)
{
//console.log(event.keyCode);
if (box.entry_history === undefined)
{box.entry_history = [];}
if (box.entry_history_pos === undefined)
{box.entry_history_pos = -1;}
if (event.keyCode == 13)
{
/* Enter */
box.entry_history.push(box.value);
button.click();
box.value = "";
}
else if (event.keyCode == 38)
{
/* Up arrow */
if (box.entry_history.length == 0)
{return}
if (box.entry_history_pos == -1)
{
box.entry_history_pos = box.entry_history.length - 1;
}
else if (box.entry_history_pos > 0)
{
box.entry_history_pos -= 1;
}
box.value = box.entry_history[box.entry_history_pos];
}
else if (event.keyCode == 27)
{
box.value = "";
}
else
{
box.entry_history_pos = -1;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 B

View file

@ -1,5 +0,0 @@
{% macro make_header() %}
<div id="header">
<a class="header_element" href="/">Home</a>
</div>
{% endmacro %}

View file

@ -1,29 +0,0 @@
<!DOCTYPE html5>
<html>
<head>
{% import "header.html" as header %}
<title>Flasksite</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="/static/common.css">
<style>
body, a
{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
</style>
</head>
<body>
{{header.make_header()}}
<p>Welcome to my flask site</p>
</body>
<script type="text/javascript">
</script>
</html>

View file

@ -1,23 +0,0 @@
<!DOCTYPE html5>
<html>
<head>
{% import "header.html" as header %}
<title>Flasksite</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="/static/common.css">
<style>
</style>
</head>
<body>
<div id="content_body">
<p>test</p>
</div>
</body>
<script type="text/javascript">
</script>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 B

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 B

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 KiB

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

View file

@ -298,10 +298,12 @@ def handle_vidme(url, customname=None):
pagedata = pagedata[0]
pagedata = pagedata.split('content="')[1].split('"')[0]
pagedata = pagedata.replace('&amp;', '&')
headers = {'Referer': 'https://vid.me/',
'Range':'bytes=0-',
'Host':'d1wst0behutosd.cloudfront.net',
'Cache-Control':'max-age=0'}
headers = {
'Referer': 'https://vid.me/',
'Range':'bytes=0-',
'Host':'d1wst0behutosd.cloudfront.net',
'Cache-Control':'max-age=0'
}
return download_file(pagedata, customname, headers=headers)
@ -387,7 +389,7 @@ HANDLERS = {
'youtube.com': handle_youtube,
'youtu.be': handle_youtube,
'twitter.com': handle_twitter
}
}
def handle_master(url, customname=None):
print('Handling %s' % url)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 B

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 B

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 B

After

Width:  |  Height:  |  Size: 128 B