pep8'd what I could, including tabs to spaces
This commit is contained in:
parent
a45ecc76b0
commit
48bf325d77
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@ scanner/floatapp/app.cfg
|
|||||||
*.pyc
|
*.pyc
|
||||||
*.min.css
|
*.min.css
|
||||||
*.min.js
|
*.min.js
|
||||||
|
.vscode
|
@ -1,49 +1,90 @@
|
|||||||
import os.path
|
import os.path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
def message(category, text):
|
def message(category, text):
|
||||||
if message.level <= 0:
|
if message.level <= 0:
|
||||||
sep = " "
|
sep = " "
|
||||||
else:
|
else:
|
||||||
sep = "--"
|
sep = "--"
|
||||||
print "%s %s%s[%s]%s%s" % (datetime.now().isoformat(), max(0, message.level) * " |", sep, category, max(1, (14 - len(category))) * " ", text)
|
print "%s %s%s[%s]%s%s" % (
|
||||||
|
datetime.now().isoformat(),
|
||||||
|
max(0, message.level) * " |",
|
||||||
|
sep,
|
||||||
|
category,
|
||||||
|
max(1, (14 - len(category))) * " ",
|
||||||
|
text)
|
||||||
message.level = -1
|
message.level = -1
|
||||||
|
|
||||||
|
|
||||||
def next_level():
|
def next_level():
|
||||||
message.level += 1
|
message.level += 1
|
||||||
|
|
||||||
|
|
||||||
def back_level():
|
def back_level():
|
||||||
message.level -= 1
|
message.level -= 1
|
||||||
|
|
||||||
|
|
||||||
def set_cache_path_base(base):
|
def set_cache_path_base(base):
|
||||||
trim_base.base = base
|
trim_base.base = base
|
||||||
|
|
||||||
|
|
||||||
def untrim_base(path):
|
def untrim_base(path):
|
||||||
return os.path.join(trim_base.base, path)
|
return os.path.join(trim_base.base, path)
|
||||||
|
|
||||||
|
|
||||||
def trim_base_custom(path, base):
|
def trim_base_custom(path, base):
|
||||||
if path.startswith(base):
|
if path.startswith(base):
|
||||||
path = path[len(base):]
|
path = path[len(base):]
|
||||||
if path.startswith('/'):
|
if path.startswith('/'):
|
||||||
path = path[1:]
|
path = path[1:]
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def trim_base(path):
|
def trim_base(path):
|
||||||
return trim_base_custom(path, trim_base.base)
|
return trim_base_custom(path, trim_base.base)
|
||||||
|
|
||||||
|
|
||||||
def cache_base(path, filepath=False):
|
def cache_base(path, filepath=False):
|
||||||
if len(path) == 0:
|
if len(path) == 0:
|
||||||
return "root"
|
return "root"
|
||||||
elif filepath and len(path.split(os.sep)) < 2:
|
elif filepath and len(path.split(os.sep)) < 2:
|
||||||
path = "root-" + path
|
path = "root-" + path
|
||||||
path = trim_base(path).replace('/', '-').replace(' ', '_').replace('(', '').replace('&', '').replace(',', '').replace(')', '').replace('#', '').replace('[', '').replace(']', '').replace('"', '').replace("'", '').replace('_-_', '-').lower()
|
path = trim_base(path).replace(
|
||||||
while path.find("--") != -1:
|
'/', '-').replace(
|
||||||
path = path.replace("--", "-")
|
' ', '_').replace(
|
||||||
while path.find("__") != -1:
|
'(', '').replace(
|
||||||
path = path.replace("__", "_")
|
'&', '').replace(
|
||||||
return path
|
',', '').replace(
|
||||||
|
')', '').replace(
|
||||||
|
'#', '').replace(
|
||||||
|
'[', '').replace(
|
||||||
|
']', '').replace(
|
||||||
|
'"', '').replace(
|
||||||
|
"'", '').replace(
|
||||||
|
'_-_', '-').lower()
|
||||||
|
while path.find("--") != -1:
|
||||||
|
path = path.replace("--", "-")
|
||||||
|
while path.find("__") != -1:
|
||||||
|
path = path.replace("__", "_")
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
def json_cache(path):
|
def json_cache(path):
|
||||||
return cache_base(path) + ".json"
|
return cache_base(path) + ".json"
|
||||||
|
|
||||||
|
|
||||||
def image_cache(path, size, square=False):
|
def image_cache(path, size, square=False):
|
||||||
if square:
|
if square:
|
||||||
suffix = str(size) + "s"
|
suffix = str(size) + "s"
|
||||||
else:
|
else:
|
||||||
suffix = str(size)
|
suffix = str(size)
|
||||||
return cache_base(path, True) + "_" + suffix + ".jpg"
|
return cache_base(path, True) + "_" + suffix + ".jpg"
|
||||||
|
|
||||||
|
|
||||||
def video_cache(path):
|
def video_cache(path):
|
||||||
return cache_base(path, True) + ".mp4"
|
return cache_base(path, True) + ".mp4"
|
||||||
|
|
||||||
|
|
||||||
def file_mtime(path):
|
def file_mtime(path):
|
||||||
return datetime.fromtimestamp(int(os.path.getmtime(path)))
|
return datetime.fromtimestamp(int(os.path.getmtime(path)))
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -6,129 +6,143 @@ from PhotoAlbum import Photo, Album, PhotoAlbumEncoder
|
|||||||
from CachePath import *
|
from CachePath import *
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
class TreeWalker:
|
class TreeWalker:
|
||||||
def __init__(self, album_path, cache_path):
|
def __init__(self, album_path, cache_path):
|
||||||
self.album_path = os.path.abspath(album_path).decode(sys.getfilesystemencoding())
|
self.album_path = os.path.abspath(
|
||||||
self.cache_path = os.path.abspath(cache_path).decode(sys.getfilesystemencoding())
|
album_path).decode(sys.getfilesystemencoding())
|
||||||
set_cache_path_base(self.album_path)
|
self.cache_path = os.path.abspath(
|
||||||
self.all_albums = list()
|
cache_path).decode(sys.getfilesystemencoding())
|
||||||
self.all_photos = list()
|
set_cache_path_base(self.album_path)
|
||||||
self.walk(self.album_path)
|
self.all_albums = list()
|
||||||
self.big_lists()
|
self.all_photos = list()
|
||||||
self.remove_stale()
|
self.walk(self.album_path)
|
||||||
message("complete", "")
|
self.big_lists()
|
||||||
def walk(self, path):
|
self.remove_stale()
|
||||||
next_level()
|
message("complete", "")
|
||||||
if not os.access(path, os.R_OK | os.X_OK):
|
|
||||||
message("access denied", os.path.basename(path))
|
|
||||||
back_level()
|
|
||||||
return None
|
|
||||||
message("walking", os.path.basename(path))
|
|
||||||
cache = os.path.join(self.cache_path, json_cache(path))
|
|
||||||
cached = False
|
|
||||||
cached_album = None
|
|
||||||
if os.path.exists(cache):
|
|
||||||
try:
|
|
||||||
cached_album = Album.from_cache(cache)
|
|
||||||
if file_mtime(path) <= file_mtime(cache):
|
|
||||||
message("full cache", os.path.basename(path))
|
|
||||||
cached = True
|
|
||||||
album = cached_album
|
|
||||||
for photo in album.photos:
|
|
||||||
self.all_photos.append(photo)
|
|
||||||
else:
|
|
||||||
message("partial cache", os.path.basename(path))
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
message("corrupt cache", os.path.basename(path))
|
|
||||||
cached_album = None
|
|
||||||
if not cached:
|
|
||||||
album = Album(path)
|
|
||||||
for entry in os.listdir(path):
|
|
||||||
if entry[0] == '.':
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
entry = entry.decode(sys.getfilesystemencoding())
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
next_level()
|
|
||||||
message("unicode error", entry.decode(sys.getfilesystemencoding(), "replace"))
|
|
||||||
back_level()
|
|
||||||
continue
|
|
||||||
entry = os.path.join(path, entry)
|
|
||||||
if os.path.isdir(entry):
|
|
||||||
next_walked_album = self.walk(entry)
|
|
||||||
if next_walked_album is not None:
|
|
||||||
album.add_album(next_walked_album)
|
|
||||||
elif not cached and os.path.isfile(entry):
|
|
||||||
next_level()
|
|
||||||
cache_hit = False
|
|
||||||
if cached_album:
|
|
||||||
cached_photo = cached_album.photo_from_path(entry)
|
|
||||||
if cached_photo and file_mtime(entry) <= cached_photo.attributes["dateTimeFile"]:
|
|
||||||
cache_file = None
|
|
||||||
if "mediaType" in cached_photo.attributes:
|
|
||||||
if cached_photo.attributes["mediaType"] == "video":
|
|
||||||
# if video
|
|
||||||
cache_file = os.path.join(self.cache_path, video_cache(entry))
|
|
||||||
else:
|
|
||||||
# if image
|
|
||||||
cache_file = os.path.join(self.cache_path, image_cache(entry, 1024, False))
|
|
||||||
else:
|
|
||||||
# if image
|
|
||||||
cache_file = os.path.join(self.cache_path, image_cache(entry, 1024, False))
|
|
||||||
|
|
||||||
# at this point we have full path to cache image/video
|
def walk(self, path):
|
||||||
# check if it actually exists
|
next_level()
|
||||||
if os.path.exists(cache_file):
|
if not os.access(path, os.R_OK | os.X_OK):
|
||||||
message("cache hit", os.path.basename(entry))
|
message("access denied", os.path.basename(path))
|
||||||
cache_hit = True
|
back_level()
|
||||||
photo = cached_photo
|
return None
|
||||||
|
message("walking", os.path.basename(path))
|
||||||
|
cache = os.path.join(self.cache_path, json_cache(path))
|
||||||
|
cached = False
|
||||||
|
cached_album = None
|
||||||
|
if os.path.exists(cache):
|
||||||
|
try:
|
||||||
|
cached_album = Album.from_cache(cache)
|
||||||
|
if file_mtime(path) <= file_mtime(cache):
|
||||||
|
message("full cache", os.path.basename(path))
|
||||||
|
cached = True
|
||||||
|
album = cached_album
|
||||||
|
for photo in album.photos:
|
||||||
|
self.all_photos.append(photo)
|
||||||
|
else:
|
||||||
|
message("partial cache", os.path.basename(path))
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
message("corrupt cache", os.path.basename(path))
|
||||||
|
cached_album = None
|
||||||
|
if not cached:
|
||||||
|
album = Album(path)
|
||||||
|
for entry in os.listdir(path):
|
||||||
|
if entry[0] == '.':
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
entry = entry.decode(sys.getfilesystemencoding())
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
next_level()
|
||||||
|
message("unicode error", entry.decode(
|
||||||
|
sys.getfilesystemencoding(), "replace"))
|
||||||
|
back_level()
|
||||||
|
continue
|
||||||
|
entry = os.path.join(path, entry)
|
||||||
|
if os.path.isdir(entry):
|
||||||
|
next_walked_album = self.walk(entry)
|
||||||
|
if next_walked_album is not None:
|
||||||
|
album.add_album(next_walked_album)
|
||||||
|
elif not cached and os.path.isfile(entry):
|
||||||
|
next_level()
|
||||||
|
cache_hit = False
|
||||||
|
if cached_album:
|
||||||
|
cached_photo = cached_album.photo_from_path(entry)
|
||||||
|
if (cached_photo and file_mtime(
|
||||||
|
entry) <= cached_photo.attributes["dateTimeFile"]):
|
||||||
|
cache_file = None
|
||||||
|
if "mediaType" in cached_photo.attributes:
|
||||||
|
if cached_photo.attributes["mediaType"] == "video":
|
||||||
|
# if video
|
||||||
|
cache_file = os.path.join(
|
||||||
|
self.cache_path, video_cache(entry))
|
||||||
|
else:
|
||||||
|
# if image
|
||||||
|
cache_file = os.path.join(
|
||||||
|
self.cache_path,
|
||||||
|
image_cache(entry, 1024, False))
|
||||||
|
else:
|
||||||
|
# if image
|
||||||
|
cache_file = os.path.join(
|
||||||
|
self.cache_path,
|
||||||
|
image_cache(entry, 1024, False))
|
||||||
|
|
||||||
if not cache_hit:
|
# at this point we have full path to cache image/video
|
||||||
message("metainfo", os.path.basename(entry))
|
# check if it actually exists
|
||||||
photo = Photo(entry, self.cache_path)
|
if os.path.exists(cache_file):
|
||||||
if photo.is_valid:
|
message("cache hit", os.path.basename(entry))
|
||||||
self.all_photos.append(photo)
|
cache_hit = True
|
||||||
album.add_photo(photo)
|
photo = cached_photo
|
||||||
else:
|
|
||||||
message("unreadable", os.path.basename(entry))
|
if not cache_hit:
|
||||||
back_level()
|
message("metainfo", os.path.basename(entry))
|
||||||
if not album.empty:
|
photo = Photo(entry, self.cache_path)
|
||||||
message("caching", os.path.basename(path))
|
if photo.is_valid:
|
||||||
album.cache(self.cache_path)
|
self.all_photos.append(photo)
|
||||||
self.all_albums.append(album)
|
album.add_photo(photo)
|
||||||
else:
|
else:
|
||||||
message("empty", os.path.basename(path))
|
message("unreadable", os.path.basename(entry))
|
||||||
back_level()
|
back_level()
|
||||||
return album
|
if not album.empty:
|
||||||
def big_lists(self):
|
message("caching", os.path.basename(path))
|
||||||
photo_list = []
|
album.cache(self.cache_path)
|
||||||
self.all_photos.sort()
|
self.all_albums.append(album)
|
||||||
for photo in self.all_photos:
|
else:
|
||||||
photo_list.append(photo.path)
|
message("empty", os.path.basename(path))
|
||||||
message("caching", "all photos path list")
|
back_level()
|
||||||
fp = open(os.path.join(self.cache_path, "all_photos.json"), 'w')
|
return album
|
||||||
json.dump(photo_list, fp, cls=PhotoAlbumEncoder)
|
|
||||||
fp.close()
|
def big_lists(self):
|
||||||
def remove_stale(self):
|
photo_list = []
|
||||||
message("cleanup", "building stale list")
|
self.all_photos.sort()
|
||||||
all_cache_entries = { "all_photos.json": True, "latest_photos.json": True }
|
for photo in self.all_photos:
|
||||||
for album in self.all_albums:
|
photo_list.append(photo.path)
|
||||||
all_cache_entries[album.cache_path] = True
|
message("caching", "all photos path list")
|
||||||
for photo in self.all_photos:
|
fp = open(os.path.join(self.cache_path, "all_photos.json"), 'w')
|
||||||
for entry in photo.image_caches:
|
json.dump(photo_list, fp, cls=PhotoAlbumEncoder)
|
||||||
all_cache_entries[entry] = True
|
fp.close()
|
||||||
message("cleanup", "searching for stale cache entries")
|
|
||||||
for cache in os.listdir(self.cache_path):
|
def remove_stale(self):
|
||||||
try:
|
message("cleanup", "building stale list")
|
||||||
cache = cache.decode(sys.getfilesystemencoding())
|
all_cache_entries = {"all_photos.json": True,
|
||||||
except KeyboardInterrupt:
|
"latest_photos.json": True}
|
||||||
raise
|
for album in self.all_albums:
|
||||||
except:
|
all_cache_entries[album.cache_path] = True
|
||||||
pass
|
for photo in self.all_photos:
|
||||||
if cache not in all_cache_entries:
|
for entry in photo.image_caches:
|
||||||
message("cleanup", os.path.basename(cache))
|
all_cache_entries[entry] = True
|
||||||
os.unlink(os.path.join(self.cache_path, cache))
|
message("cleanup", "searching for stale cache entries")
|
||||||
|
for cache in os.listdir(self.cache_path):
|
||||||
|
try:
|
||||||
|
cache = cache.decode(sys.getfilesystemencoding())
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
if cache not in all_cache_entries:
|
||||||
|
message("cleanup", os.path.basename(cache))
|
||||||
|
os.unlink(os.path.join(self.cache_path, cache))
|
||||||
|
@ -2,46 +2,49 @@ from CachePath import message
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
class VideoToolWrapper(object):
|
|
||||||
def call(self, *args):
|
|
||||||
path = args[-1]
|
|
||||||
for tool in self.wrappers:
|
|
||||||
try:
|
|
||||||
if self.check_output:
|
|
||||||
p = subprocess.check_output((tool,) + args)
|
|
||||||
else:
|
|
||||||
p = subprocess.call((tool,) + args)
|
|
||||||
if p > 0:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return "SUCCESS"
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
if self.cleanup:
|
|
||||||
self.remove(path)
|
|
||||||
raise
|
|
||||||
except OSError:
|
|
||||||
continue
|
|
||||||
except:
|
|
||||||
if self.cleanup:
|
|
||||||
self.remove(path)
|
|
||||||
continue
|
|
||||||
return p
|
|
||||||
return False
|
|
||||||
|
|
||||||
def remove(self, path):
|
class VideoToolWrapper(object):
|
||||||
try:
|
def call(self, *args):
|
||||||
os.unlink(path)
|
path = args[-1]
|
||||||
except:
|
for tool in self.wrappers:
|
||||||
pass
|
try:
|
||||||
|
if self.check_output:
|
||||||
|
p = subprocess.check_output((tool,) + args)
|
||||||
|
else:
|
||||||
|
p = subprocess.call((tool,) + args)
|
||||||
|
if p > 0:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return "SUCCESS"
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
if self.cleanup:
|
||||||
|
self.remove(path)
|
||||||
|
raise
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
except:
|
||||||
|
if self.cleanup:
|
||||||
|
self.remove(path)
|
||||||
|
continue
|
||||||
|
return p
|
||||||
|
return False
|
||||||
|
|
||||||
|
def remove(self, path):
|
||||||
|
try:
|
||||||
|
os.unlink(path)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class VideoTranscodeWrapper(VideoToolWrapper):
|
class VideoTranscodeWrapper(VideoToolWrapper):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.wrappers = ['avconv', 'ffmpeg']
|
self.wrappers = ['avconv', 'ffmpeg']
|
||||||
self.check_output = False
|
self.check_output = False
|
||||||
self.cleanup = True
|
self.cleanup = True
|
||||||
|
|
||||||
|
|
||||||
class VideoProbeWrapper(VideoToolWrapper):
|
class VideoProbeWrapper(VideoToolWrapper):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.wrappers = ['avprobe', 'ffprobe']
|
self.wrappers = ['avprobe', 'ffprobe']
|
||||||
self.check_output = True
|
self.check_output = True
|
||||||
self.cleanup = False
|
self.cleanup = False
|
||||||
|
@ -3,7 +3,9 @@ from flask_login import LoginManager
|
|||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config.from_pyfile(os.path.join(os.path.dirname(os.path.abspath(__file__)), "app.cfg"))
|
app.config.from_pyfile(
|
||||||
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), "app.cfg"))
|
||||||
|
|
||||||
login_manager = LoginManager()
|
login_manager = LoginManager()
|
||||||
import login
|
import login
|
||||||
login_manager.setup_app(app)
|
login_manager.setup_app(app)
|
||||||
|
@ -1,118 +1,160 @@
|
|||||||
from floatapp import app
|
|
||||||
from floatapp.login import admin_required, login_required, is_authenticated, query_is_photo_user, query_is_admin_user, photo_user, admin_user
|
|
||||||
from floatapp.jsonp import jsonp
|
|
||||||
from process import send_process
|
|
||||||
from TreeWalker import TreeWalker
|
|
||||||
from flask import Response, abort, json, request, jsonify
|
|
||||||
from flask_login import login_user, current_user
|
|
||||||
from random import shuffle
|
|
||||||
import os
|
import os
|
||||||
from mimetypes import guess_type
|
from mimetypes import guess_type
|
||||||
|
from random import shuffle
|
||||||
|
|
||||||
|
from flask import Response, abort, json, jsonify, request
|
||||||
|
from flask_login import current_user, login_user
|
||||||
|
|
||||||
|
from floatapp import app
|
||||||
|
from floatapp.jsonp import jsonp
|
||||||
|
from floatapp.login import (admin_required, admin_user, is_authenticated,
|
||||||
|
login_required, photo_user, query_is_admin_user,
|
||||||
|
query_is_photo_user)
|
||||||
|
from process import send_process
|
||||||
|
from TreeWalker import TreeWalker
|
||||||
|
|
||||||
cwd = os.path.dirname(os.path.abspath(__file__))
|
cwd = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/scan")
|
@app.route("/scan")
|
||||||
@admin_required
|
@admin_required
|
||||||
def scan_photos():
|
def scan_photos():
|
||||||
global cwd
|
global cwd
|
||||||
response = send_process([ "stdbuf", "-oL", os.path.abspath(os.path.join(cwd, "../main.py")),
|
response = send_process([
|
||||||
os.path.abspath(app.config["ALBUM_PATH"]), os.path.abspath(app.config["CACHE_PATH"]) ],
|
"stdbuf",
|
||||||
os.path.join(cwd, "scanner.pid"))
|
"-oL",
|
||||||
response.headers.add("X-Accel-Buffering", "no")
|
os.path.abspath(os.path.join(cwd, "../venv/bin/python")),
|
||||||
response.cache_control.no_cache = True
|
os.path.abspath(os.path.join(cwd, "../main.py")),
|
||||||
return response
|
os.path.abspath(app.config["ALBUM_PATH"]),
|
||||||
|
os.path.abspath(app.config["CACHE_PATH"])
|
||||||
|
],
|
||||||
|
os.path.join(cwd, "scanner.pid"))
|
||||||
|
response.headers.add("X-Accel-Buffering", "no")
|
||||||
|
response.cache_control.no_cache = True
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route("/auth")
|
@app.route("/auth")
|
||||||
def login():
|
def login():
|
||||||
success = False
|
success = False
|
||||||
if current_user.is_authenticated():
|
if current_user.is_authenticated:
|
||||||
success = True
|
success = True
|
||||||
elif query_is_photo_user(request.form) or query_is_photo_user(request.args):
|
elif (query_is_photo_user(request.form) or
|
||||||
success = login_user(photo_user, remember=True)
|
query_is_photo_user(request.args)):
|
||||||
elif query_is_admin_user(request.form) or query_is_admin_user(request.args):
|
success = login_user(photo_user, remember=True)
|
||||||
success = login_user(admin_user, remember=True)
|
elif (query_is_admin_user(request.form) or
|
||||||
if not success:
|
query_is_admin_user(request.args)):
|
||||||
abort(403)
|
success = login_user(admin_user, remember=True)
|
||||||
return ""
|
if not success:
|
||||||
|
abort(403)
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def cache_base(path):
|
def cache_base(path):
|
||||||
path = path.replace('/', '-').replace(' ', '_').replace('(', '').replace('&', '').replace(',', '').replace(')', '').replace('#', '').replace('[', '').replace(']', '').replace('"', '').replace("'", '').replace('_-_', '-').lower()
|
path = path.replace(
|
||||||
while path.find("--") != -1:
|
'/', '-').replace(
|
||||||
path = path.replace("--", "-")
|
' ', '_').replace(
|
||||||
while path.find("__") != -1:
|
'(', '').replace(
|
||||||
path = path.replace("__", "_")
|
'&', '').replace(
|
||||||
if len(path) == 0:
|
',', '').replace(
|
||||||
path = "root"
|
')', '').replace(
|
||||||
return path
|
'#', '').replace(
|
||||||
|
'[', '').replace(
|
||||||
|
']', '').replace(
|
||||||
|
'"', '').replace(
|
||||||
|
"'", '').replace(
|
||||||
|
'_-_', '-').lower()
|
||||||
|
|
||||||
|
while path.find("--") != -1:
|
||||||
|
path = path.replace("--", "-")
|
||||||
|
|
||||||
|
while path.find("__") != -1:
|
||||||
|
path = path.replace("__", "_")
|
||||||
|
|
||||||
|
if len(path) == 0:
|
||||||
|
path = "root"
|
||||||
|
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
auth_list = []
|
||||||
|
|
||||||
|
|
||||||
auth_list = [ ]
|
|
||||||
def read_auth_list():
|
def read_auth_list():
|
||||||
global auth_list, cwd
|
global auth_list, cwd
|
||||||
f = open(os.path.join(cwd, "auth.txt"), "r")
|
f = open(os.path.join(cwd, "auth.txt"), "r")
|
||||||
paths = [ ]
|
paths = []
|
||||||
for path in f:
|
for path in f:
|
||||||
path = path.strip()
|
path = path.strip()
|
||||||
paths.append(path)
|
paths.append(path)
|
||||||
paths.append(cache_base(path))
|
paths.append(cache_base(path))
|
||||||
f.close()
|
f.close()
|
||||||
auth_list = paths
|
auth_list = paths
|
||||||
|
|
||||||
|
|
||||||
# TODO: Make this run via inotify
|
# TODO: Make this run via inotify
|
||||||
read_auth_list()
|
read_auth_list()
|
||||||
|
|
||||||
|
|
||||||
def check_permissions(path):
|
def check_permissions(path):
|
||||||
if not is_authenticated():
|
if not is_authenticated():
|
||||||
for auth_path in auth_list:
|
for auth_path in auth_list:
|
||||||
if path.startswith(auth_path):
|
if path.startswith(auth_path):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/albums/<path:path>")
|
@app.route("/albums/<path:path>")
|
||||||
def albums(path):
|
def albums(path):
|
||||||
check_permissions(path)
|
check_permissions(path)
|
||||||
return accel_redirect(app.config["ALBUM_ACCEL"], app.config["ALBUM_PATH"], path)
|
return accel_redirect(
|
||||||
|
app.config["ALBUM_ACCEL"], app.config["ALBUM_PATH"], path)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/cache/<path:path>")
|
@app.route("/cache/<path:path>")
|
||||||
def cache(path):
|
def cache(path):
|
||||||
check_permissions(path)
|
check_permissions(path)
|
||||||
return accel_redirect(app.config["CACHE_ACCEL"], app.config["CACHE_PATH"], path)
|
return accel_redirect(
|
||||||
|
app.config["CACHE_ACCEL"], app.config["CACHE_PATH"], path)
|
||||||
|
|
||||||
|
|
||||||
def accel_redirect(internal, real, relative_name):
|
def accel_redirect(internal, real, relative_name):
|
||||||
real_path = os.path.join(real, relative_name)
|
real_path = os.path.join(real, relative_name)
|
||||||
internal_path = os.path.join(internal, relative_name)
|
internal_path = os.path.join(internal, relative_name)
|
||||||
if not os.path.isfile(real_path):
|
if not os.path.isfile(real_path):
|
||||||
abort(404)
|
abort(404)
|
||||||
mimetype = None
|
mimetype = None
|
||||||
types = guess_type(real_path)
|
types = guess_type(real_path)
|
||||||
if len(types) != 0:
|
if len(types) != 0:
|
||||||
mimetype = types[0]
|
mimetype = types[0]
|
||||||
response = Response(mimetype=mimetype)
|
response = Response(mimetype=mimetype)
|
||||||
response.headers.add("X-Accel-Redirect", internal_path)
|
response.headers.add("X-Accel-Redirect", internal_path)
|
||||||
response.cache_control.public = True
|
response.cache_control.public = True
|
||||||
if mimetype == "application/json":
|
if mimetype == "application/json":
|
||||||
response.cache_control.max_age = 3600
|
response.cache_control.max_age = 3600
|
||||||
else:
|
else:
|
||||||
response.cache_control.max_age = 29030400
|
response.cache_control.max_age = 29030400
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route("/photos")
|
@app.route("/photos")
|
||||||
@jsonp
|
@jsonp
|
||||||
def photos():
|
def photos():
|
||||||
f = open(os.path.join(app.config["CACHE_PATH"], "all_photos.json"), "r")
|
f = open(os.path.join(app.config["CACHE_PATH"], "all_photos.json"), "r")
|
||||||
photos = json.load(f)
|
photos = json.load(f)
|
||||||
f.close()
|
f.close()
|
||||||
if not is_authenticated():
|
if not is_authenticated():
|
||||||
def allowed(photo):
|
def allowed(photo):
|
||||||
for auth_path in auth_list:
|
for auth_path in auth_list:
|
||||||
if photo.startswith(auth_path):
|
if photo.startswith(auth_path):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
photos = [photo for photo in photos if allowed(photo)]
|
photos = [photo for photo in photos if allowed(photo)]
|
||||||
count = int(request.args.get("count", len(photos)))
|
count = int(request.args.get("count", len(photos)))
|
||||||
random = request.args.get("random") == "true"
|
random = request.args.get("random") == "true"
|
||||||
if random:
|
if random:
|
||||||
shuffle(photos)
|
shuffle(photos)
|
||||||
else:
|
else:
|
||||||
photos.reverse()
|
photos.reverse()
|
||||||
response = jsonify(photos=photos[0:count])
|
response = jsonify(photos=photos[0:count])
|
||||||
response.cache_control.no_cache = True
|
response.cache_control.no_cache = True
|
||||||
return response
|
return response
|
||||||
|
@ -5,14 +5,16 @@ import re
|
|||||||
|
|
||||||
jsonp_validator = re.compile("^[a-zA-Z0-9_\-.]{1,128}$")
|
jsonp_validator = re.compile("^[a-zA-Z0-9_\-.]{1,128}$")
|
||||||
|
|
||||||
|
|
||||||
def jsonp(f):
|
def jsonp(f):
|
||||||
"""Wraps JSONified output for JSONP"""
|
"""Wraps JSONified output for JSONP"""
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
callback = request.args.get('callback', False)
|
callback = request.args.get('callback', False)
|
||||||
if callback and jsonp_validator.match(callback):
|
if callback and jsonp_validator.match(callback):
|
||||||
content = str(callback) + '(' + str(f(*args,**kwargs).data) + ')'
|
content = str(callback) + '(' + str(f(*args, **kwargs).data) + ')'
|
||||||
return current_app.response_class(content, mimetype='application/javascript')
|
return current_app.response_class(
|
||||||
else:
|
content, mimetype='application/javascript')
|
||||||
return f(*args, **kwargs)
|
else:
|
||||||
return decorated_function
|
return f(*args, **kwargs)
|
||||||
|
return decorated_function
|
||||||
|
@ -3,51 +3,66 @@ from flask import request, abort
|
|||||||
from flask_login import current_user, UserMixin
|
from flask_login import current_user, UserMixin
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
|
|
||||||
class User(UserMixin):
|
class User(UserMixin):
|
||||||
def __init__(self, id, admin=False):
|
def __init__(self, id, admin=False):
|
||||||
self.admin = admin
|
self.admin = admin
|
||||||
self.id = id
|
self.id = id
|
||||||
|
|
||||||
photo_user = User("user")
|
photo_user = User("user")
|
||||||
admin_user = User("admin", True)
|
admin_user = User("admin", True)
|
||||||
|
|
||||||
|
|
||||||
@login_manager.user_loader
|
@login_manager.user_loader
|
||||||
def load_user(id):
|
def load_user(id):
|
||||||
if id == "user":
|
if id == "user":
|
||||||
return photo_user
|
return photo_user
|
||||||
elif id == "admin":
|
elif id == "admin":
|
||||||
return admin_user
|
return admin_user
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@login_manager.unauthorized_handler
|
@login_manager.unauthorized_handler
|
||||||
def unauthorized():
|
def unauthorized():
|
||||||
return abort(403)
|
return abort(403)
|
||||||
|
|
||||||
|
|
||||||
def login_required(fn):
|
def login_required(fn):
|
||||||
@wraps(fn)
|
@wraps(fn)
|
||||||
def decorated_view(*args, **kwargs):
|
def decorated_view(*args, **kwargs):
|
||||||
if query_is_admin_user(request.args) or query_is_photo_user(request.args) or current_user.is_authenticated():
|
if (query_is_admin_user(request.args) or
|
||||||
return fn(*args, **kwargs)
|
query_is_photo_user(request.args) or
|
||||||
return app.login_manager.unauthorized()
|
current_user.is_authenticated):
|
||||||
return decorated_view
|
return fn(*args, **kwargs)
|
||||||
|
return app.login_manager.unauthorized()
|
||||||
|
return decorated_view
|
||||||
|
|
||||||
|
|
||||||
def admin_required(fn):
|
def admin_required(fn):
|
||||||
@wraps(fn)
|
@wraps(fn)
|
||||||
def decorated_view(*args, **kwargs):
|
def decorated_view(*args, **kwargs):
|
||||||
if query_is_admin_user(request.args) or (current_user.is_authenticated() and current_user.admin):
|
if (query_is_admin_user(request.args) or
|
||||||
return fn(*args, **kwargs)
|
(current_user.is_authenticated and current_user.admin)):
|
||||||
return app.login_manager.unauthorized()
|
return fn(*args, **kwargs)
|
||||||
return decorated_view
|
return app.login_manager.unauthorized()
|
||||||
|
return decorated_view
|
||||||
|
|
||||||
|
|
||||||
def query_is_photo_user(query):
|
def query_is_photo_user(query):
|
||||||
username = query.get("username", None)
|
username = query.get("username", None)
|
||||||
password = query.get("password", None)
|
password = query.get("password", None)
|
||||||
return username == app.config["PHOTO_USERNAME"] and password == app.config["PHOTO_PASSWORD"]
|
return username == (app.config["PHOTO_USERNAME"] and
|
||||||
|
password == app.config["PHOTO_PASSWORD"])
|
||||||
|
|
||||||
|
|
||||||
def query_is_admin_user(query):
|
def query_is_admin_user(query):
|
||||||
username = query.get("username", None)
|
username = query.get("username", None)
|
||||||
password = query.get("password", None)
|
password = query.get("password", None)
|
||||||
return username == app.config["ADMIN_USERNAME"] and password == app.config["ADMIN_PASSWORD"]
|
return username == (app.config["ADMIN_USERNAME"] and
|
||||||
|
password == app.config["ADMIN_PASSWORD"])
|
||||||
|
|
||||||
|
|
||||||
def is_authenticated():
|
def is_authenticated():
|
||||||
return query_is_admin_user(request.args) or query_is_photo_user(request.args) or current_user.is_authenticated()
|
return (query_is_admin_user(request.args) or
|
||||||
|
query_is_photo_user(request.args) or
|
||||||
|
current_user.is_authenticated)
|
||||||
|
@ -3,50 +3,61 @@ import subprocess
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
class ProcessWrapper(object):
|
class ProcessWrapper(object):
|
||||||
def __init__(self, process, done):
|
def __init__(self, process, done):
|
||||||
self.process = process
|
self.process = process
|
||||||
self.done = done
|
self.done = done
|
||||||
def close(self):
|
|
||||||
self.done()
|
def close(self):
|
||||||
if self.process.returncode is not None:
|
self.done()
|
||||||
return
|
if self.process.returncode is not None:
|
||||||
self.process.stdout.close()
|
return
|
||||||
self.process.terminate()
|
self.process.stdout.close()
|
||||||
self.process.wait()
|
self.process.terminate()
|
||||||
def __iter__(self):
|
self.process.wait()
|
||||||
return self
|
|
||||||
def __del__(self):
|
def __iter__(self):
|
||||||
self.close()
|
return self
|
||||||
def next(self):
|
|
||||||
try:
|
def __del__(self):
|
||||||
data = self.process.stdout.readline()
|
self.close()
|
||||||
except:
|
|
||||||
self.close()
|
def next(self):
|
||||||
raise StopIteration()
|
try:
|
||||||
if data:
|
data = self.process.stdout.readline()
|
||||||
return data
|
except:
|
||||||
self.close()
|
self.close()
|
||||||
raise StopIteration()
|
raise StopIteration()
|
||||||
|
if data:
|
||||||
|
return data
|
||||||
|
self.close()
|
||||||
|
raise StopIteration()
|
||||||
|
|
||||||
|
|
||||||
def send_process(args, pid_file):
|
def send_process(args, pid_file):
|
||||||
def setup_proc():
|
def setup_proc():
|
||||||
f = open(pid_file, "w")
|
f = open(pid_file, "w")
|
||||||
f.write(str(os.getpid()))
|
f.write(str(os.getpid()))
|
||||||
f.close()
|
f.close()
|
||||||
os.close(0)
|
os.close(0)
|
||||||
os.dup2(1, 2)
|
os.dup2(1, 2)
|
||||||
def tear_down_proc():
|
|
||||||
try:
|
def tear_down_proc():
|
||||||
os.unlink(pid_file)
|
try:
|
||||||
except:
|
os.unlink(pid_file)
|
||||||
pass
|
except:
|
||||||
if os.path.exists(pid_file):
|
pass
|
||||||
f = open(pid_file, "r")
|
|
||||||
pid = f.read()
|
if os.path.exists(pid_file):
|
||||||
f.close()
|
f = open(pid_file, "r")
|
||||||
if os.path.exists("/proc/%s/status" % pid):
|
pid = f.read()
|
||||||
return Response("Scanner is already running.\n", mimetype="text/plain")
|
f.close()
|
||||||
process = subprocess.Popen(args, close_fds=True, stdout=subprocess.PIPE, preexec_fn=setup_proc)
|
if os.path.exists("/proc/%s/status" % pid):
|
||||||
response = ProcessWrapper(process, tear_down_proc)
|
return Response(
|
||||||
return Response(response, direct_passthrough=True, mimetype="text/plain")
|
"Scanner is already running.\n", mimetype="text/plain")
|
||||||
|
|
||||||
|
process = subprocess.Popen(
|
||||||
|
args, close_fds=True, stdout=subprocess.PIPE, preexec_fn=setup_proc)
|
||||||
|
response = ProcessWrapper(process, tear_down_proc)
|
||||||
|
return Response(response, direct_passthrough=True, mimetype="text/plain")
|
||||||
|
@ -5,19 +5,21 @@ from CachePath import message
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
def main():
|
|
||||||
reload(sys)
|
|
||||||
sys.setdefaultencoding("UTF-8")
|
|
||||||
|
|
||||||
if len(sys.argv) != 3:
|
def main():
|
||||||
print "usage: %s ALBUM_PATH CACHE_PATH" % sys.argv[0]
|
reload(sys)
|
||||||
return
|
sys.setdefaultencoding("UTF-8")
|
||||||
try:
|
|
||||||
os.umask(022)
|
if len(sys.argv) != 3:
|
||||||
TreeWalker(sys.argv[1], sys.argv[2])
|
print "usage: %s ALBUM_PATH CACHE_PATH" % sys.argv[0]
|
||||||
except KeyboardInterrupt:
|
return
|
||||||
message("keyboard", "CTRL+C pressed, quitting.")
|
|
||||||
sys.exit(-97)
|
try:
|
||||||
|
os.umask(022)
|
||||||
|
TreeWalker(sys.argv[1], sys.argv[2])
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
message("keyboard", "CTRL+C pressed, quitting.")
|
||||||
|
sys.exit(-97)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
Loading…
Reference in New Issue
Block a user