diff --git a/README.md b/README.md
index eeb8ef2..fe04e4d 100644
--- a/README.md
+++ b/README.md
@@ -1 +1 @@
-"# Video Viewer App"
+# Video Viewer App
diff --git a/server.py b/server.py
new file mode 100644
index 0000000..66ae734
--- /dev/null
+++ b/server.py
@@ -0,0 +1,784 @@
+
+
+
+import json, codecs, re, os, pypandoc, striprtf, sqlite3, random
+import heapq, shutil, datetime, urllib
+import itertools, time, markdown, csv, os.path, webbrowser, threading
+import hashlib, funcy, platform, sys, socket
+from striprtf.striprtf import rtf_to_text
+from functools import wraps
+from queue import Queue
+from flask import Flask, request, send_from_directory, Response, render_template
+from flask_socketio import SocketIO, emit
+from werkzeug.routing import PathConverter
+from orgpython import to_html
+from importlib import reload
+from pathlib import Path
+
+
+from stat import S_ISREG, ST_CTIME, ST_MODE
+
+this_machine = socket.gethostname()
+print(this_machine)
+
+
+# pypandoc striprtf sqlite3 heapq markdown webbrowser funcy platform socket functools queue flask flask_socketio werkzeug orgpython importlib pathlib stat
+
+# orgpython webbrowser functools socket platform sqlite3 heapq funcy striprtf flask_socketio queue stat importlib
+
+
+
+# https://github.com/SethMMorton/natsort
+# Simple yet flexible natural sorting in Python
+
+
+from servertoys import *
+import servertoys
+
+#q = Queue()
+
+
+
+
+def dict_factory(cursor, row):
+ d = {}
+ for idx, col in enumerate(cursor.description):
+ d[col[0]] = row[idx]
+ return d
+
+
+
+def server_save(key,value):
+ codecs.open('cache/server_data.txt','a').write( "%s=%s\n" % (str(key),str(value)))
+
+def flask_thread(q=0):
+ print(" my __name__ is...", end=" ")
+ print(__name__)
+
+
+ #print("Starting ... flask_thread ...")
+ #app = Flask(__name__, static_url_path='/cache',
+ # static_folder='cache',)
+
+
+ app = Flask(__name__, static_url_path='')
+ app.config['SECRET_KEY'] = 'secret!abc#xyz91239456'
+ app.jinja_env.auto_reload = True
+
+ print(app)
+
+ #socketio = SocketIO(app)
+
+ app.config['TEMPLATES_AUTO_RELOAD'] = True
+
+ #def before_request():
+ # app.jinja_env.cache = {}
+
+ @app.route('/all')
+ def list_all_pics():
+ all = servertoys.BASE # "/home/phowell/hd2/deep1_homedir/Documents/ooo/"
+
+ #paths = sorted(Path(dirpath).iterdir(), key=os.path.getmtime)
+
+
+ allp = os.listdir(all)
+ # allp = [base(x) for x in allp]
+ #print(allp)
+ return "\n".join([ "
%s" % a('/img/'+x, x) for x in filter(isPic, allp) ])
+
+ @app.route('/main')
+ def main_app():
+ all = servertoys.BASE # "/home/phowell/hd2/deep1_homedir/Documents/ooo/"
+ allp = os.listdir(all)
+ allp = ['/img/'+x for x in filter(isPic, allp) ]
+ allp.sort()
+
+ allj = json.dumps(allp,indent=2)
+ return render_template('index.html', all_pics_json=allj, index="100")
+
+ @app.route('/main/')
+ def main_app_indexed(index):
+ all = servertoys.BASE # "/home/phowell/hd2/deep1_homedir/Documents/ooo/"
+ allp = os.listdir(all)
+ allp = ['/img/'+x for x in filter(isPic, allp) ]
+ allp.sort()
+
+ allj = json.dumps(allp,indent=2)
+ return render_template('index.html', all_pics_json=allj, index=str(index))
+
+
+ @app.route('/manage/')
+ def manager_app_indexed(index):
+ all = servertoys.BASE # "/home/phowell/hd2/deep1_homedir/Documents/ooo/"
+ allp = os.listdir(all)
+ allp = ['/img/'+x for x in filter(isPic, allp) ]
+ allp.sort()
+
+ allj = json.dumps(allp,indent=2)
+ return render_template('manage.html', all_pics_json=allj, index=str(index))
+
+
+ @app.route('/meme/')
+ def manager_meme_indexed2(index):
+ all = servertoys.MEMEBASE # "/home/phowell/hd2/peter_home/images/Ok Pictures and Memes/"
+ allp = os.listdir(all)
+ #allp = ['/mimg/'+x for x in filter(isPic, allp) ]
+ allp = ['/mimg/'+x for x in allp ]
+ allp.sort()
+
+ allj = json.dumps(allp,indent=2)
+ return render_template('manage2.html', which=2, all_pics_json=allj, index=str(index))
+
+
+ @app.route('/m/')
+ def manager_app_indexed2(index):
+ all = servertoys.BASE # "/home/phowell/hd2/deep1_homedir/Documents/ooo/"
+ allp = os.listdir(all)
+ allp = ['/img/'+x for x in filter(isPic, allp) ]
+ allp.sort()
+
+ allj = json.dumps(allp,indent=2)
+ return render_template('manage2.html', which=1, all_pics_json=allj, index=str(index))
+
+
+ @app.route('/importer/')
+ def manager_importer(index):
+ chanpath = servertoys.CHANBASE # "/home/phowell/hd2/peter_home_offload/Documents/scripts/chan/"
+ if index=='list':
+ return "
".join( [ "%s" % (x,x) for x in sorted(os.listdir(chanpath))] )
+
+ #return "
".join( [ "%s" % ("/chan/"+index+"/"+x,x) for x in os.listdir(chanpath + index)] )
+
+ all = chanpath + index
+ allp = os.listdir(all)
+ allp = ['/chan/'+index+'/'+ x for x in allp] # filter(isPic, allp) ]
+ allp.sort()
+
+ allj = json.dumps(allp,indent=2)
+ return render_template('importer.html', all_pics_json=allj, index=0)
+
+
+ @app.route('/alltags')
+ def alltags():
+ return servertoys.get_all_tags()
+
+ @app.route('/alltags/')
+ def alltags_which(which):
+ return servertoys.get_all_tags(which)
+
+ @app.route('/add_pic_tag//')
+ def apt(ttt,ppp):
+ return servertoys.add_pic_tag(ttt,ppp)
+
+
+
+
+ @app.route('/prefix/')
+ def prefix_app_indexed(index):
+ all = servertoys.BASE # "/home/phowell/hd2/deep1_homedir/Documents/ooo/"
+
+ allp = os.listdir(all)
+ allp = ['/img/'+x for x in filter(isPic, allp) if plain_sw(x,index) ]
+ #funcy.filter(lambda x: plain(x).startswith('/img/'+index), allp)
+ allp.sort()
+ allj = json.dumps(allp,indent=2)
+ return render_template('index.html', all_pics_json=allj, index="0")
+
+
+ @app.route('/add_pic/')
+ def b(path):
+ return add_pic(path)
+
+ @app.route('/pic')
+ def a():
+ return get_all_pics(1)
+
+ @app.route('/pic/')
+ def c(index):
+ allp = get_all_pics()
+ #allp = ['/img/'+x for x in filter(isPic, allp) ]
+ #allp.sort()
+
+ #allj = json.dumps(allp,indent=2)
+ allp = [ii for ii in map( lambda x: "/img/" + x, funcy.pluck( 'path', allp ))]
+ allj = json.dumps( allp )
+ return render_template('manage.html', all_pics_json=allj, index=str(index))
+
+ @app.route('/i')
+ def db_img():
+ global con, cur
+ con = sqlite3.connect(LIBBASE + 'ooo.db')
+ con.row_factory = dict_factory
+ cur = con.cursor()
+
+ cur.execute("SELECT * FROM pics p JOIN pictag pt ON pt.pic=p.id JOIN tags t ON t.id=pt.tag")
+ result = cur.fetchall()
+ return "%s
" % json.dumps( result,indent=2 )
+
+ ###
+ ###
+ ### VIDEO
+ ###
+ ###
+ ###
+
+
+ @app.route('/videos')
+ def vs_app():
+ global MOVBASE
+
+
+ # get all entries in the directory
+ # Get their stats
+ # leave only regular files, insert creation date
+
+
+ #os.path.join(MOVBASE, file_name) for file_name in os.listdir( MOVBASE ))
+ #print( list(entries)[1:10] )
+ #print( list(entries)[1:10] )
+
+ #entries = ((os.stat(path), path) for path in entries)
+ #entries = ((stat[ST_CTIME], path) for stat, path in entries if S_ISREG(stat[ST_MODE]))
+ #print( list(allp)[1:10] )
+ #allp = os.listdir(MOVBASE)
+ #allp.sort()
+ #print( list(allp)[1:10] )
+
+
+
+ allp = sorted(Path(MOVBASE).iterdir(), key=os.path.getmtime)
+ allp = filter(isVid, allp)
+ allp = [ '/vid/'+urllib.parse.quote(x.name ) for x in allp ]
+
+ allj = json.dumps(allp) # ,indent=2)
+ return render_template('allvids.html', all_pics_json=allj, index="100")
+
+ @app.route('/video')
+ def v_app():
+ global MOVBASE
+ #allp = os.listdir(MOVBASE)
+ #allp = ['/vid/'+urllib.parse.quote(x) for x in filter(isVid, allp) ]
+ #allp.sort()
+
+ allp = sorted(Path(MOVBASE).iterdir(), key=os.path.getmtime)
+ allp = filter(isVid, allp)
+ allp = [ '/vid/'+urllib.parse.quote(x.name ) for x in allp ]
+
+
+ allj = json.dumps(allp,indent=2)
+ return render_template('vindex.html', all_pics_json=allj, index="100")
+
+ @app.route('/video/')
+ def v_app_indexed(index):
+ global MOVBASE
+ allp = filter(isVid,sorted(Path(MOVBASE).iterdir(), key=os.path.getmtime))
+ allj = json.dumps([ '/vid/'+urllib.parse.quote(x.name ) for x in allp ],indent=2)
+ return render_template('vindex.html', all_pics_json=allj, index=str(index))
+
+
+ #
+ # SORTING FILES
+ #
+
+ @app.route('/sort')
+ def sorter():
+ return plain_sort()
+
+
+ @app.route('/sort/')
+ def sort_index(index):
+ return plain_sort(index='')
+
+
+
+
+ #
+ # SAVING STUFF
+ #
+
+ @app.route('/save', methods=['POST'])
+ def save_post():
+ now = datetime.now().strftime('%Y%m%dT%H%M')
+ path = request.form['path']
+ txt = request.form['content']
+
+ o3 = codecs.open(server.writing_path + path, 'r', 'utf-8')
+ orig_text = o3.read()
+ o3.close()
+
+ bu_filename = server.writing_path + 'older_copies/' + path + '_' + now + '.md'
+ o2 = codecs.open( bu_filename, 'w', 'utf-8' )
+ o2.write(orig_text)
+ o2.close()
+ print('wrote backup to %s.' % bu_filename)
+
+ o1 = codecs.open(server.writing_path+path, 'w', 'utf-8')
+ o1.write(txt)
+ o1.close()
+ return "Successfully Saved
" + a('back to writing folder','/x/writing/index') + \
+ " " + a('back to home','/')
+
+
+ #
+ # SERVER maintenance type stuff
+ @app.route('/rl')
+ def restart():
+ reload(servertoys)
+ #reload(localcache)
+ return "Server code reloaded"
+
+ @app.route("/x///")
+ def dispatch3(func,arg,arrg):
+ print("2 args")
+ return "" + server_dispatch(func, arg, arrg)
+
+ @app.route("/x//")
+ def dispatch2(func,arg):
+ print("1 arg")
+ return "" + server_dispatch(func, arg)
+
+ @app.route("/x/")
+ def dispatch(func):
+ print("0 arg")
+ return server_dispatch(func)
+
+ @app.route("/api///")
+ def dispatch3j(func,arg,arrg):
+ print("json, 3 args")
+ return Response(server_dispatch(func, arg, arrg), mimetype='text/json')
+
+ @app.route("/api//")
+ def dispatch2j(func,arg):
+ print("json, 1 arg")
+ return Response(server_dispatch(func, arg), mimetype='text/json')
+
+ @app.route("/api/")
+ def dispatch1j(func):
+ print("json, 0 arg")
+ return Response(server_dispatch(func), mimetype='text/json')
+
+ @app.route("/")
+ def home():
+ return "Homepage
"
+
+ #
+ # STATIC ROUTES
+ #
+
+ """@app.route('/lib/')
+ def send_jslib(path):
+ return send_from_directory('gui/lib', path)"""
+
+ @app.route('/data/')
+ def send_cachedata(path):
+ #myfile = os.path.join('cache', path).replace('\\','/')
+ print(path)
+ #return app.send_static_file(myfile)
+ return send_from_directory('cache', path)
+
+ #@app.route('/hello/')
+ #@app.route('/hello/')
+
+
+ @app.route("/save//")
+ def s(key,val):
+ server_save(key,val)
+ return tag('h1','Saved.') + "
" + tag('p', 'Saved: %s = %s' % (str(key),str(val)))
+
+ @app.route("/sample")
+ def do_sample():
+ return sample()
+
+
+ @app.route("/crazy")
+ def hello():
+ r = ''
+ r += tag('style', 'textarea { white-space:nowrap; }')
+ r += tag('body', \
+ tagc('div','container-fluid', \
+ tagc('div','row', \
+ tagc( 'div', 'col-md-6', tag('pre', walk_file() ) ) + \
+ tagc( 'div', 'col-md-6', 'Column 2' + a('Shut Down','/shutdown' ) ) ) ) )
+
+
+
+ return r
+
+ @app.route("/sd")
+ def sd():
+ print('SIGINT or CTRL-C detected. Exiting gracefully')
+ func = request.environ.get('werkzeug.server.shutdown')
+ if func is None:
+ raise RuntimeError('Not running with the Werkzeug Server')
+ func()
+ return "Server has shut down."
+
+
+ #@socketio.on('my event', namespace='/test')
+ #def test_message(message):
+ # print('received and event: "my event" from page. message is: %s' % message)
+ # emit('my response', {'data': 'got it! it is MYEVENT'})
+
+ # Main images folder
+
+ @app.route('/img/')
+ def send_img(path):
+ return send_from_directory(BASE, path)
+
+ @app.route('/mimg/')
+ def send_mimg(path):
+ return send_from_directory(MEMEBASE, path)
+
+ @app.route('/chan/')
+ def send_chan_img(path):
+ chanpath = servertoys.CHANBASE # "/home/phowell/hd2/peter_home_offload/Documents/scripts/chan/"
+ return send_from_directory(chanpath, path)
+
+ @app.route('/vid/')
+ def send_vid(path):
+ return send_from_directory(MOVBASE, urllib.parse.unquote(path))
+
+ # Static folder
+
+ @app.route('/lib/')
+ def send_lib(path):
+ return send_from_directory(LIBBASE, path)
+
+
+ app.run(host=HOST, port=PORT)
+
+
+ #socketio.run(app, host= '0.0.0.0', port=9998)
+
+
+
+
+
+
+#################################################################################################################
+#################################################################################################################
+######
+###### server infrastructure
+######
+
+
+
+
+def server_dispatch_json(function_name,arg='', arg2=''):
+ print("Looking for function: %s. arg:%s. arg2:%s." % (function_name, arg, arg2))
+ try:
+ result = "" + globals()[function_name](arg, arg2)
+ print("doing 2 args")
+ return result
+ except Exception as e:
+ print("Error with that: %s" % str(e))
+ try:
+ result = "" + globals()[function_name](arg) #
+ print("doing 1 arg")
+ return result
+ except Exception as f:
+ print("Error with that: %s" % str(f))
+ try:
+ result = globals()[function_name]()
+ print("doing 0 arg")
+ return result
+ except Exception as gg:
+ print("Error with that: %s" % str(gg))
+ return json.dumps({'result':'failed: exception', 'e1':str(e), 'e2':str(f), 'e3':str(gg)}, indent=2)
+
+
+def server_dispatch(function_name,arg='', arg2=''):
+ print("Looking for function: %s. arg:%s. arg2:%s." % (function_name, arg, arg2))
+ try:
+ result = "" + globals()[function_name](arg, arg2)
+ print("doing 2 args")
+ return result
+ except Exception as e:
+ print("Error with that: %s" % str(e))
+ try:
+ result = "" + globals()[function_name](arg) #
+ print("doing 1 arg")
+ return result
+ except Exception as f:
+ print("Error with that: %s" % str(f))
+ try:
+ result = globals()[function_name]()
+ print("doing 0 arg")
+ return result
+ except Exception as gg:
+ print("Error with that: %s" % str(gg))
+ return json.dumps({'result':'failed: exception', 'e1':str(e), 'e2':str(f), 'e3':str(gg)}, indent=2)
+
+
+#################################################################################################################
+#################################################################################################################
+######
+###### server startup
+######
+
+def serve():
+ flask_thread()
+ #x = threading.Thread(target=flask_thread, args=(q,))
+ #x.start()
+ #webbrowser.open_new_tab("http://localhost:%s" % str(PORT))
+ print("Started?")
+
+if __name__ == '__main__':
+ print("Serving on %s" % str(PORT))
+ #serve()
+ flask_thread()
+ print("...serve() function finished?...")
+else:
+ pass
+ #print("Doing nothing.")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#################################################################################################################
+#################################################################################################################
+######
+###### MORE MORE MORE
+######
+
+
+## Extract frames from video
+#
+# ffmpeg -i DJI_0024.MP4 -vf fps=10/1 output%04d.jpg
+
+
+
+# list files
+# - all
+# - by tag
+# - by rating
+# - untagged / unrated
+# - query
+
+
+# load / save tag, rate data for a file
+
+
+
+# api for web / events
+# - rating
+# - tag add / remove
+
+
+
+# events for new images, scrapes, imports
+
+# a queue of unprocessed files
+
+# approve import, move file & save metadata
+
+# report on original, crop, resizes,
+
+# add to queue for creating crops, resizes, removals
+
+
+# add to queue for scrape from insta
+
+# add to queue for scrape from chan
+
+# add to queue for scrape from reddit
+
+# toc of folders that have been scraped, ready for import check.
+
+# list of urls that have been scraped. Don't double-download.
+
+
+
+
+
+"""
+
+
+use: D:\peter_home\Documents\scripts\chan\get.py
+
+use: D:\peter_home\Documents\scripts\imageworld\cache.py
+
+
+https://reddit.com/r/18_19/top?t=year
+https://reddit.com/r/2busty2hide/top?t=year
+https://reddit.com/r/aa_cups/top?t=year
+https://reddit.com/r/adorableporn/top?t=year
+https://reddit.com/r/asiancuties/top?t=year
+https://reddit.com/r/asiansgonewild/top?t=year
+https://reddit.com/r/asianhotties/top?t=year
+https://reddit.com/r/barelylegal/top?t=year
+https://reddit.com/r/barelylegalteens/top?t=year
+https://reddit.com/r/biggerthanyouthought/top?t=year
+https://reddit.com/r/bodyperfection/top?t=year
+https://reddit.com/r/boobies/top?t=year
+https://reddit.com/r/nsfwbraids/top?t=year
+https://reddit.com/r/burstingout/top?t=year
+https://reddit.com/r/bustypetite/top?t=year
+https://reddit.com/r/chubby/top?t=year
+https://reddit.com/r/cleavage/top?t=year
+https://reddit.com/r/curls/top?t=year
+https://reddit.com/r/curvy/top?t=year
+https://reddit.com/r/cutelittlebutts/top?t=year
+https://reddit.com/r/darkangels/top?t=year
+https://reddit.com/r/downblouse/top?t=year
+https://reddit.com/r/dirtysmall/top?t=year
+https://reddit.com/r/ebony/top?t=year
+https://reddit.com/r/fauxbait/top?t=year
+https://reddit.com/r/fuckdoll/top?t=year
+https://reddit.com/r/funsized/top?t=year
+https://reddit.com/r/funwithfriends/top?t=year
+https://reddit.com/r/ginger/top?t=year
+https://reddit.com/r/girlsinschooluniforms/top?t=year
+https://reddit.com/r/GirlsInWhiteTanks/top?t=year
+https://reddit.com/r/girlskissing/top?t=year
+https://reddit.com/r/gonewild18/top?t=year
+https://reddit.com/r/gwnerdy/top?t=year
+https://reddit.com/r/happyembarassedgirls/top?t=year
+https://reddit.com/r/juicyasians/top?t=year
+https://reddit.com/r/just18/top?t=year
+https://reddit.com/r/latinas/top?t=year
+https://reddit.com/r/legalteens/top?t=year
+https://reddit.com/r/o_faces/top?t=year
+https://reddit.com/r/petite/top?t=year
+https://reddit.com/r/petitegonewild/top?t=year
+https://reddit.com/r/pokies/top?t=year
+https://reddit.com/r/prettygirls/top?t=year
+https://reddit.com/r/realasians/top?t=year
+https://reddit.com/r/realgirls/top?t=year
+https://reddit.com/r/redheads/top?t=year
+https://reddit.com/r/repressedgonewild/top?t=year
+https://reddit.com/r/schoolgirlskirts/top?t=year
+https://reddit.com/r/seethru/top?t=year
+https://reddit.com/r/sexyfrex/top?t=year
+https://reddit.com/r/sexygirls/top?t=year
+https://reddit.com/r/shorthairchicks/top?t=year
+https://reddit.com/r/slimthick/top?t=year
+https://reddit.com/r/smallboobs/top?t=year
+https://reddit.com/r/smallcutie/top?t=year
+https://reddit.com/r/stacked/top?t=year
+https://reddit.com/r/tanktops/top?t=year
+https://reddit.com/r/teenbeauties/top?t=year
+https://reddit.com/r/theratio/top?t=year
+https://reddit.com/r/theunderboob/top?t=year
+https://reddit.com/r/thick/top?t=year
+https://reddit.com/r/thicker/top?t=year
+https://reddit.com/r/tinytits/top?t=year
+https://reddit.com/r/twingirls/top?t=year
+https://reddit.com/r/toocuteforporn/top?t=year
+https://reddit.com/r/WhiteTopAndJeans/top?t=year
+https://reddit.com/r/womenofcolor/top?t=year
+https://reddit.com/r/xsmallgirls/top?t=year
+
+
+toocuteforporn
+
+
+18_19
+2busty2hide
+aa_cups
+adorableporn
+asiancuties
+asiansgonewild
+asianhotties
+barelylegal
+barelylegalteens
+biggerthanyouthought
+bodyperfection
+boobies
+braids
+burstingout
+bustypetite
+chubby
+cleavage
+curls
+curvy
+cutelittlebutts
+darkangels
+downblouse
+dirtysmall
+ebony
+fauxbait
+fuckdoll
+funsized
+funwithfriends
+ginger
+girlsinschooluniforms
+GirlsInWhiteTanks
+girlskissing
+gonewild18
+gwnerdy
+happyembarassedgirls
+juicyasians
+just18
+latinas
+legalteens
+o_faces
+petite
+petitegonewild
+pokies
+prettygirls
+realasians
+realgirls
+redheads
+repressedgonewild
+seethru
+sexyfrex
+sexygirls
+shorthairchicks
+slimthick
+smallboobs
+smallcutie
+stacked
+tanktops
+teenbeauties
+theratio
+theunderboob
+thick
+thicker
+tinytits
+twingirls
+toocuteforporn
+WhiteTopAndJeans
+womenofcolor
+xsmallgirls
+
+
+
+
+
+
+chan queue
+
+younggiant https://8kun.top/s/res/534.html
+acup https://8kun.top/s/res/3386.html
+penninsula https://8kun.top/s/res/35323.html
+caramel https://8kun.top/s/res/9384.html
+chub2 https://8kun.top/s/res/2290.html
+amateur3 https://8kun.top/s/res/27209.html
+vball https://8kun.top/s/res/19691.html
+ariel https://8kun.top/s/res/2739.html
+fb2 https://8kun.top/s/res/27347.html
+models2 https://8kun.top/s/res/5853.html
+south https://8kun.top/s/res/17718.html
+milena https://8kun.top/s/res/16383.html
+pigtails https://8kun.top/s/res/186.html
+slut https://8kun.top/s/res/28135.html
+ibtc https://8kun.top/s/res/15527.html
+creep https://boards.4chan.org/b/thread/859972934
+girlb https://boards.4chan.org/b/thread/859975536
+celeb https://boards.4chan.org/b/thread/860000898
+fb3 https://boards.4chan.org/b/thread/860003419
+
+
+
+"""
diff --git a/servertoys.py b/servertoys.py
new file mode 100644
index 0000000..f43fb68
--- /dev/null
+++ b/servertoys.py
@@ -0,0 +1,459 @@
+
+import os,json, funcy, re, sqlite3, operator, sys, socket
+from flask import render_template
+from collections import defaultdict
+
+q = ''
+
+DEEP_IP = '192.168.1.6'
+
+this_machine = socket.gethostname()
+
+if this_machine=='ROGDESKTOP':
+ HOST = "192.168.1.7"
+ PORT = "9999"
+ BASE = "\\\\%s\\hd2\\deep1_homedir\\Documents\\ooo\\" % DEEP_IP
+ LIBBASE = "\\\\%s\\hd2\\peter_home\\Documents\\scripts\\ooopics\\lib\\" % DEEP_IP
+ MEMEBASE = "\\\\s\\hd2\\peter_home\\images\\Ok Pictures and Memes\\" % DEEP_IP
+ CHANBASE = "\\\\%s\\hd2\\peter_home_offload\\Documents\\scripts\\chan\\" % DEEP_IP
+
+ MOVBASE = "\\\\%s\\hd2\\nogood\\media\\" % DEEP_IP
+ DOCBASE = "\\\\%s\\hd2\\bit_complete" % DEEP_IP
+
+else:
+
+ HOST = DEEP_IP
+ PORT = "9999"
+ BASE = "/media/hd2/deep1_homedir/Documents/ooo/"
+ LIBBASE = "/media/hd2/peter_home/Documents/scripts/ooopics/lib/"
+ MEMEBASE = "/media/hd2/peter_home/images/Ok Pictures and Memes/"
+ CHANBASE = "/media/hd2/peter_home_offload/Documents/scripts/chan/"
+
+ MOVBASE = "/media/hd2/nogood/media/"
+ DOCBASE = "/media/hd2/bit_complete"
+
+
+con = ''
+cur = ''
+
+
+def dict_factory(cursor, row):
+ d = {}
+ for idx, col in enumerate(cursor.description):
+ d[col[0]] = row[idx]
+ return d
+
+
+##########
+########## Determining the contents of a folder
+##########
+
+movietype = "mkv,mp4,wmv,avi,webm,mpg,mpeg".split(",")
+booktype = "epub,pdf,odt,docx,html,rtf,mobi,djvu,azw,azw3,chm".split(",")
+musictype = "flac,mp3,wma,m3u".split(",")
+imagetype = "jpg,jpeg,png,gif,webp".split(",")
+archivetype = "zip,rar,iso,tar,gz".split(",")
+
+types = {'movie':movietype, 'book':booktype, 'music':musictype, 'image':imagetype, 'archive':archivetype}
+#to_keep = "flac,epub,pdf,mkv,mp4,wmv,mp3,wma,avi,webm,m3u,zip,odt,jpeg,jpg,png,html,rtf,txt,mobi,djvu,azw,docx,azw3,".split(",")
+
+def cleantext(x):
+ return x.encode('utf8').decode(sys.stdout.encoding)
+
+def overview_folder(f):
+ count_files = 0
+ count_dirs = 0
+ size = 0
+ for root, dirs, files in os.walk(f):
+ count_files += len(files)
+ count_dirs += len(dirs)
+ size += sum( [ os.path.getsize(os.path.join(root,x)) for x in files ] )
+ return (count_files,count_dirs,size)
+
+
+def ending(x): return x.split('.')[-1].lower()
+
+def depth(x): return len(x.split('/')) - len(DOCBASE.split('/'))
+
+def greater_type(x):
+ for label,coll in types.items():
+ if x in coll: return label
+ return "unknown"
+
+def count_types(filelist):
+ endings = map(ending, filelist)
+ gts = list(map(greater_type, endings))
+ howmany = defaultdict(int)
+ for G in gts: howmany[G] += 1
+ sorted_d = sorted(howmany.items(), key=operator.itemgetter(1))
+ return sorted_d
+
+def most_common(filelist):
+ sortedtypes = count_types(filelist)
+ if len(sortedtypes)==0: return ''
+ y = sortedtypes[-1]
+ if not y[0]=='unknown': return y[0]
+ if len(sortedtypes)>1:
+ return sortedtypes[-2][0]
+ return ''
+
+#### GOAL: picture manager. Show folder, and
+####
+#### - mark for hide or delete
+#### - rate
+#### - accomodate major sortings, like personal/fam, gav, meme, x3, etc
+####
+#### - refer to any pic from Z app.
+####
+#### - make an infinite / zoomable ideaboard
+#### + make autolayout
+#### + make auto slideshows
+#### + multi-client slideshow for bigger effects (multi-projector)
+####
+#### - tag it
+#### - possibly move all selected or all of a tag if the drives cooperate
+#### - make hierarchy of tags so i can see or search a group of them
+#### - and hide some by default
+#### - framework for ai so i can practice
+#### + face detection
+#### + descriptions
+#### + similar photos
+#### + train fancier stuff, gans, face swaps, etc
+####
+
+# oopics look.py
+
+# my torrent sort.py
+
+# zettle app
+
+# my bookmark
+# app and
+# chrome bkmks
+
+
+
+# Serve the ooopics backend
+
+def tag(x,y): return "<%s>%s%s>" % (x,y,x)
+
+def img(s): return "
" % s
+
+def tagc(x,c,y): return '<%s class="%s">%s%s>' % (x,c,y,x)
+
+def a(href,txt): return '%s' % (href,txt)
+
+def isPic(s): return os.path.isfile(BASE+s)
+
+def isPicPre(s,p):
+ t = s
+ t.lower()
+ if plain(t).startswith(p):
+ return os.path.isfile(BASE+s)
+ return False
+
+def isVid(s): return s.name.endswith('.mp4') and os.path.isfile(MOVBASE+s.name)
+
+def base(s): return BASE + s
+
+def plain(s):
+ s = s.lower()
+ return re.sub(r'[^a-z0-9]','',s)
+
+def plain_sw(s,pre):
+ s = plain(s)
+ if s[0] == pre[0] and s[1]==pre[1] and s[2]==pre[2]:
+ print("%s startswith %s ?" % (s,pre))
+ if s.startswith(pre):
+ print('returning T')
+ return True
+ #print('returning F')
+ return False
+
+def first_n(s): # / i m g / _5____
+ n = 4
+ prefixlen = 4
+ s = plain(s)
+ if len(s) > (n+prefixlen): return s[prefixlen:prefixlen+n]
+ return s
+
+
+def modtime(s):
+ try:
+ return os.path.getmtime(base(s))
+ except:
+ return ''
+
+
+def main2():
+ all = BASE # "/home/phowell/hd2/deep1_homedir/Documents/ooo/"
+
+ allp = os.listdir(all)
+ allp = ['/img/'+x for x in filter(isPic, allp) ]
+ allp.sort( key=modtime )
+
+ allj = json.dumps(allp,indent=2)
+ return render_template('index.html', all_pics_json=allj, index="100")
+
+
+def prefix():
+ all = BASE # "/home/phowell/hd2/deep1_homedir/Documents/ooo/"
+
+ allp = os.listdir(all)
+ allp = ['/img/'+x for x in filter(isPic, allp) ]
+ allp.sort()
+ all_grps = [ [g for g in k] for k in funcy.partition_by(first_n, allp)]
+ all_label_grps = [ (first_n(k[0]),len(k),k) for k in all_grps if len(k) > 3 ]
+ print(all_label_grps)
+ #return json.dumps(all_label_grps,indent=2)
+ #all_label_grps.sort( key= lambda x: 0-x[1] )
+ all_label_grps.sort( key= lambda x: x[0] )
+
+ all_keys = [ k[0] for k in all_label_grps ]
+ amounts = [k[1] for k in all_label_grps ]
+
+ allk = json.dumps(all_keys,indent=2)
+ alla = json.dumps(amounts,indent=2)
+ allj = json.dumps(allp,indent=2)
+ return render_template('prefix.html', prefix_json=allk, amounts=alla, all_pics_json=allj, index="1")
+
+
+# secret Cles0wAwm9o4N_jnPNXOwgH2-DJXKw
+# name stm21
+
+def image_getter(thread_url):
+ r = requests.get(thread_url) #, auth=('user', 'pass'))
+ soup = bs(r.text,'html.parser')
+ img_url = a.title[href].attr('href')
+ folder = "tt1/"
+ (head,tail) = os.path.split(img_url)
+
+ if os.path.exists( os.path.join(folder,tail) ):
+ print(" + Image %i was already downloaded." % i)
+ return
+
+ print(" getting %s" % img_url)
+ r = requests.get(imgsrc,stream=True)
+ if r.status_code == 200:
+ with open(os.path.join(folder,tail),'wb') as f:
+ r.raw.decode_content = True
+ shutil.copyfileobj(r.raw, f)
+ print(" + Done with image.")
+ time.sleep(0.75)
+ else:
+ print(" - Failed with image.")
+
+
+
+
+
+# hd2/peter_home_offload/Documents/scripts/chan/
+# all the folders
+# display images in a folder, mark them if [yes] save to database.
+# [clean] the folder, delete 'unmarked' ones
+#
+
+def get_pic_by_id(g):
+ #global con, cur
+ con = sqlite3.connect(LIBBASE + 'ooo.db')
+ con.row_factory = dict_factory
+ cur = con.cursor()
+
+ cur.execute("SELECT * FROM pics p JOIN pictag pt ON pt.pic=p.id JOIN tags t ON t.id=pt.tag WHERE p.id=%s" % str(g))
+ return json.dumps(cur.fetchone())
+
+def get_all_pics(j=0):
+ #global con, cur
+ con = sqlite3.connect(LIBBASE + 'ooo.db')
+ con.row_factory = dict_factory
+ cur = con.cursor()
+
+ #cur.execute("SELECT * FROM pics p JOIN pictag pt ON pt.pic=p.id JOIN tags t ON t.id=pt.tag GROUP BY p.path")
+ cur.execute("SELECT * FROM pics")
+ if j: return json.dumps(cur.fetchall())
+ return cur.fetchall()
+
+def get_all_tags(which=1):
+ #global con, cur
+ con = sqlite3.connect(LIBBASE + 'ooo.db')
+ con.row_factory = dict_factory
+ cur = con.cursor()
+
+ cur.execute("SELECT * FROM 'tags' WHERE app=%s ORDER BY label" % str(which))
+ return json.dumps(cur.fetchall())
+
+
+
+def add_pic(path):
+ #global con, cur
+ con = sqlite3.connect(LIBBASE + 'ooo.db')
+ con.row_factory = dict_factory
+ cur = con.cursor()
+
+ cur.execute("INSERT INTO pics (path) VALUES('%s')" % str(path))
+ con.commit()
+ return json.dumps(cur.lastrowid)
+
+
+def add_pic_tag(tag_id,pic_id):
+ con = sqlite3.connect(LIBBASE + 'ooo.db')
+ cur = con.cursor()
+ # TODO select and dont duplicate
+ cur.execute("INSERT INTO pictag (pic,tag) VALUES('%s','%s')" % ( str(pic_id), str(tag_id) ) )
+ con.commit()
+ return json.dumps( {'status':'success','id':cur.lastrowid} )
+
+def add_tag(tag):
+ #global con, cur
+ con = sqlite3.connect(LIBBASE + 'ooo.db')
+ con.row_factory = dict_factory
+ cur = con.cursor()
+ cur.execute("INSERT INTO tags (label) VALUES('%s')" % tag )
+ con.commit()
+ return cur.lastrowid
+
+
+def add_pic_tag_str(pic_id,tag_str):
+ #global con, cur
+ con = sqlite3.connect(LIBBASE + 'ooo.db')
+ con.row_factory = dict_factory
+ cur = con.cursor()
+ cur.execute("SELECT * FROM tags WHERE label='%s'" % str(tag_str))
+ result = cur.fetchone()
+ if result:
+ tag_id = result['id']
+ else:
+ tag_id = add_tag(tag_str)
+ return add_pic_tag(pic_id,tag_id)
+
+
+
+
+def start_tt():
+ global q
+
+ url = 'https://www.reddit.com/r/TinyTits/top/?t=week'
+ r = requests.get(url) #, auth=('user', 'pass'))
+ open('temp.html','w').write(r.text)
+ soup = bs(r.text,'html.parser')
+ threads = []
+ qqueue = []
+ i = 0
+
+
+ print(soup.get_text())
+
+ for img in soup.select('a.title'):
+
+ link = img.attrs('href')
+ print(link)
+ image_getter(link)
+ i += 1
+
+ print("There are %i images to fetch." % len(qqueue))
+ pool = ThreadPoolExecutor(max_workers=5)
+ for q in qqueue:
+ q["total"] = len(qqueue)
+ future = pool.submit(image_getter, q)
+
+
+
+
+def plain_sort(index=''):
+ root_len = len(DOCBASE)
+
+ output = "\n"
+ output += tag('h2','Downloaded Files')
+ print(' --starting dir listing')
+
+ folder_line = '\n'
+ file_line = '\n'
+
+ for (root,dirs,files) in os.walk( DOCBASE , topdown=True):
+ dirs.sort()
+ files.sort()
+ #output += str(root)
+ #output += str(dirs)
+ #output += str(files)
+
+ print(' --%s' % root, end=", ")
+
+ myroot = root[root_len:]
+ parts = myroot.split("\/")
+ path_len = "" # " " * len(parts[:-1])
+ path_last = parts[-1]
+ (fi,di,si) = overview_folder(root) # num files, num dirs, size
+
+ if si < 1024:
+ si = "%s b" % si
+ elif si < 1024 * 1024:
+ si = "%s K" % int( si / 1024.0 )
+ elif si < (1024 * 1024 * 1024):
+ si = "%s M" % int( si/(1024*1024.0) )
+ else:
+ si = "%s G" % int( si/(1024*1024*1024.0) )
+
+ default_cmd = most_common(files)
+ if not default_cmd: default_cmd = "NO"
+
+
+
+ this_line = "%s\n\t%s \tDepth: %i \t Subdirs: %i \t # Files: %i \t # Total size: %s \n" % \
+ ( path_len + cleantext(path_last), default_cmd, depth(root), len(dirs), fi, si)
+
+ mydepth = depth(root) + 1
+ if mydepth > 22: mydepth = 22
+
+ output += folder_line % (mydepth, 24-mydepth, this_line)
+
+ #moveit(files, root, default_cmd)
+ try:
+ #output += tag("p", "%i total files, in %i total folders, for %s" % (fi,di,si))
+ for F in sorted(files, \
+ key = lambda x: (os.path.splitext(x))[1]+(os.path.split(x))[1] ):
+ output += file_line % (mydepth, 24-mydepth, cleantext(F))
+ except Exception as e:
+ print("**** Some sort of error with %s" % files, end=" ")
+ print(e)
+
+ output += tag("p","All done with files.")
+ return output
+
+
+if __name__ == '__main__':
+
+
+ from queue import Queue
+ from bs4 import BeautifulSoup as bs
+ from concurrent.futures import ThreadPoolExecutor
+ import requests, os, json, shutil, time
+ import requests
+ import requests.auth
+ #import praw
+
+
+
+ q = Queue()
+
+
+ def start_tt2():
+ client_auth = requests.auth.HTTPBasicAuth('DDsmF856ZAookA', 'Cles0wAwm9o4N_jnPNXOwgH2-DJXKw')
+ post_data = {"grant_type": "password", "username": "ssttmm2323", "password": "ssttmm2323"}
+ headers = {"User-Agent": "Totally Chrome"}
+ response = requests.post("https://www.reddit.com/api/v1/access_token", auth=client_auth, data=post_data, headers=headers)
+ print(response.json())
+
+
+ headers = {"Authorization": "bearer fhTdafZI-0ClEzzYORfBSCR7x3M", "User-Agent": "Totally Chrome"}
+ response = requests.get("https://oauth.reddit.com/api/v1/me", headers=headers)
+ print(response.json())
+
+ def start_tt3():
+ reddit = praw.Reddit( client_id="DDsmF856ZAookA",
+ client_secret="Cles0wAwm9o4N_jnPNXOwgH2-DJXKw",
+ password="ssttmm2323",
+ user_agent="Totally Chrome",
+ username="ssttmm2323",)
+
+
+ # start_tt3()