#!/usr/bin/python3
# to forece reindex pass a single argument:
# force
# to 'hide' files
# NB: file still will be accessible for ALL who know a mangled url
# place file into '.hide' subdirectory
# to set user thumbs:
# path.thumb.png - user thumb for 'path'
# path/.thumb.png - user thumb for 'path'
# path/.thumb..png - default thumb for unknown extension
# path/.thumb...png - default thumb for subdirectories
# path/.thumb.ext.png - default thumb for 'ext'
# path/.thumb/.png - default thumb for unknown extension
# path/.thumb/..png - default thumb for subdirectories
# path/.thumb/ext.png - default thumb for 'ext'
import os
import sys
import time
import base64
import hashlib
import urllib.parse
import config
import thumb
import template
thumbext = [ 'png', 'svg', 'jpg' ]
skipname = [ 'index.html', 'indexd.html' ]
def join(*args): return '/'.join(a for a in args if a)
def isdir(path): return not os.path.islink(path) and os.path.isdir(path)
def isfile(path): return not os.path.islink(path) and os.path.isfile(path)
def nameext(name):
name = os.path.basename(name)
i = name.rfind('.')
return (name[:i], name[i+1:].lower()) if i >= 0 else (name, '')
def remove(path):
if os.path.isfile(path) or os.path.islink(path):
print('remove file:', path)
os.remove(path)
if os.path.isdir(path):
for f in os.listdir(path): remove(join(path, f))
print('remove dir:', path)
os.rmdir(path)
def mkdirfor(path):
os.makedirs(os.path.dirname(path), exist_ok = True)
def touch(path, msg = True):
if msg: print('create file:', path)
assert(not path in files)
mkdirfor(path)
files.add(path)
return path
def link(src, dst):
touch(dst, False)
if not force and isfile(dst) and os.path.samefile(src, dst): return
remove(dst)
print('create link:', dst, '(' + src + ')')
os.link(src, dst, follow_symlinks = False)
def clean(path, root = True):
if os.path.islink(path) or (os.path.isfile(path) and not path in files):
print('remove file:', path)
os.remove(path)
elif os.path.isdir(path):
for f in os.listdir(path): clean(join(path, f), False)
if not root and not os.listdir(path):
print('remove dir:', path)
os.rmdir(path)
def mangle(path):
return base64.b64encode(hashlib.sha256((config.salt + ':' + path).encode()).digest()).decode()[:20].replace('/', '_').replace('+', '-')
class Node:
def __init__(self, parent, src, name, hide = False):
#print('node:', src)
self.parent = parent
self.src = src
self.name = name
self.title = name if parent else config.title
self.isdir = isdir(src)
self.ext = '' if self.isdir else nameext(name)[1]
self.path = join(parent.path, name) if parent else name
self.date = os.path.getmtime(src)
self.mangle = config.mangle + mangle(self.path) + '.' + self.ext
self.hide = hide or (parent and parent.hide)
self.url = join((parent.url if self.parent else config.url), urllib.parse.quote(self.name))
self.mangleurl = join(config.url, self.mangle)
self.hideurl = join(config.hideurl, self.path) if self.isdir else (self.mangleurl if self.hide else self.url)
self.thumburl = None
self.thumbs = parent.thumbs if parent else {}
self.subs = {}
if parent: parent.touchdate(self.date); parent.subs[name] = self
def touchdate(self, date):
if self.date >= date: return
self.date = date
if self.parent: self.parent.touchdate(date)
def scan(self):
if not self.isdir: return
td = join(self.src, '.thumb')
if isdir(td):
for f in os.listdir(td):
n, e = nameext(f); fn = join(td, f)
if e in thumbext and isfile(fn): self.thumbs[None if n == '.' else n] = fn
for f in os.listdir(self.src):
fn = join(self.src, f)
if f[:1] != '.' or not isfile(fn): continue
n, e = nameext(f[1:])
if not e in thumbext: continue
n, e = nameext(n)
if n == 'thumb': self.thumbs[e] = fn
if n == 'thumb.': self.thumbs[None] = fn
for f in os.listdir(self.src): self.sub(join(self.src, f), f, False)
hd = join(self.src, '.hide')
if isdir(hd):
for f in os.listdir(hd): self.sub(join(hd, f), f, True)
def sub(self, src, name, hide):
n, e = nameext(name)
if ( self.isdir
and name
and name not in self.subs
and name[0] != '.'
and not name.lower() in skipname
and (self.parent or not name.lower().startswith(config.mangle))
and nameext(n)[1] != 'thumb'
and (isfile(src) or isdir(src)) ):
Node(self, src, name, hide).scan()
def makefile(self):
if self.isdir: return
link(self.src, join(config.dst, self.mangle))
if not self.hide: link(self.src, join(config.dst, self.path))
def makethumb(self):
if self.hide and not config.hidedst: return
ts = None
td = None
dst = config.hidedst if self.hide else config.dst
url = self.hideurl if self.hide else self.url
if self.isdir:
dst = join(dst, self.path, '.thumb.')
url = join(url, '.thumb.')
else:
dst = join(dst, self.path + '.thumb.')
url += '.thumb.'
for e in reversed(thumbext):
if isfile(join(self.src, '.thumb.' + e)): ts = join(self.src, '.thumb.' + e)
for e in reversed(thumbext):
if isfile(self.src + '.thumb.' + e): ts = self.src + '.thumb.' + e
if not ts and not self.isdir:
self.path + '.thumb.'
if not force:
for e in reversed(thumbext):
if isfile(dst + e) and os.path.getmtime(dst + e) > self.date: td = dst + e
if td: touch(td, False)
else:
mkdirfor(dst + thumbext[0])
td = thumb.make(self.src, dst + thumbext[0]);
if td: touch(td)
if not ts and not td:
if self.isdir: ts = self.thumbs.get(None)
else: ts = self.thumbs.get(self.ext, self.thumbs.get(''))
if not td and ts:
td = dst + nameext(ts)[1]
link(ts, td)
if td: self.thumburl = url + nameext(td)[1]
def makeinfo(self):
return {
'name': self.name,
'title': self.title,
'date': time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(self.date)),
'isdir': self.isdir,
'hide': self.hide,
'url': self.url,
'hideurl': self.hideurl,
'mangleurl': self.mangleurl,
'thumburl': self.thumburl }
def makepath(self):
r = []; p = ''; n = self
while n: r.insert(0, {'name': n.name, 'title': n.title, 'url': p}); p = join(p, '..'); n = n.parent
return r
def makeinfoex(self, bydate):
r = self.makeinfo()
r['bydate'] = bydate
r['path'] = self.makepath()
r['tw'] = config.thumbw
r['th'] = config.thumbh
return r
def makeindex(self, bydate):
if not self.isdir or self.hide: return
fn = join(config.dst, self.path, ('indexd.html' if bydate else 'index.html'))
if not force and isfile(fn) and os.path.getmtime(fn) > self.date: touch(fn, False); return
r = self.makeinfoex(bydate)
r['subs'] = []
for n in sorted(self.subs.values(), key=lambda n: (not n.isdir, (-n.date if bydate else n.name))):
if not n.hide: r['subs'].append(n.makeinfo())
with open(touch(fn), 'w') as f: tpl.write(f, r)
def makehideindex(self, bydate):
if not self.isdir or not config.hidedst: return
fn = join(config.hidedst, self.path, ('indexd.html' if bydate else 'index.html'))
if not force and isfile(fn) and os.path.getmtime(fn) > self.date: touch(fn, False); return
r = self.makeinfoex(bydate)
r['subs'] = [ n.makeinfo() for n in sorted(self.subs.values(), key=lambda n: (not n.isdir, (-n.date if bydate else n.name), not n.hide)) ]
with open(touch(fn), 'w') as f: hidetpl.write(f, r)
def make(self):
#print('make:', self.path)
for n in self.subs.values(): n.make()
self.makefile()
self.makethumb()
self.makeindex(False)
self.makeindex(True)
self.makehideindex(False)
self.makehideindex(True)
#os.umask(0o0027)
force = len(sys.argv) > 1 and sys.argv[1] == 'force'
files = set()
tpl = template.TplLoader.load("tpl/index.tpl")
hidetpl = template.TplLoader.load("tpl/hide.tpl")
n = Node(None, config.src, '')
n.scan()
if time.time() - n.date <= config.guardsecs:
print('files are being update just now')
exit(0)
n.make()
clean(config.dst)
if config.hidedst: clean(config.hidedst)
#print('done')