server.py (84954B)
1 #!/usr/bin/python3 2 # -*- coding: utf-8 -*- 3 #heartranked is FREEDOM SOFTWARE by King Robin Johannes 4 #don't worry, information want's to be free. nobody can stop it. Bless. 5 6 import time, datetime, os, sys 7 8 file_dir = os.path.dirname(__file__) 9 sys.path.append(file_dir) 10 11 import json 12 import requests 13 import subprocess 14 import web 15 import hashlib 16 import random 17 import settings 18 import markdown 19 import re 20 import unicodedata 21 import urllib 22 from pathlib import Path 23 from mutagen.flac import FLAC 24 from mutagen.easyid3 import EasyID3 25 from mutagen.oggvorbis import OggVorbis 26 from PIL import Image 27 from PIL import ImageSequence 28 import settings 29 30 urls = ( 31 '/?','heartranked', 32 '/trust', 'trust', 33 "/stats?", "stats", 34 "/login?", "login", 35 "/logout", "logout", 36 "/invites?", "invites", 37 "/users","users", 38 "/like?","like", 39 "/imageapi?","imageapi", 40 "/u/(.*)?", "user", 41 "/forgotpass?", "forgotpass", 42 "/register?", "register", 43 "/tuning?", "tuning", 44 '/editor?', 'editor', 45 '/save', 'savepost', 46 '/upload', 'upload', 47 '/rendered', 'rendered', 48 '/uploads?', 'uploads', 49 '/pull?', 'pull', 50 '/config', 'config') 51 52 #Load from settings 53 webmaster = settings.webmaster 54 baseurl = settings.baseurl 55 sitename = settings.sitename 56 siteslogan = settings.siteslogan 57 siteurl = baseurl 58 postadmin = settings.postadmin 59 postadmin_signature = settings.postadmin_signature 60 heart=settings.heart 61 hearted=settings.hearted 62 zipandship=settings.zipandship 63 #load msg 64 postadmin_msg_new=settings.postadmin_msg_new 65 postadmin_msg_new_long=settings.postadmin_msg_new_long 66 welcome=settings.welcome 67 welcome_long=settings.welcome_long 68 basedir = os.path.dirname(os.path.realpath(__file__))+'/' 69 templatedir = basedir + 'html/' 70 staticdir = basedir + 'static/' 71 web.config.debug = False 72 app = web.application(urls, globals()) 73 store = web.session.DiskStore(basedir + 'sessions') 74 render = web.template.render(templatedir, base="base") 75 renderop = web.template.render(templatedir, base="op") 76 rendersplash = web.template.render(templatedir, base="splash") 77 session = web.session.Session(app,store,initializer={'login':0, 'privilege':0, 'bag':[], 'sessionkey':'empty','postid':'','backurl':'','user':'','search':'', 'bildsida':'', 'feedbase':'', 'timebase':'', 'usrfeed':''}) 78 79 allowedchar = 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z' 80 81 # --- Parameters (often called N, r, p) --- 82 # N (n): CPU/Memory cost factor. Must be a power of 2 (e.g., 2**14 = 16384). 83 # r: Block size factor (typically 8). 84 # p: Parallelization factor (typically 1). 85 # klen: Desired key length (e.g., 32 for a 256-bit key). 86 N_COST = 16384 # Adjust this for desired security/speed tradeoff 87 R_BLOCK_SIZE = 8 88 P_PARALLELIZATION = 1 89 KEY_LENGTH = 32 90 91 def runfirst(): 92 os.makedirs(basedir+'p/posts',exist_ok=True) 93 os.makedirs(basedir+'p/zipped',exist_ok=True) 94 os.makedirs(basedir+'p/comborank',exist_ok=True) 95 os.makedirs(basedir+'p/heartrank',exist_ok=True) 96 os.makedirs(basedir+'p/deleted',exist_ok=True) 97 os.makedirs(basedir+'u/',exist_ok=True) 98 os.makedirs(basedir+'r/',exist_ok=True) 99 os.makedirs(basedir+'r/visitors',exist_ok=True) 100 os.makedirs(basedir+'r/invites',exist_ok=True) 101 os.makedirs(basedir+'r/trusted',exist_ok=True) 102 os.makedirs(basedir+'r/users',exist_ok=True) 103 os.makedirs(basedir+'r/stopflood',exist_ok=True) 104 os.makedirs(basedir+'r/stopresetpass',exist_ok=True) 105 106 runfirst() 107 108 def hash_password(password: str) -> bytes: 109 # 1. Generate a random, unique salt for each password 110 salt = os.urandom(16) 111 # 2. Derive the key (hash) 112 # The 'password' must be bytes, so we use .encode('utf-8') 113 key = hashlib.scrypt( 114 password.encode('utf-8'), 115 salt=salt, 116 n=N_COST, 117 r=R_BLOCK_SIZE, 118 p=P_PARALLELIZATION, 119 dklen=KEY_LENGTH 120 ) 121 # 3. Store the salt AND the key (hash) together 122 return salt + key 123 124 def verify_password(stored_hash_with_salt: bytes, provided_password: str) -> bool: 125 # 1. Separate the salt and the stored key 126 salt = stored_hash_with_salt[:16] # Assuming 16 bytes for the salt 127 stored_key = stored_hash_with_salt[16:] 128 # 2. Re-derive the key from the provided password using the stored salt and parameters 129 # **Crucially, use the exact same n, r, p, and dklen parameters!** 130 try: 131 derived_key = hashlib.scrypt( 132 provided_password.encode('utf-8'), 133 salt=salt, 134 n=N_COST, 135 r=R_BLOCK_SIZE, 136 p=P_PARALLELIZATION, 137 dklen=KEY_LENGTH 138 ) 139 # 3. Compare the newly derived key with the stored key 140 # Use a constant-time comparison (like `hmac.compare_digest` if available, 141 # but Python's standard byte comparison is often okay here too) 142 return derived_key == stored_key 143 except ValueError: 144 # This can happen if parameters like dklen are wrong during verification 145 return False 146 147 def logged(): 148 if session.login > 0: 149 return True 150 else: 151 return False 152 153 class createpost: 154 def __init__(self, name, value): 155 self.name = name 156 self.value = value 157 158 def savetext(thename, description): 159 with open(basedir+thename, "w") as f: 160 f.write(description) 161 162 def loadtext(thename): 163 if os.path.exists(basedir+thename): 164 with open(basedir+thename, 'r') as f: 165 description = f.read() 166 return description 167 return '' 168 169 def savejson(name, thedict): 170 #full path to filename 171 #save to next line make an ID automatically, you have to check the save to know how to load it. 172 #loadfile check if record exist, first in line is the record hash and update if it exists 173 #make users in a folder as files first is username username will always be user record 174 #hearts will be files as usernames with timestamp in a hearts folder in post folder. be stored and checked in u/hearts and post/hearts 175 # be sure to add home domain setting to username 176 #make save list a dict use same names 177 print(thedict) 178 print('this to be saved') 179 thename=basedir+name 180 if os.path.exists(thename): 181 with open(thename, 'r') as f: 182 p=json.load(f) 183 print(p) 184 print('this to be saved to') 185 p.update(thedict) 186 with open(thename, "w") as f: 187 json.dump(p,f) 188 else: 189 with open(thename, "w") as f: 190 #f.write(str(i) + ',') 191 print('saving json') 192 json.dump(thedict,f) 193 194 def loadjson(name): 195 settings = '' 196 thename=basedir+name 197 if os.path.exists(thename): 198 with open(thename, 'r') as f: 199 settings = json.load(f) 200 #for key, i in settings.items(): 201 # createpost(key,i) 202 return settings 203 204 def deletepost(thefile): 205 os.system('rm '+thefile) 206 207 def adduser(name, password, mail): 208 originalname=name 209 name=safe_filename(name[:12]) 210 password_hashed=hash_password(password).hex() 211 tot = len(os.listdir(basedir+'r/users/')) 212 #print(password_hashed) 213 print('users alltsomallt: ' + str(tot)) 214 if tot > 1: 215 adminlevel=3 216 else: 217 adminlevel=5 218 thedict={'name':name, 'displayname':originalname, 'password':password_hashed,'mail':mail,'adminlevel':adminlevel} 219 savejson('r/users/'+name, thedict) 220 #savetext('r/user/'+name,password_hashed) 221 print("new user added") 222 return 223 224 def updateuser(displayname, password, mail): 225 password_hashed=hash_password(password).hex() 226 tot = len(os.listdir(basedir+'r/users/')) 227 thedict={'displayname':displayname, 'password':password_hashed,'mail':mail} 228 savejson('r/users/'+session.user, thedict) 229 print("user info updated") 230 return 231 232 def safe_filename(name: str, max_length: int = 100, replacement: str = "-") -> str: 233 """ 234 Convert a filename into a web-safe version. 235 """ 236 if not name: 237 return "file" 238 #Normalize unicode (é → e, etc.) 239 name = unicodedata.normalize('NFKD', name) 240 name = name.encode('ascii', 'ignore').decode('ascii') 241 #Replace spaces and common separators with the replacement char 242 name = re.sub(r'[\s_]+', replacement, name) 243 #Remove any character that is not alphanumeric, hyphen, underscore, or dot 244 name = re.sub(r'[^a-zA-Z0-9.\-_]', '', name) 245 #Replace multiple replacement chars with single one 246 name = re.sub(re.escape(replacement) + r'+', replacement, name) 247 #Remove leading/trailing replacement chars and dots 248 name = name.strip(replacement + '.') 249 #Prevent empty or hidden files 250 if not name or name.startswith('.'): 251 name = "file" + name 252 #Enforce max length (leave room for extension) 253 if len(name) > max_length: 254 name = name[:max_length] 255 return name.lower() 256 257 def adminlevel(user): 258 #level = db.query("SELECT adminlevel FROM rymdadmin WHERE name='"+user+"';")[0] 259 level=loadjson('r/users/'+user) 260 #1 session logout, web.py bug 261 #2 rights to see pics and comment 262 #3 rights to upoload 263 #5 superadmin 264 session.login = int(level['adminlevel']) 265 return 266 267 def stopresetpass(mail): 268 t = None 269 if os.path.exists(basedir+'r/stopresetpass/'+mail) == True: 270 t=loadjson('r/stopresetpass/'+mail) 271 else: 272 thedict={'timeadded':time.time()} 273 savejson('r/stopresetpass/'+mail, thedict) 274 return 275 thedict={'timeadded':time.time()} 276 savejson('r/stopresetpass/'+mail, thedict) 277 latest = time.time() - t['timeadded'] 278 print(latest) 279 if latest < 600: 280 print('mail is in password reset spam filter') 281 return True 282 else: 283 return False 284 285 def stopflood(ip,referer): 286 t = 0 287 if os.path.exists(basedir+'r/stopflood/'+ip) == True: 288 t=loadjson('r/stopflood/'+ip) 289 else: 290 thedict={'timeadded':time.time()} 291 savejson('r/stopflood/'+ip, thedict) 292 return 293 thedict={'timeadded':time.time()} 294 savejson('r/stopflood/'+ip, thedict) 295 try: 296 latest = time.time() - t 297 print(latest) 298 except: 299 latest = 10 300 pass 301 if latest < 1: 302 print('flooding recognized!') 303 return True 304 else: 305 return False 306 307 def getinvitation(secretinvitation): 308 os.makedirs(basedir+'r/invites/'+session.user+'/',exist_ok=True) 309 invite=loadjson('r/invites/'+session.user+'/'+secretinvitation) 310 print(invite) 311 if invite['secretinvitekey'] == secretinvitation: 312 return True 313 return False 314 315 class trust(): 316 form = web.form.Form( 317 web.form.Textbox('servername', web.form.notnull, description="server:"), 318 web.form.Textbox('port', description="port:"), 319 web.form.Textbox('user', web.form.notnull, description="user:"), 320 web.form.Password('password', web.form.notnull, description="passcode:"), 321 web.form.Button('Trust')) 322 def GET(self): 323 os.makedirs(basedir+'r/trusted/'+session.user+'/',exist_ok=True) 324 i = web.input(remove=None) 325 if i.remove != None: 326 os.system('rm '+basedir+'r/trusted/'+session.user+'/'+i.remove) 327 return web.seeother('/trust') 328 trusted=os.listdir(basedir+'r/trusted/'+session.user+'/') 329 trustedlist=[] 330 for t in trusted: 331 trusted=loadjson('r/trusted/'+session.user+'/'+t) 332 trustedlist.append(trusted) 333 trustform = self.form() 334 return render.trust(trustform, trustedlist) 335 def POST(self): 336 i = web.input(port='') 337 referer = web.ctx.env.get('HTTP_REFERER',baseurl) 338 ip = web.ctx['ip'] 339 stopflood(ip, referer) 340 loginform = self.form() 341 i = web.input() 342 #password_hashed=hash_password(i.password).hex() 343 thedict={'servername':i.servername, 'port':i.port, 'user':i.user, 'password':i.password} 344 savejson('r/trusted/'+session.user+'/'+i.servername,thedict) 345 return web.seeother('/trust') 346 347 class login(): 348 form = web.form.Form( 349 web.form.Textbox('user', web.form.notnull, description="your registered mail account:"), 350 web.form.Password('password', web.form.notnull, description="and your passcode please:"), 351 web.form.Button('Login')) 352 def GET(self): 353 users=os.listdir(basedir+'r/users/') 354 if len(users) == 0: 355 result = subprocess.run(['whoami'], capture_output=True, text=True) 356 adduser('op', 'blessyou', result.stdout.rstrip()+'@localhost') 357 visitorlog() 358 fejl = '' 359 resetpasslink = False 360 i = web.input(error=None) 361 if i.error == 'fejl': 362 fejl = 'wrong passcode!' 363 resetpasslink = True 364 if i.error == 'tom': 365 fejl = 'didnt work' 366 if session.login < 3: 367 loginform = self.form() 368 return render.login(loginform, fejl, resetpasslink) 369 if session.login == 3: 370 return web.seeother('/') 371 if session.login == 5: 372 raise web.seeother('/') 373 def POST(self): 374 visitorlog() 375 referer = web.ctx.env.get('HTTP_REFERER',baseurl) 376 ip = web.ctx['ip'] 377 stopflood(ip, referer) 378 loginform = self.form() 379 i = web.input() 380 if i.user == '' or i.password == '': 381 raise web.seeother('/login?error=tom') 382 rymdadmins = [] 383 users = os.listdir(basedir+'r/users/') 384 for r in users: 385 admin=loadjson('r/users/'+r) 386 rymdadmins.append(admin) 387 #if not rymdadmins: 388 # raise web.seeother('/register') 389 for p in rymdadmins: 390 if p['name'].lower() == i['user'].lower() or p['mail'].lower() == i['user'].lower(): 391 passcode=bytes.fromhex(p['password']) 392 if verify_password(passcode,i.password): 393 session.user = p['name'] 394 adminlevel(p['name']) 395 if session.login == 5: 396 print('ACCESS!') 397 raise web.seeother('/') 398 if session.backurl != '': 399 backurl = session.backurl 400 session.backurl = '' 401 raise web.seeother(backurl) 402 else: 403 raise web.seeother('/') 404 return web.seeother('/login?error=fejl') 405 406 class register(): 407 form = web.form.Form( 408 web.form.Textbox('invite', description="invitation code (do not edit):"), 409 web.form.Textbox('user', description="name:"), 410 web.form.Password('password', description="passcode:"), 411 web.form.Textbox('mail', description="mail:"), 412 web.form.Button('JOIN')) 413 def GET(self): 414 visitorlog() 415 registerform = self.form() 416 w = web.input(invite=None) 417 formfail = '' 418 n = '' 419 m = '' 420 if getinvitation(w.invite): 421 try: 422 if w.fail == 'namn': 423 formfail = 'hey, need a name. If ya don lik ya real name imagination buddy' 424 if w.namn: 425 n = w.namn 426 if w.epost: 427 m = w.epost 428 elif w.epost == '': 429 formfail = 'we need an email, if u loose your passcode for example...' 430 if w.fail == 'notmail': 431 formfail = 'uhm, this is not an email' 432 elif w.fail == 'nametaken': 433 formfail = 'Name already taken' 434 elif w.fail == 'mailtaken': 435 formfail = 'You already got a account on this email. Try reset your passcode' 436 elif w.fail == 'kortlosen': 437 formfail = 'Too shoort passcode. Min 5 char.' 438 except: 439 pass 440 totusers = len(os.listdir(basedir+'r/users/')) 441 registerform.fill(user=urllib.parse.unquote_plus(n), mail=urllib.parse.unquote_plus(m), invite=w.invite) 442 return render.register(registerform, formfail, totusers) 443 else: 444 return web.seeother('/oopsie') 445 def POST(self): 446 visitorlog() 447 registerform = self.form() 448 i = web.input(invite=None) 449 if getinvitation(i.invite): 450 r = '&namn=' + i.user + '&epost=' + i.mail 451 urllib.parse.quote_plus(r) 452 if i.user == '': 453 raise web.seeother('/register?invite='+i.invite+'&fail=namn'+r) 454 if '@' not in i.mail: 455 raise web.seeother('/register?invite='+i.invite+'&fail=notmail'+r) 456 if len(i.password) < 5: 457 raise web.seeother('/register?invite='+i.invite+'&fail=kortlosen'+r) 458 #rymdadmins = db.select('rymdadmin', what='name, mail') 459 rymdadmins = [] 460 users = os.listdir(basedir+'r/users/') 461 for r in users: 462 admin=loadjson('r/users/'+r) 463 rymdadmins.append(admin) 464 for p in rymdadmins: 465 if p['name'].lower() == i.user.lower(): 466 raise web.seeother('/register?invite='+i.invite+'&fail=nametaken' +r) 467 if p['mail'].lower() == i.mail.lower(): 468 raise web.seeother('/register?invite='+i.invite+'&fail=mailtaken' +r) 469 adduser(i.user, i.password, i.mail.lower()) 470 #Send mail (change messages in settings.py) 471 sendmail(postadmin, postadmin_msg_new, postadmin_msg_new_long+i.user+' '+i.mail) 472 #Send mail to new user 473 sendmail(i.mail, welcome, welcome_long+i.user) 474 #session.login = 3 475 #session.user = safe_filename(i.user) 476 #add user to matrix 477 #os.system("register_new_matrix_user -u "+i.user+" -p "+i.password+" --no-admin -c /etc/matrix-synapse/homeserver.yaml") 478 #db.update('invites', where='secretinvitation="'+i.invite+'"', accepted=formattime(datetime.datetime.now())) 479 return web.seeother('/login') 480 else: 481 raise web.seeother('/oopsie') 482 483 class welcome(): 484 def GET(self): 485 if session.login > 2: 486 backurl = '' 487 if session.backurl != '': 488 backurl = session.backurl 489 session.backurl = '' 490 return render.ny(session.user, backurl) 491 492 class like: 493 def POST(self): 494 if session.user != '': 495 i = web.input(unlike=None, like=None, hate=None, unhate=None, user=None, imghash=None) 496 user = i.user 497 postid = i.imghash 498 mail='' 499 displayname='' 500 rymdadmins=[] 501 users = os.listdir(basedir+'r/users/') 502 for r in users: 503 admin=loadjson('r/users/'+r) 504 rymdadmins.append(admin) 505 for r in rymdadmins: 506 if r['name']==session.user: 507 if 'displayname' in r: 508 displayname=r['displayname'] 509 if 'mail' in r: 510 mail=r['mail'] 511 #l = db.query("SELECT * FROM likes WHERE bild='"+postid+"' AND user='"+session.user+"';") 512 os.makedirs(basedir+'p/posts/'+postid+'/hearts/',exist_ok=True) 513 os.makedirs(basedir+'p/heartrank/',exist_ok=True) 514 os.makedirs(basedir+'u/'+session.user+'/posts/'+postid+'/hearts/',exist_ok=True) 515 try: 516 l = loadjson('p/posts/'+postid+'/hearts/'+session.user) 517 except: 518 l={} 519 print(session.user) 520 if l: 521 user_likes = True 522 else: 523 user_likes = False 524 if user_likes == False: 525 #db.insert('likes', user=session.user, bild=postid, datum=formattime(datetime.datetime.now())) 526 try: 527 l=len(os.listdir(basedir+'p/posts/'+postid+'/hearts/')) 528 except: 529 l=0 530 l=l+1 531 thedict={'hearts':l,'name':session.user,'mail':mail,'displayname':displayname,'timeadded':formattime(datetime.datetime.now())} 532 savejson('p/posts/'+postid+'/meta',thedict) 533 os.system('rm '+basedir+'p/heartrank/'+postid+'-'+str(int(l-1)).zfill(16)) 534 os.system('cp '+basedir+'p/posts/'+postid+'/meta '+basedir+'p/heartrank/'+postid+'-'+str(int(l)).zfill(16)) 535 savejson('p/posts/'+postid+'/hearts/'+session.user, thedict) 536 savejson('u/'+session.user+'/posts/'+postid+'/hearts/'+session.user, thedict) 537 user_likes = True 538 elif user_likes == True: 539 try: 540 l=len(os.listdir(basedir+'p/posts/'+postid+'/hearts/')) 541 except: 542 l=0 543 if l > 0: 544 l=l-1 545 thedict={'hearts':l,'name':session.user,'mail':mail,'displayname':displayname,'timeadded':formattime(datetime.datetime.now())} 546 savejson('p/posts/'+postid+'/meta',thedict) 547 deletepost(basedir+'u/'+session.user+'/posts/'+postid+'/hearts/'+session.user) 548 deletepost(basedir+'p/posts/'+postid+'/hearts/'+session.user) 549 os.system('rm '+basedir+'p/heartrank/'+postid+'-'+str(int(l+1)).zfill(16)) 550 os.system('cp '+basedir+'p/posts/'+postid+'/meta '+basedir+'p/heartrank/'+postid+'-'+str(int(l)).zfill(16)) 551 user_likes = False 552 likes = len(os.listdir(basedir+'p/posts/'+postid+'/hearts/')) 553 # Example: Update like count in your database 554 # This is a placeholder; replace with your database logic 555 # Return JSON response 556 web.header('Content-Type', 'application/json') 557 return json.dumps({'likes': likes, 'user_likes': user_likes ,'heart':heart,'hearted':hearted}) 558 559 class user(): 560 def GET(self, user): 561 data = web.input(soundname=None, onair=None, public=None, showuploads=None) 562 if user == session.user: 563 if data.public and data.soundname: 564 public=data.public 565 thedict={'timeadded':formattime(datetime.datetime.now())} 566 savejson('p/posts/'+postid+'/meta') 567 elif data.showuploads=='yes': 568 uploads = [] 569 uploads = get_files_by_time('public_html/u/' + user + '/images/web/',newest_first=True) 570 return render.showuploads(uploads,user,allowedchar, random) 571 elif data.onair and data.soundname: 572 onair={"onair":data.onair} 573 savejson('p/posts/'+postid+'/meta') 574 #soundname='aurora_ruderalis-greatful_bread' 575 #filetype='flac' 576 #postid = hashlib.md5(str(random.getrandbits(256)).encode('utf-8')).hexdigest() 577 usersounds=loadjson('r/users/'+user) 578 #sounds=os.listdir(basedir+'p/posts/') 579 creditsounds = [] 580 #for i in sounds: 581 # creditsounds.append(loadjson('p/posts/'+i)) 582 #for i in creditsound: 583 # try: 584 # credits=i['musicians'].split(',') 585 # except: 586 # credits='' 587 # for u in credits: 588 # if u.strip().lower() == user.strip().lower(): 589 # creditsounds.append(i.title) 590 return render.user(usersounds,creditsounds,user,datetime,str,int) 591 return web.seeother('/login') 592 593 class invites(): 594 form = web.form.Form( 595 web.form.Textbox('mail', description="epost:"), 596 web.form.Button('Skicka')) 597 def GET(self): 598 if session.login > 2: 599 os.makedirs(basedir+'r/invites/'+session.user+'/',exist_ok=True) 600 invites=[] 601 v = get_files_by_time('r/invites/'+session.user,newest_first=True) 602 if v: 603 for i in v: 604 invite=loadjson('r/invites/'+session.user+'/'+i) 605 invites.append(invite) 606 tuningform = self.form() 607 w = web.input(epost=None, render=None) 608 formfail = '' 609 if w.epost == '': 610 formfail = formfail + 'you have to put your email in' 611 if w.render == 'yes': 612 secretinvitekey = hashlib.md5(str(random.getrandbits(256)).encode('utf-8')).hexdigest() 613 thedict={"secretinvitekey":secretinvitekey,"timeadded":formattime(datetime.datetime.now()),"creator":session.user, "accepted":''} 614 savejson('r/invites/'+session.user+'/'+secretinvitekey, thedict) 615 raise web.seeother('/invites') 616 return render.invites(tuningform, formfail, session.user, invites) 617 def POST(self): 618 if session.login > 2: 619 user = loadjson('r/users/'+session.user) 620 tuningform = self.form() 621 i = web.input() 622 if i.mail == '': 623 raise web.seeother('/invites?fail=nomail') 624 if '@' not in i.mail: 625 raise web.seeother('/tuning?fail=notmail') 626 secretinvitekey = hashlib.md5(str(random.getrandbits(256)).encode('utf-8')).hexdigest() 627 thedict={"secretinvitekey":secretinvitekey,"timeadded":formattime(datetime.datetime.now()),"creator":session.user, "accepted":''} 628 savejson('r/invites/'+session.user+'/'+secretinvitekey, thedict) 629 msg = "YO! You are the One! " + user.name + " is your Morpheous. Follow this rabbit https://robinbackman.com/register?invite="+secretinvitekey 630 sendmail(i.mail, 'Invitation to HEART RANKED!', msg) 631 return web.seeother('/') 632 633 class tuning(): 634 form = web.form.Form( 635 web.form.Textbox('user', description="synligt namn:"), 636 web.form.Password('password', description="lösenord:"), 637 web.form.Password('newpassword', description="nytt lösen (ifall du vill byta):"), 638 web.form.Password('newpassword2', description="nytt lösen igen:"), 639 web.form.Textbox('mail', description="epost:"), 640 web.form.Button('Spara')) 641 def GET(self): 642 if session.login > 2: 643 user = loadjson('r/users/'+session.user) 644 tuningform = self.form() 645 w = web.input(namn=None,epost=None,fail=None,upd=None) 646 formfail = '' 647 if w.fail == 'wrongpass': 648 formfail = formfail + 'wrong passcode' 649 if w.fail == 'nopass': 650 formfail = formfail + 'you have to write passcode' 651 if w.namn == '': 652 formfail = formfail + 'whats your displayname' 653 if w.epost == '': 654 formfail = formfail + 'write your email please' 655 elif w.fail == 'notmail': 656 formfail = formfail + 'email please' 657 if w.fail == 'nametaken': 658 formfail = formfail + 'name is taken! choose another' 659 if w.fail == 'mailtaken': 660 formfail = formfail + 'mail address taken' 661 if w.fail == 'kortlosen': 662 formfail = formfail + '5 characters minimum' 663 if w.fail == 'newpass': 664 formfail = formfail + 'new passcode doesnt match' 665 if w.upd == 'yes': 666 formfail = 'Yes, your account has been tuned in thanks!' 667 tuningform.fill(user=user['displayname'], mail=user['mail']) 668 return render.tuning(tuningform, formfail, user['name']) 669 else: 670 return web.seeother('/register') 671 def POST(self): 672 if session.login > 2: 673 tuningform = self.form() 674 i = web.input() 675 if i.password == '': 676 raise web.seeother('/tuning?fail=nopass') 677 rymdadmins = [] 678 users = os.listdir(basedir+'r/users/') 679 for r in users: 680 admin=loadjson('r/users/'+r) 681 rymdadmins.append(admin) 682 for p in rymdadmins: 683 print(p) 684 if p['name'] == session.user: 685 passcode=bytes.fromhex(p['password']) 686 if verify_password(passcode,i.password): 687 #check if display name taken 688 for a in rymdadmins: 689 if i.user in a['displayname'] and a['name'] != session.user: 690 raise web.seeother('/tuning?fail=nametaken') 691 if i.mail in a['mail'] and i.mail != p['mail']: 692 raise web.seeother('/tuning?fail=mailtaken') 693 if i.newpassword != '': 694 if i.newpassword != i.newpassword2: 695 raise web.seeother('/tuning?fail=newpass') 696 if len(i.newpassword) < 5: 697 raise web.seeother('/tuning?fail=kortlosen') 698 else: 699 #update with password change 700 updateuser(i.user,i.newpassword,i.mail) 701 return web.seeother('/tuning?upd=yes') 702 if '@' not in i.mail: 703 raise web.seeother('/tuning?fail=notmail') 704 #update without passwordchange 705 thedict={'displayname':i.user,'mail':i.mail.lower()} 706 savejson('r/users/'+session.user, thedict) 707 return web.seeother('/tuning?upd=yes') 708 else: 709 raise web.seeother('/tuning?fail=wrongpass') 710 711 class forgotpass(): 712 form = web.form.Form( 713 web.form.Textbox('mail', web.form.notnull, description="email:"), 714 web.form.Button('Send me new passcode')) 715 def GET(self): 716 fejl = '' 717 i = web.input(error=None) 718 if i.error == 'fejl': 719 fejl = 'no email like that sorry!' 720 elif i.error == 'done': 721 fejl = 'your passcode is updated and sent to your mail' 722 elif i.error == 'nej': 723 fejl = 'nope, dont worky' 724 elif i.error == 'stopresetpass': 725 fejl = 'Already sent passcode to mail' 726 if session.login < 3: 727 loginform = self.form() 728 return render.forgotpass(loginform, fejl) 729 if session.login == 3: 730 return web.seeother('/s') 731 if session.login == 5: 732 raise web.seeother('/s') 733 def POST(self): 734 referer = web.ctx.env.get('HTTP_REFERER',baseurl) 735 ip = web.ctx['ip'] 736 stopflood(ip, referer) 737 sendpassform = self.form() 738 if not sendpassform.validates(): 739 raise web.seeother('/forgotpass?error=fejl') 740 else: 741 i = web.input() 742 if '@' not in i.mail: 743 raise web.seeother('/forgotpass?error=fejl') 744 rymdadmins = [] 745 users = os.listdir(basedir+'r/users/') 746 for r in users: 747 admin=loadjson('r/users/'+r) 748 rymdadmins.append(admin) 749 for p in rymdadmins: 750 if p['mail'].lower() == i.mail.lower(): 751 passfilter = stopresetpass(i.mail.lower()) 752 if passfilter == True: 753 raise web.seeother('/forgotpass?error=stopresetpass') 754 unencrypted_password = ('%06x' % random.randrange(16**6)) 755 password = unencrypted_password.encode("utf-8") 756 password_hashed=hash_password(password).hex() 757 thedict={'password':password_hashed} 758 savejson('r/users/'+session.user, thedict) 759 print("lösenordet uppdaterat!") 760 msg = "Your new passcode is: " + unencrypted_password 761 sendmail(p.mail, 'Heart Ranked Passcode', msg) 762 raise web.seeother('/forgotpass?error=done') 763 raise web.seeother('/forgotpass?error=fejl') 764 765 def sendmail(email, subject, msg): 766 #Send mail 767 try: 768 echomsg = subprocess.Popen(('echo', msg+'\n'+postadmin_signature), stdout=subprocess.PIPE) 769 sendmsg = subprocess.check_output(('mail', '-r', postadmin, '-s', subject, email), stdin=echomsg.stdout) 770 echomsg.wait() 771 except: 772 print('no mail server found') 773 #subprocess.call(['echo', msg, '|', 'mail', '-r', postadmin,'-s', subject, email]) 774 775 def get_new_frames(gif, scale): 776 new_frames = [] 777 actual_frames = gif.n_frames 778 for frame in range(actual_frames): 779 gif.seek(frame) 780 new_frame = Image.new('RGBA', gif.size) 781 new_frame.paste(gif) 782 new_frame.thumbnail(scale, Image.Resampling.LANCZOS) 783 new_frames.append(new_frame) 784 return new_frames 785 786 def save_new_gif(new_frames, old_gif_information, new_path): 787 new_frames[0].save(new_path, 788 save_all = True, 789 append_images = new_frames[1:], 790 duration = old_gif_information['duration'], 791 loop = old_gif_information['loop'], 792 background = old_gif_information['background'], 793 extension = old_gif_information['extension'] , 794 transparency = old_gif_information['transparency']) 795 796 def resize_gif(input_path, output_path, max_size): 797 input_image = Image.open(input_path) 798 frames = list(_thumbnail_frames(input_image,max_size)) 799 output_image = frames[0] 800 output_image.save( 801 output_path, 802 save_all=True, 803 append_images=frames[1:], 804 disposal=input_image.disposal_method, 805 **input_image.info, 806 ) 807 808 def _thumbnail_frames(image,max_size): 809 for frame in ImageSequence.Iterator(image): 810 new_frame = frame.copy() 811 new_frame.thumbnail(max_size, Image.Resampling.LANCZOS) 812 yield new_frame 813 814 def scale_gif(path, scale, new_path=None): 815 gif = Image.open(path) 816 if not new_path: 817 new_path = path 818 old_gif_information = { 819 'loop': bool(gif.info.get('loop', 1)), 820 'duration': gif.info.get('duration', 40), 821 'background': gif.info.get('background', 223), 822 'extension': gif.info.get('extension', (b'NETSCAPE2.0')), 823 'transparency': gif.info.get('transparency', 223) 824 } 825 new_frames = get_new_frames(gif, scale) 826 save_new_gif(new_frames, old_gif_information, new_path) 827 828 829 def getdisplayname(user): 830 try: 831 displayname = loadjson('r/users/'+session.user) 832 displayname = displayname.displayname 833 except: 834 displayname = user 835 return displayname 836 837 #-------------Get files and sort em by date modified--------------- 838 839 def get_dirs_by_time(path, reverse=False): 840 """Return names of immediate subdirectories, sorted by modification time.""" 841 path=basedir+path 842 with os.scandir(path) as it: 843 return sorted( 844 (e.name for e in it if e.is_dir(follow_symlinks=False)), 845 key=lambda name: os.stat(os.path.join(path, name)).st_mtime, 846 reverse=reverse, 847 ) 848 849 def get_files_by_time(directory: str = ".", newest_first: bool = True): 850 path = Path(basedir+directory) 851 #Get all files (exclude directories and hidden files if you want) 852 files = [f for f in path.iterdir() if f.is_file()] 853 #Sort by modification time 854 sorted_files = sorted( 855 files, 856 key=lambda f: f.stat().st_mtime, # last modified timestamp 857 reverse=newest_first 858 ) 859 # Return just the file names (as strings) 860 return [f.name for f in sorted_files] 861 862 def getfiles(filmfolder): 863 #get a list of films, in order of settings.p file last modified 864 films_sorted = [] 865 films = next(os.walk(filmfolder))[1] 866 for i in films: 867 uploaded = os.listdir(filmfolder + i + '/') 868 for f in uploaded: 869 if os.path.isfile(filmfolder + i + '/'+f) == True: 870 #DUUUUUUDE HERE CHECK IF OLDER THAN 871 lastupdate = os.path.getmtime(filmfolder + i + '/' + f) 872 films_sorted.append((i,f,lastupdate)) 873 else: 874 films_sorted.append((i,f,0)) 875 films_sorted = sorted(films_sorted, key=lambda tup: tup[2], reverse=True) 876 return films_sorted 877 878 def callsubprocess(cmd): 879 subprocess.call(cmd.split()) 880 881 def visitorlog(): 882 ip = web.ctx['ip'] 883 referer = web.ctx.env.get('HTTP_REFERER', 'none') 884 environ = web.ctx.env.get('HTTP_USER_AGENT', 'dunno') 885 last = get_files_by_time('r/visitors/',newest_first=True) 886 stopflood(ip, referer) 887 country='local loco' 888 countrycode='fi' 889 if last: 890 lastip=loadjson('r/visitors/'+last[0]) 891 else: 892 lastip='' 893 try: 894 country = '' 895 country = os.popen('geoiplookup '+ip).read() 896 print(country) 897 if country != '': 898 countrycode = country.split(':')[1].split(',')[0].lower().strip() 899 country = country.split(':')[1].split(',')[1].strip() 900 else: 901 country='none' 902 except: 903 country='local loco' 904 countrycode='fi' 905 thedict={'ip':ip,'referer':referer,'environ':environ,'country':country,'countrycode':countrycode,'time':formattime(datetime.datetime.now())} 906 savejson('r/visitors/'+ip,thedict) 907 print("added to visitor log") 908 if lastip != ip: 909 try: 910 country = '' 911 country = os.popen('geoiplookup '+ip).read() 912 #print(soundtype) 913 countrycode = country.split(':')[1].split(',')[0].lower().strip() 914 country = country.split(':')[1].split(',')[1].strip() 915 #db.insert('visitors', ip=ip, referer=referer, environ=environ, country=country, countrycode=countrycode, time=formattime(datetime.datetime.now())) 916 thedict={'ip':ip,'referer':referer,'environ':environ,'country':country,'countrycode':countrycode,'time':formattime(datetime.datetime.now())} 917 savejson('r/visitors/'+ip,thedict) 918 except: 919 pass 920 print("added to visitor log") 921 return 922 923 def getvisitors(): 924 visitors=[] 925 v = get_files_by_time('r/visitors/',newest_first=True) 926 if v: 927 for i in v: 928 visit=loadjson('r/visitors/'+i) 929 visitors.append(visit) 930 print(visit) 931 total=len(os.listdir(basedir+'r/visitors/')) 932 unique=[] 933 for i in visitors: 934 for p in visitors: 935 if i['ip'] == p['ip']: 936 unique.append(i) 937 uniquevisits=len(unique) 938 return visitors, total, uniquevisits 939 940 def getvisits(): 941 visitors=[] 942 #v=os.listdir(basedir+'r/visitors/') 943 v = get_files_by_time('r/visitors/',newest_first=True) 944 for i in v: 945 visit=loadjson('r/visitors/'+i) 946 visitors.append(visit) 947 total=len(os.listdir(basedir+'r/visitors/')) 948 unique=[] 949 for i in visitors: 950 for p in visitors: 951 if i['ip'] == p['ip']: 952 unique.append(i) 953 uniquevisits=len(unique) 954 countrylist=[] 955 for i in visitors: 956 if i['countrycode'] not in countrylist: 957 countrylist.append(i['countrycode']) 958 return countrylist, total, uniquevisits 959 960 class stats: 961 def GET(self): 962 p = web.input(logfilter=None) 963 visitors, total, unique = getvisitors() 964 return rendersplash.stats(visitors, total, unique, p.logfilter) 965 966 class logout: 967 def GET(self): 968 session.login = 0 969 session.user = None 970 raise web.seeother('/') 971 972 def getlikes(postid, user): 973 user_likes = False 974 #l = db.query("SELECT Count(*) AS likes FROM likes WHERE bild='"+postid+"';")[0] 975 os.makedirs(basedir+'p/posts/'+postid+'/hearts/',exist_ok=True) 976 try: 977 l=len(os.listdir(basedir+'p/posts/'+postid+'/hearts/')) 978 print(os.listdir(basedir+'p/posts/'+postid+'/hearts/')) 979 except: 980 l=0 981 #db.update('published', where='postid="'+postid+'"', hearts=l.likes) 982 if user != None: 983 #m = db.query("SELECT * FROM likes WHERE bild='"+postid+"' AND user='"+user+"';") 984 m=loadjson('/p/posts/'+postid+'/hearts/'+user) 985 if m: 986 user_likes = True 987 else: 988 user_likes = False 989 if l >= 0: 990 if user_likes: 991 likes = hearted+" " + str(l) 992 else: 993 if l > 0: 994 likes = heart+" " + str(l) 995 else: 996 likes = heart 997 return likes 998 999 def postexist(postid): 1000 try: 1001 #l = db.select('published', where="postid='"+postid+"'")[0] 1002 l=loadjson('/p/posts/'+postid+'/meta') 1003 except: 1004 return False 1005 try: 1006 if l['postid'] != None: 1007 return True 1008 else: 1009 return False 1010 except: 1011 return False 1012 return False 1013 1014 def getcombines(postid): 1015 #l = db.query("SELECT Count(*) AS combines FROM published WHERE combine='"+postid+"';")[0] 1016 try: 1017 l=len(os.listdir(basedir+'p/posts/'+postid+'/combos/')) 1018 except: 1019 l=0 1020 print(postid) 1021 #m = db.query("SELECT * FROM likes WHERE bild='"+postid+"' AND user='"+user+"';") 1022 #db.update('published', where='postid="'+postid+'"', combines=0) 1023 if int(l) > 0: 1024 return "⚭ " + str(l) 1025 else: 1026 return '' 1027 1028 def pushcombines(postid): 1029 #l = db.query("SELECT Count(*) AS combines FROM published WHERE postid='"+postid+"';")[0] 1030 try: 1031 l=len(os.listdir(basedir+'p/posts/'+postid+'/combos/')) 1032 except: 1033 l=0 1034 #m = db.query("SELECT * FROM likes WHERE bild='"+postid+"' AND user='"+user+"';") 1035 #db.update('published', where='postid="'+postid+'"', combines=l.combines) 1036 #thedict={'combines':l} 1037 #savejson('/p/posts/'+postid+'/meta',thedict) 1038 if l >= 0: 1039 return "⚭ " + str(l) 1040 else: 1041 return '' 1042 1043 def formattime(timeadded): 1044 return timeadded.strftime("%Y-%m-%d %H:%M:%S") 1045 1046 def sort_by_name_then_time(path): 1047 sortedposts=[] 1048 heartrank=os.listdir(basedir+path) 1049 for h in heartrank: 1050 postid=h.split('-')[0] 1051 postrank=h.split('-')[1] 1052 sortedposts.append((postrank,postid)) 1053 heartrank=sorted(sortedposts, reverse=True) 1054 return heartrank 1055 1056 def rankrender(): 1057 posts = os.listdir(basedir+'p/posts') 1058 os.system('rm -r '+basedir+'p/heartrank') 1059 os.system('rm -r '+basedir+'p/comborank') 1060 os.makedirs(basedir+'p/comborank',exist_ok=True) 1061 os.makedirs(basedir+'p/heartrank',exist_ok=True) 1062 for postid in posts: 1063 try: 1064 l=len(os.listdir(basedir+'p/posts/'+postid+'/hearts/')) 1065 except: 1066 l=0 1067 #db.update('published', where='postid="'+postid+'"', hearts=l.likes) 1068 thedict={'hearts':l} 1069 savejson('p/posts/'+postid+'/meta',thedict) 1070 os.system('cp '+basedir+'p/posts/'+postid+'/meta '+basedir+'p/heartrank/'+postid+'-'+str(int(l)).zfill(16)) 1071 try: 1072 l=len(os.listdir(basedir+'p/posts/'+postid+'/combos/')) 1073 except: 1074 l=0 1075 #db.update('published', where='postid="'+postid+'"', hearts=l.likes) 1076 thedict={'combos':l} 1077 savejson('p/posts/'+postid+'/meta',thedict) 1078 os.system('cp '+basedir+'p/posts/'+postid+'/meta '+basedir+'p/comborank/'+postid+'-'+str(int(l)).zfill(16)) 1079 1080 rankrender() 1081 1082 def getfeed(): 1083 timebase=session.timebase 1084 feedbase=session.feedbase 1085 usr=session.usrfeed 1086 print('fuuuuuu') 1087 print(usr) 1088 if feedbase == '': 1089 feedbase = 'time' 1090 if timebase == '': 1091 timebase = 'week' 1092 now = datetime.datetime.now() 1093 goodies=[] 1094 #HEARTS 1095 #SAVE HEARTRANKING EVERY MINUTE CHECK BOTH USER LIKES AND POST LIKES IF IT CHECKS OUT GOOD IT NOT WRITE ERROR AT LEAST (A BACKEND PROGRAM, RUNS EVERY MINUTE AND COUNTS LIKES AND WRITES THE HEARTRANKING FOR TODAY. HEARTRANKING STAYES SAVED FOREVER IN FOLDERS BY DAYS. IT IS A FOLDER WITH NUMBERS. starting with 0000000000000001 pointing to postid. simple. effective. 1096 #backend program will also sync posts and likes to trustees 1097 if feedbase == "heart" and timebase == "today": 1098 one_day_before = now - datetime.timedelta(days=1) 1099 #goodies = db.query("SELECT * FROM published WHERE timeadded BETWEEN '"+one_day_before+"' AND '"+now+"' ORDER BY hearts DESC LIMIT 1000;") 1100 #posts=os.listdir('/p/posts/') 1101 #make function get_files_by_time newest_first and by today week month year 1102 posts = sort_by_name_then_time('p/heartrank/') 1103 for p in posts: 1104 #check modtime here day 1105 p=p[1] 1106 lastupdate = os.path.getmtime(basedir+'p/posts/'+p+'/meta') 1107 if datetime.datetime.fromtimestamp(lastupdate) > one_day_before: 1108 l=loadjson('p/posts/'+p+'/meta') 1109 if usr=='': 1110 goodies.append(l) 1111 if usr==l['creator']: 1112 goodies.append(l) 1113 elif feedbase == "heart" and timebase == "week": 1114 one_day_before = now - datetime.timedelta(weeks=1) 1115 #goodies = db.query("SELECT * FROM published WHERE timeadded BETWEEN '"+one_day_before+"' AND '"+now+"' ORDER BY hearts DESC LIMIT 1000;") 1116 posts = sort_by_name_then_time('p/heartrank/') 1117 for p in posts: 1118 #check modtime here day 1119 p=p[1] 1120 lastupdate = os.path.getmtime(basedir+'p/posts/'+p+'/meta') 1121 if datetime.datetime.fromtimestamp(lastupdate) > one_day_before: 1122 l=loadjson('p/posts/'+p+'/meta') 1123 if usr=='': 1124 goodies.append(l) 1125 if usr==l['creator']: 1126 goodies.append(l) 1127 elif feedbase == "heart" and timebase == "month": 1128 one_day_before = now - datetime.timedelta(weeks=4) 1129 #goodies = db.query("SELECT * FROM published WHERE timeadded BETWEEN '"+one_day_before+"' AND '"+now+"' ORDER BY hearts DESC LIMIT 1000;") 1130 posts = sort_by_name_then_time('p/heartrank/') 1131 for p in posts: 1132 #check modtime here day 1133 p=p[1] 1134 lastupdate = os.path.getmtime(basedir+'p/posts/'+p+'/meta') 1135 if datetime.datetime.fromtimestamp(lastupdate) > one_day_before: 1136 l=loadjson('p/posts/'+p+'/meta') 1137 if usr=='': 1138 goodies.append(l) 1139 if usr==l['creator']: 1140 goodies.append(l) 1141 elif feedbase == "heart" and timebase == "year": 1142 one_day_before = now - datetime.timedelta(weeks=54) 1143 #goodies = db.query("SELECT * FROM published WHERE timeadded BETWEEN '"+one_day_before+"' AND '"+now+"' ORDER BY hearts DESC LIMIT 1000;") 1144 posts = sort_by_name_then_time('p/heartrank/') 1145 for p in posts: 1146 #check modtime here day 1147 p=p[1] 1148 lastupdate = os.path.getmtime(basedir+'p/posts/'+p+'/meta') 1149 if datetime.datetime.fromtimestamp(lastupdate) > one_day_before: 1150 l=loadjson('p/posts/'+p+'/meta') 1151 if usr=='': 1152 goodies.append(l) 1153 if usr==l['creator']: 1154 goodies.append(l) 1155 elif feedbase == "heart" and timebase == "" or feedbase == "heart" and timebase == "all": 1156 #goodies = db.query("SELECT * FROM published ORDER BY hearts DESC LIMIT 1000;") 1157 posts = sort_by_name_then_time('/p/heartrank/') 1158 for p in posts: 1159 p=p[1] 1160 l=loadjson('p/posts/'+p+'/meta') 1161 if usr=='': 1162 goodies.append(l) 1163 if usr==l['creator']: 1164 goodies.append(l) 1165 #TIME 1166 elif feedbase == "time" and timebase == "today": 1167 one_day_before = now - datetime.timedelta(days=1) 1168 #goodies = db.query("SELECT * FROM published WHERE timeadded BETWEEN '"+one_day_before+"' AND '"+now+"' ORDER BY ID DESC LIMIT 1000;") 1169 posts = get_dirs_by_time('p/posts/', reverse=True) 1170 print(posts) 1171 for p in posts: 1172 #check modtime here day 1173 lastupdate = os.path.getmtime(basedir+'p/posts/'+p+'/meta') 1174 if datetime.datetime.fromtimestamp(lastupdate) > one_day_before: 1175 l=loadjson('p/posts/'+p+'/meta') 1176 if usr=='': 1177 goodies.append(l) 1178 if usr==l['creator']: 1179 goodies.append(l) 1180 elif feedbase == "time" and timebase == "week": 1181 one_day_before = now - datetime.timedelta(weeks=1) 1182 #goodies = db.query("SELECT * FROM published WHERE timeadded BETWEEN '"+one_day_before+"' AND '"+now+"' ORDER BY ID DESC LIMIT 1000;") 1183 posts = get_dirs_by_time('p/posts/', reverse=True) 1184 for p in posts: 1185 #check modtime here day 1186 lastupdate = os.path.getmtime(basedir+'p/posts/'+p+'/meta') 1187 if datetime.datetime.fromtimestamp(lastupdate) > one_day_before: 1188 l=loadjson('p/posts/'+p+'/meta') 1189 if usr=='': 1190 goodies.append(l) 1191 if usr==l['creator']: 1192 goodies.append(l) 1193 elif feedbase == "time" and timebase == "month": 1194 one_day_before = now - datetime.timedelta(weeks=4) 1195 #goodies = db.query("SELECT * FROM published WHERE timeadded BETWEEN '"+one_day_before+"' AND '"+now+"' ORDER BY ID DESC LIMIT 1000;") 1196 posts = get_dirs_by_time('p/posts/', reverse=True) 1197 for p in posts: 1198 #check modtime here day 1199 lastupdate = os.path.getmtime(basedir+'p/posts/'+p+'/meta') 1200 if datetime.datetime.fromtimestamp(lastupdate) > one_day_before: 1201 l=loadjson('p/posts/'+p+'/meta') 1202 if usr=='': 1203 goodies.append(l) 1204 if usr==l['creator']: 1205 goodies.append(l) 1206 elif feedbase == "time" and timebase == "year": 1207 one_day_before = now - datetime.timedelta(weeks=54) 1208 #goodies = db.query("SELECT * FROM published WHERE timeadded BETWEEN '"+one_day_before+"' AND '"+now+"' ORDER BY ID DESC LIMIT 1000;") 1209 posts = get_dirs_by_time('p/posts/', reverse=True) 1210 for p in posts: 1211 #check modtime here day 1212 lastupdate = os.path.getmtime(basedir+'p/posts/'+p+'/meta') 1213 if datetime.datetime.fromtimestamp(lastupdate) > one_day_before: 1214 l=loadjson('p/posts/'+p+'/meta') 1215 if usr=='': 1216 goodies.append(l) 1217 if usr==l['creator']: 1218 goodies.append(l) 1219 elif feedbase == "time" and timebase == "" or feedbase == "time" and timebase == "all": 1220 #goodies = db.query("SELECT * FROM published ORDER BY ID DESC LIMIT 1000;") 1221 posts = get_dirs_by_time('p/posts/', reverse=True) 1222 for p in posts: 1223 l=loadjson('p/posts/'+p+'/meta') 1224 if usr=='': 1225 goodies.append(l) 1226 if usr==l['creator']: 1227 goodies.append(l) 1228 1229 #COMBO 1230 elif feedbase == "combo" and timebase == "today": 1231 one_day_before = now - datetime.timedelta(days=1) 1232 #goodies = db.query("SELECT * FROM published WHERE timeadded BETWEEN '"+one_day_before+"' AND '"+now+"' ORDER BY combines DESC LIMIT 1000;") 1233 posts = sort_by_name_then_time('p/comborank/') 1234 for p in posts: 1235 #check modtime here day 1236 p=p[1] 1237 lastupdate = os.path.getmtime(basedir+'p/posts/'+p+'/meta') 1238 if datetime.datetime.fromtimestamp(lastupdate) > one_day_before: 1239 l=loadjson('p/posts/'+p+'/meta') 1240 if usr=='': 1241 goodies.append(l) 1242 if usr==l['creator']: 1243 goodies.append(l) 1244 elif feedbase == "combo" and timebase == "week": 1245 one_day_before = now - datetime.timedelta(weeks=1) 1246 #goodies = db.query("SELECT * FROM published WHERE timeadded BETWEEN '"+one_day_before+"' AND '"+now+"' ORDER BY combines DESC LIMIT 1000;") 1247 posts = sort_by_name_then_time('p/comborank/') 1248 for p in posts: 1249 #check modtime here day 1250 p=p[1] 1251 lastupdate = os.path.getmtime(basedir+'p/posts/'+p+'/meta') 1252 if datetime.datetime.fromtimestamp(lastupdate) > one_day_before: 1253 l=loadjson('p/posts/'+p+'/meta') 1254 if usr=='': 1255 goodies.append(l) 1256 if usr==l['creator']: 1257 goodies.append(l) 1258 elif feedbase == "combo" and timebase == "month": 1259 one_day_before = now - datetime.timedelta(weeks=4) 1260 #goodies = db.query("SELECT * FROM published WHERE timeadded BETWEEN '"+one_day_before+"' AND '"+now+"' ORDER BY combines DESC LIMIT 1000;") 1261 posts = sort_by_name_then_time('p/comborank/') 1262 for p in posts: 1263 #check modtime here day 1264 p=p[1] 1265 lastupdate = os.path.getmtime(basedir+'p/posts/'+p+'/meta') 1266 if datetime.datetime.fromtimestamp(lastupdate) > one_day_before: 1267 l=loadjson('p/posts/'+p+'/meta') 1268 if usr=='': 1269 goodies.append(l) 1270 if usr==l['creator']: 1271 goodies.append(l) 1272 elif feedbase == "combo" and timebase == "year": 1273 one_day_before = now - datetime.timedelta(weeks=54) 1274 #goodies = db.query("SELECT * FROM published WHERE timeadded BETWEEN '"+one_day_before+"' AND '"+now+"' ORDER BY combines DESC LIMIT 1000;") 1275 posts = sort_by_name_then_time('p/comborank/') 1276 for p in posts: 1277 #check modtime here day 1278 p=p[1] 1279 lastupdate = os.path.getmtime(basedir+'p/posts/'+p+'/meta') 1280 if datetime.datetime.fromtimestamp(lastupdate) > one_day_before: 1281 l=loadjson('p/posts/'+p+'/meta') 1282 if usr=='': 1283 goodies.append(l) 1284 if usr==l['creator']: 1285 goodies.append(l) 1286 elif feedbase == "combo" and timebase == "" or feedbase == "combo" and timebase == "all": 1287 #goodies = db.query("SELECT * FROM published ORDER BY combines DESC LIMIT 1000;") 1288 posts = sort_by_name_then_time('p/comborank/') 1289 for p in posts: 1290 #check modtime here day 1291 p=p[1] 1292 l=loadjson('p/posts/'+p+'/meta') 1293 if usr=='': 1294 goodies.append(l) 1295 if usr==l['creator']: 1296 goodies.append(l) 1297 elif feedbase == "Idontevenknow": 1298 #goodies = db.query("SELECT * FROM published ORDER BY combines DESC LIMIT 1000;") 1299 print('write your own custom feed here') 1300 else: 1301 #goodies = db.query("SELECT * FROM published ORDER BY ID DESC LIMIT 1000;") 1302 posts = os.listdir('p/posts/') 1303 for p in posts: 1304 l=loadjson('p/posts/'+p+'/meta') 1305 if usr=='': 1306 goodies.append(l) 1307 if usr==l['creator']: 1308 goodies.append(l) 1309 return goodies 1310 1311 def getcombofeed(show): 1312 timebase=session.timebase 1313 feedbase=session.feedbase 1314 if feedbase == "heart": 1315 #comboposts = db.query("SELECT * FROM published WHERE combine='"+show+"' ORDER BY hearts DESC LIMIT 1000;") 1316 posts = sort_by_name_then_time('p/heartrank/') 1317 comboposts=[] 1318 for p in posts: 1319 p=p[1] 1320 l=loadjson('p/posts/'+p+'/meta') 1321 if 'combine' in l: 1322 if l['combine'] == show: 1323 comboposts.append(l) 1324 elif feedbase == "combo": 1325 #comboposts = db.query("SELECT * FROM published WHERE combine='"+show+"' ORDER BY combines DESC LIMIT 1000;") 1326 posts = sort_by_name_then_time('p/comborank/') 1327 comboposts=[] 1328 for p in posts: 1329 p=p[1] 1330 l=loadjson('p/posts/'+p+'/meta') 1331 if 'combine' in l: 1332 if l['combine'] == show: 1333 comboposts.append(l) 1334 else: 1335 #comboposts = db.query("SELECT * FROM published WHERE combine='"+show+"' ORDER BY ID DESC LIMIT 1000;") 1336 posts = get_dirs_by_time('p/posts/',reverse=True) 1337 comboposts=[] 1338 for p in posts: 1339 l=loadjson('p/posts/'+p+'/meta') 1340 if 'combine' in l: 1341 if l['combine'] == show: 1342 comboposts.append(l) 1343 return comboposts 1344 1345 def userimage(user): 1346 usrimg = '' 1347 i = staticdir+'users/'+user+'/images/thumb/'+user 1348 print(i) 1349 if os.path.isfile(i+'.jpeg'): 1350 usrimg='static/users/'+user+'/images/thumb/'+user+'.jpeg' 1351 elif os.path.isfile(i+'.jpg'): 1352 usrimg='static/users/'+user+'/images/thumb/'+user+'.jpg' 1353 elif os.path.isfile(i+'.png'): 1354 usrimg='static/users/'+user+'/images/thumb/'+user+'.png' 1355 elif os.path.isfile(i+'.gif'): 1356 usrimg='static/users/'+user+'/images/thumb/'+user+'.gif' 1357 if usrimg != '': 1358 imghtml='<img class="usrimg" src="'+usrimg+'">' 1359 return imghtml 1360 else: 1361 return 1362 1363 class heartranked: 1364 form = web.form.Form(web.form.Textbox('search', web.form.notnull, description="or search")) 1365 def GET(self): 1366 siterendered=formattime(datetime.datetime.now()) 1367 visitorlog() 1368 visitors, total, unique = getvisits() 1369 print(visitors) 1370 print(str(total)) 1371 print(str(unique)) 1372 searchform = self.form() 1373 bildpersida = 1000 1374 session.search = '' 1375 session.bildsida = 0 1376 i = web.input(publised=None, public=None, show=None, remove=None, edit=None, feedbase=None, timebase=None, usrfeed=None) 1377 #search 1378 try: 1379 #bilder_totalt = db.query("SELECT COUNT(*) AS sound FROM published")[0] 1380 bilder_totalt=os.listdir(basedir+'p/posts/') 1381 tot = len(bilder_totalt) 1382 print('bilder alltsomallt: ' + str(tot)) 1383 except: 1384 print("inga bilder") 1385 tot = 0 1386 #print('session search: ' + session.search) 1387 try: 1388 if i.search == '': 1389 session.search = '' 1390 session.bildsida = 0 1391 elif i.search != "": 1392 session.search = urllib.parse.unquote_plus(i.search) 1393 session.bildsida = 0 1394 except: 1395 pass 1396 if session.search != '': 1397 search_result = [] 1398 tot = 0 1399 try: 1400 #search_result.append(db.query("SELECT * FROM published WHERE creator LIKE '%"+session.search+"%' ORDER BY ID DESC LIMIT 1000;")) 1401 #tot = db.query("SELECT Count(*) AS sound FROM published WHERE creator LIKE '%"+session.search+"%';")[0] 1402 #b1 = tot.sound 1403 for i in bilder_totalt: 1404 searchthis=loadjson('p/posts/'+i+'/meta') 1405 for p in dir(searchthis): 1406 if session.search in p: 1407 search_result.append(searchthis) 1408 tot=tot+1 1409 except: 1410 pass 1411 try: 1412 print(search_result) 1413 print('sökta bilder: ' + str(tot)) 1414 except: 1415 pass 1416 try: 1417 if i.page == "next": 1418 if session.bildsida < tot: 1419 session.bildsida += bildpersida 1420 if i.page == "back": 1421 if session.bildsida > bildpersida: 1422 session.bildsida -= bildpersida 1423 else: 1424 session.bildsida = 0 1425 except: 1426 pass 1427 limit = session.bildsida + bildpersida 1428 offset = session.bildsida 1429 #EOF search 1430 print(session.bildsida) 1431 bilder=[] 1432 if session.search == '': 1433 #bilder = db.query("SELECT * FROM published ORDER BY id DESC LIMIT " + str(limit) + " OFFSET " + str(offset)) 1434 posts=os.listdir(basedir+'p/posts/') 1435 for p in posts: 1436 this=loadjson('p/posts/'+p+'/meta') 1437 bilder.append(this) 1438 #print(bilder) 1439 else: 1440 bilder = search_result 1441 if i.feedbase == None: 1442 feedbase = '' 1443 else: 1444 feedbase = i.feedbase 1445 session.feedbase = feedbase 1446 if i.timebase == None: 1447 timebase = '' 1448 else: 1449 timebase = i.timebase 1450 session.timebase = timebase 1451 if i.timebase == None: 1452 timebase = '' 1453 else: 1454 timebase = i.timebase 1455 session.timebase = timebase 1456 if i.usrfeed == None: 1457 usrfeed = '' 1458 else: 1459 usrfeed = i.usrfeed 1460 session.usrfeed = usrfeed 1461 if session.user=='': 1462 free_hash_for_user = hashlib.md5(str(random.getrandbits(256)).encode('utf-8')).hexdigest()[:4] 1463 #session.user = 'heart_'+free_hash_for_user 1464 session.user = None 1465 if i.edit != None: 1466 session.postid=i.edit 1467 raise web.seeother('/editor?public=yes') 1468 if i.remove != None: 1469 try: 1470 #user = db.select('published', where="postid='"+i.remove+"'")[0] 1471 post=loadjson('p/posts/'+i.remove+'/meta') 1472 if post['creator'] == session.user: 1473 os.makedirs(basedir+'u/'+session.user+'/deleted/', exist_ok=True) 1474 os.makedirs(basedir+'p/deleted/', exist_ok=True) 1475 os.system('mv '+basedir+'u/'+session.user+'/posts/'+i.remove+' '+basedir+'u/'+session.user+'/deleted/') 1476 os.system('mv '+basedir+'p/posts/'+i.remove+' '+basedir+'p/deleted/') 1477 print('move to a deleted folder, make backend clean things up for real') 1478 except: 1479 pass 1480 if session.login > 3: 1481 try: 1482 if i.delete != '': 1483 return web.seeother('/remove/' + i.delete) 1484 except: 1485 pass 1486 rights = 'admin' 1487 elif session.login > 2: 1488 rights = 'mod' 1489 else: 1490 rights = 'spacer' 1491 return rendersplash.heartranked(markdown, visitors, total, unique, logged, rights, session.user, getlikes, formattime, feedbase, tot, limit, offset, bildpersida, session.search, bilder, searchform, getcombines, timebase, getfeed, getcombofeed, userimage, postexist, i.show, loadjson, loadtext, len, heart, hearted, sitename, siteslogan, siterendered, siteurl, session.usrfeed) 1492 def POST(self): 1493 searchform = self.form() 1494 i = web.input() 1495 if i.search != '': 1496 raise web.seeother('/?search='+i.search) 1497 1498 def zippitandshippit(postid): 1499 #also zippit here! 1500 os.makedirs(basedir+'r/trusted/'+session.user+'/', exist_ok=True) 1501 #os.system('zip -r '+basedir+'p/zipped/'+session.postid+'.zip '+basedir+'p/posts/'+session.postid ) 1502 os.system('cd '+basedir+'p/posts/ && zip -o -r '+postid+'.zip '+postid ) 1503 os.system('mv '+basedir+'p/posts/'+postid+'.zip '+basedir+'/p/zipped/') 1504 #LETS SHIPPIT! 1505 trustedlist=[] 1506 trusted=os.listdir(basedir+'r/trusted/'+session.user+'/') 1507 for t in trusted: 1508 trusted=loadjson('r/trusted/'+session.user+'/'+t) 1509 trustedlist.append(trusted) 1510 for t in trustedlist: 1511 url=t['servername']+':'+t['port'] 1512 for a in allowedchar: 1513 if '.'+a in t['servername']: #is webaddress use https 1514 url='https://'+t['servername'] 1515 passcode=t['password'] 1516 trustedlogin = ['curl','-X','POST', url+'/login', '-i', '-b', basedir+'/sessions/sessions-'+session.user, '-c',basedir+'/sessions/sessions-'+session.user, '-d', 'user='+t['user'], '-d', 'password='+passcode] 1517 subprocess.check_output(trustedlogin) 1518 #OK GOT EM COOKIES LES DO IT DO IT DO IT SHIPPIT! 1519 shippit = ['curl','-X', 'POST', '--verbose', '--header', 'Content-Type: multipart/form-data', '-F', 'files=@'+basedir+'p/zipped/'+postid+'.zip;type=application/zip', '-b', basedir+'/sessions/sessions-'+session.user, '-c',basedir+'/sessions/sessions-'+session.user, url+'/upload'] 1520 subprocess.check_output(shippit) 1521 1522 storage = {"content": ""} 1523 class editor: 1524 def GET(self): 1525 if logged(): 1526 i = web.input(publish=None, public=None, new=None, combine=None, remix=None) 1527 if i.combine != None: 1528 if session.user: 1529 text='' 1530 text2='' 1531 session.postid = hashlib.sha256(str(random.getrandbits(256)).encode('utf-8')).hexdigest()[9:36] 1532 os.makedirs(basedir+'u/'+session.user+'/posts/'+session.postid, exist_ok=True) 1533 thedict={'postid':session.postid, 'siteurl':siteurl, 'timeadded':formattime(datetime.datetime.now()), 'creator':session.user, 'combine':i.combine} 1534 savejson('u/'+session.user+'/posts/'+session.postid+'/meta',thedict) 1535 if i.remix != None: 1536 if session.user: 1537 text='' 1538 text2='' 1539 try: 1540 #olduser = db.select('unpublished', where="postid='"+i.remix+"'")[0] 1541 olduser=loadjson('p/posts/'+i.remix+'/meta') 1542 #text = db.select('unpublished', where="postid='"+i.remix+"'")[0] 1543 text=loadtext('p/posts/'+i.remix+'/intro') 1544 #text2 = db.select('unpublished', where="postid='"+i.remix+"'")[0] 1545 text2=loadtext('p/posts/'+i.remix+'/post') 1546 except: 1547 pass 1548 try: 1549 olduser = olduser['creator'] 1550 except: 1551 olduser = '' 1552 if olduser != '': 1553 allcreators = olduser+','+session.user 1554 else: 1555 allcreators = session.user 1556 session.postid = hashlib.sha256(str(random.getrandbits(256)).encode('utf-8')).hexdigest()[9:36] 1557 #db.insert('unpublished', postid=session.postid, description=text, description2=text2, timeadded=formattime(datetime.datetime.now()), creator=allcreators, remix=i.remix) 1558 os.makedirs(basedir+'u/'+session.user+'/posts/'+session.postid, exist_ok=True) 1559 thedict={'postid':session.postid, 'siteurl':siteurl,'timeadded':formattime(datetime.datetime.now()), 'creator':allcreators, 'remix':i.remix} 1560 os.makedirs(basedir+'p/posts/'+i.remix+'/remix', exist_ok=True) 1561 savetext('p/posts/'+i.remix+'/remix/'+session.postid,session.user) 1562 1563 savejson('u/'+session.user+'/posts/'+session.postid+'/meta',thedict) 1564 savetext('u/'+session.user+'/posts/'+session.postid+'/post', text2) 1565 savetext('u/'+session.user+'/posts/'+session.postid+'/intro', text) 1566 if session.postid != '': 1567 if i.public=='yes': 1568 try: 1569 #text = db.select('published', where="postid='"+session.postid+"'")[0] 1570 text=loadtext('p/posts/'+session.postid+'/intro') 1571 except: 1572 session.postid = '' 1573 else: 1574 try: 1575 #text = db.select('unpublished', where="postid='"+session.postid+"'")[0] 1576 text=loadtext('u/'+session.user+'/posts/'+session.postid+'/intro') 1577 except: 1578 text = '' 1579 if i.public=='yes': 1580 try: 1581 #text2 = db.select('published', where="postid='"+session.postid+"'")[0] 1582 text2=loadtext('p/posts/'+session.postid+'/post') 1583 except: 1584 session.postid = '' 1585 else: 1586 try: 1587 #text2 = db.select('unpublished', where="postid='"+session.postid+"'")[0] 1588 text2=loadtext('u/'+session.user+'/posts/'+session.postid+'/post') 1589 except: 1590 text2='' 1591 else: 1592 text = '' 1593 text2 = '' 1594 if i.new == 'yes': 1595 session.postid = '' 1596 raise web.seeother('/editor') 1597 if i.publish == 'yes' and text != '' and i.public == None and logged() and len(text) < 256: 1598 c=loadjson('u/'+session.user+'/posts/'+session.postid+'/meta') 1599 if 'combine' in c: 1600 if c['combine'] != '': 1601 #calculate comborank 1602 os.makedirs(basedir+'p/posts/'+c['combine']+'/combos', exist_ok=True) 1603 savetext('p/posts/'+c['combine']+'/combos/'+session.postid,session.user) 1604 os.makedirs(basedir+'p/heartrank/',exist_ok=True) 1605 try: 1606 l=len(os.listdir(basedir+'p/posts/'+session.postid+'/combos/')) 1607 except: 1608 l=0 1609 thedict={'combos':l} 1610 savejson('p/posts/'+c['combine']+'/meta',thedict) 1611 os.system('rm '+basedir+'p/comborank/'+session.postid+'-'+str(int(l)).zfill(16)) 1612 os.system('cp '+basedir+'p/posts/'+session.postid+'/meta '+basedir+'p/comborank/'+session.postid+'-'+str(int(l)).zfill(16)) 1613 description1 = text 1614 description2 = text2 1615 soundname = safe_filename(description1[0:27]) 1616 thedict={'soundname':soundname} 1617 savejson('u/'+session.user+'/posts/'+session.postid+'/meta',thedict) 1618 #session.postid = hashlib.sha256(str(random.getrandbits(256)).encode('utf-8')).hexdigest()[9:36] 1619 os.system('cp -r '+basedir+'u/'+session.user+'/posts/'+session.postid+' '+basedir+'p/posts/') 1620 #symlinkthis(session.postid, session.user) 1621 #also zippit here! 1622 #os.system('zip -r '+basedir+'p/zipped/'+session.postid+'.zip '+basedir+'p/posts/'+session.postid ) 1623 #os.system('cd '+basedir+'p/posts/ && zip -o -r '+session.postid+'.zip '+session.postid ) 1624 #os.system('mv '+basedir+'p/posts/'+session.postid+'.zip '+basedir+'/p/zipped/') 1625 if zipandship == 'yes' or zipandship == 'True' or zipandship == 'y' or zipandship == 'Y': 1626 zippitandshippit(session.postid) 1627 raise web.seeother('/') 1628 #db.insert('pawning', pawning=i.remix, name=session.user, timeadded=formattime(datetime.datetime.now())) 1629 return rendersplash.editor(storage, text, text2, markdown, safe_filename, session.postid, i.public, logged(), session.user, i.combine, i.remix) 1630 1631 class savepost: 1632 def POST(self): 1633 data = json.loads(web.data()) 1634 text = data.get("text", "") 1635 text2 = data.get("text2", "") 1636 if session.postid == '': 1637 session.postid = hashlib.sha256(str(random.getrandbits(256)).encode('utf-8')).hexdigest()[9:36] 1638 os.makedirs(basedir+'u/'+session.user+'/posts/'+session.postid,exist_ok=True) 1639 thedict={'postid':session.postid, 'siteurl':siteurl, 'timeadded':formattime(datetime.datetime.now()), 'creator':session.user} 1640 savejson('u/'+session.user+'/posts/'+session.postid+'/meta',thedict) 1641 savetext('u/'+session.user+'/posts/'+session.postid+'/intro', text) 1642 savetext('u/'+session.user+'/posts/'+session.postid+'/post', text2) 1643 else: 1644 os.makedirs(basedir+'u/'+session.user+'/posts/'+session.postid,exist_ok=True) 1645 thedict={'postid':session.postid, 'siteurl':siteurl, 'timeadded':formattime(datetime.datetime.now()), 'creator':session.user} 1646 savejson('u/'+session.user+'/posts/'+session.postid+'/meta',thedict) 1647 savetext('u/'+session.user+'/posts/'+session.postid+'/intro', text) 1648 savetext('u/'+session.user+'/posts/'+session.postid+'/post', text2) 1649 print('post saved!') 1650 return "ok" # simple response 1651 1652 class imageapi: 1653 def POST(self): 1654 action = '' 1655 data = json.loads(web.data()) 1656 image = data.get("image", "") 1657 action = data.get("action", "") 1658 original_thumb = Image.open(staticdir+'users/'+session.user+'/images/thumb/'+image) 1659 original_web = Image.open(staticdir+'users/'+session.user+'/images/web/'+image) 1660 if action != '': 1661 if action == 'rotateright' and image != None: 1662 o_thumb=original_thumb.rotate(270) 1663 o_web=original_web.rotate(270) 1664 if action == 'rotateleft' and image != None: 1665 o_thumb=original_thumb.rotate(90) 1666 o_web=original_web.rotate(90) 1667 o_thumb.save(staticdir+'users/'+session.user+'/images/thumb/'+image) 1668 o_web.save(staticdir+'users/'+session.user+'/images/web/'+image) 1669 return # simple response 1670 1671 class rendered: 1672 def GET(self): 1673 description='' 1674 description2='' 1675 try: 1676 description=loadtext('u/'+session.user+'/posts/'+session.postid+'/intro') 1677 except: 1678 pass 1679 try: 1680 description2=loadtext('u/'+session.user+'/posts/'+session.postid+'/post') 1681 except: 1682 pass 1683 return markdown.markdown(description+'\n\n---\n\n'+description2) 1684 1685 def get_new_frames(gif, scale): 1686 new_frames = [] 1687 actual_frames = gif.n_frames 1688 for frame in range(actual_frames): 1689 gif.seek(frame) 1690 new_frame = Image.new('RGBA', gif.size) 1691 new_frame.paste(gif) 1692 new_frame.thumbnail(scale, Image.Resampling.LANCZOS) 1693 new_frames.append(new_frame) 1694 return new_frames 1695 1696 def save_new_gif(new_frames, old_gif_information, new_path): 1697 new_frames[0].save(new_path, 1698 save_all = True, 1699 append_images = new_frames[1:], 1700 duration = old_gif_information['duration'], 1701 loop = old_gif_information['loop'], 1702 background = old_gif_information['background'], 1703 extension = old_gif_information['extension'] , 1704 transparency = old_gif_information['transparency']) 1705 1706 def scale_gif(path, scale, new_path=None): 1707 gif = Image.open(path) 1708 if not new_path: 1709 new_path = path 1710 old_gif_information = { 1711 'loop': bool(gif.info.get('loop', 1)), 1712 'duration': gif.info.get('duration', 40), 1713 'background': gif.info.get('background', 223), 1714 'extension': gif.info.get('extension', (b'NETSCAPE2.0')), 1715 'transparency': gif.info.get('transparency', 223) 1716 } 1717 new_frames = get_new_frames(gif, scale) 1718 save_new_gif(new_frames, old_gif_information, new_path) 1719 1720 def getallmedia(media_dir, extensions): 1721 files = [] 1722 for ext in extensions: 1723 c=list(Path(media_dir).rglob('*.'+ext)) 1724 for a in c: 1725 files.append(str(a)) 1726 print('wasassssup!?') 1727 print(files) 1728 return files 1729 1730 def symlinkmedia(mediafile,postid,user): 1731 fixstatic = mediafile.split('/posts/'+postid+'/')[1] 1732 symlinkfile = staticdir + 'users/' + user + '/'+fixstatic 1733 symlinkdir=symlinkfile.rsplit('/',1)[0] 1734 os.makedirs(symlinkdir, exist_ok=True) 1735 os.system('ln -sf '+mediafile+' '+symlinkfile) 1736 1737 def symlinkthis(postid,user): 1738 extensions=['zip','pdf','txt','md','mp4','jpeg','jpg','png','gif','wav','flac','mp3','ogg'] 1739 mediafiles = getallmedia(basedir+'u/'+user+'/posts/'+postid,extensions) 1740 for m in mediafiles: 1741 symlinkmedia(m,postid,user) 1742 print(mediafiles) 1743 1744 #symlinkthis('cd449464ec08fc7aa967d9b2795') 1745 1746 class upload: 1747 def POST(self): 1748 if logged(): 1749 if session.postid == '': 1750 session.postid = hashlib.sha256(str(random.getrandbits(256)).encode('utf-8')).hexdigest()[9:36] 1751 os.makedirs(basedir+'u/'+session.user+'/posts/'+session.postid,exist_ok=True) 1752 try: 1753 extensions=['zip','pdf','txt','md','mp4','jpeg','jpg','png','gif','wav','flac','mp3','ogg'] 1754 saved_files = [] 1755 # Best way for multiple files in web.py 1756 input_data = web.webapi.rawinput() 1757 uploaded = input_data.get('files') 1758 print('LETS GO!') 1759 print(session.user) 1760 print(input_data) 1761 print(uploaded) 1762 if not isinstance(uploaded, list): 1763 uploaded = [uploaded] if uploaded else [] 1764 for f in uploaded: 1765 if f and hasattr(f, 'filename') and f.filename: 1766 # Sanitize filename a bit 1767 imgdir = basedir + 'u/' + session.user + '/temp/' 1768 os.system('mkdir -p ' + imgdir) 1769 safe_name = safe_filename(os.path.basename(f.filename)) 1770 filepath = os.path.join(imgdir, safe_name) 1771 with open(filepath, 'wb') as out: 1772 out.write(f.file.read()) # Important: use .file.read() 1773 saved_files.append(safe_name) 1774 ##---------- UPLOAD SOUND ---------- 1775 imgname=filepath.split('/')[-1] # splits the and chooses the last part (the filename with extension) 1776 filetype = imgname.split('.')[-1].lower() 1777 soundfile=safe_name 1778 print(filetype) 1779 if filetype == 'zip': 1780 print('incoming!') 1781 usersound = basedir + 'u/' + session.user + '/zipped/' 1782 os.system('mkdir -p ' + usersound) 1783 os.system('mv ' + imgdir + soundfile + ' ' + usersound + soundfile) 1784 os.system('cd '+usersound+' && unzip -o '+soundfile+' -d '+basedir+'u/'+session.user+'/posts/') 1785 os.system('cd '+usersound+' && unzip -o '+soundfile+' -d '+basedir+'p/posts/') 1786 mediafiles = getallmedia(basedir+'p/posts/'+soundfile.split('.zip')[0],extensions) 1787 print('wowoweewaa') 1788 print(basedir+'p/posts/'+soundfile.split('.zip')[0]) 1789 print(mediafiles) 1790 for m in mediafiles: 1791 symlinkmedia(m,soundfile.split('.zip')[0],session.user) 1792 elif filetype == 'pdf' or filetype == 'txt' or filetype == 'md': 1793 usersound = basedir + 'u/' + session.user + '/posts/'+session.postid+'/docs/' 1794 os.system('mkdir -p ' + usersound) 1795 os.system('mv ' + imgdir + soundfile + ' ' + usersound + soundfile) 1796 symlinkmedia(usersound + soundfile,session.postid,session.user) 1797 elif filetype == 'mp4': 1798 usersound = basedir + 'u/' + session.user + '/posts/'+session.postid+'/films/' 1799 os.system('mkdir -p ' + usersound) 1800 os.system('mv ' + imgdir + soundfile + ' ' + usersound + soundfile) 1801 symlinkmedia(usersound + soundfile,session.postid,session.user) 1802 elif filetype == 'jpeg' or filetype == 'jpg' or filetype == 'png' or filetype == 'gif': 1803 usersound = basedir + 'u/' + session.user + '/posts/'+session.postid+'/images/' 1804 os.system('mkdir -p ' + usersound) 1805 os.makedirs(usersound + 'web/', exist_ok=True) 1806 os.makedirs(usersound + 'thumb/', exist_ok=True) 1807 os.system('mv ' + imgdir + soundfile + ' ' + usersound + soundfile) 1808 symlinkmedia(usersound + soundfile,session.postid,session.user) 1809 if filetype == 'gif': 1810 scale_gif(usersound+soundfile, [900,900], usersound+'web/'+soundfile) 1811 scale_gif(usersound+soundfile, [300,300], usersound+'thumb/'+soundfile) 1812 symlinkmedia(usersound + 'web/' + soundfile,session.postid,session.user) 1813 symlinkmedia(usersound + 'thumb/' + soundfile,session.postid,session.user) 1814 else: 1815 ##---------- OPEN FILE & CHEKC IF JPEG -------- 1816 image = Image.open(usersound + soundfile) 1817 ##---------- RESIZE IMAGE ----------- 1818 image.thumbnail((900,900), Image.Resampling.LANCZOS) 1819 image.save(usersound + 'web/' + soundfile) 1820 symlinkmedia(usersound + 'web/' + soundfile,session.postid,session.user) 1821 image.thumbnail((300,300), Image.Resampling.LANCZOS) 1822 image.save(usersound + 'thumb/' + soundfile) 1823 symlinkmedia(usersound + 'thumb/' + soundfile,session.postid,session.user) 1824 print('images resized images') 1825 elif filetype == 'wav' or filetype == 'flac' or filetype == 'mp3' or filetype == 'ogg': 1826 usersound = basedir + 'u/' + session.user + '/posts/'+session.postid+'/sounds/' 1827 os.system('mkdir -p ' + usersound) 1828 os.system('mv ' + imgdir + soundfile + ' ' + usersound + soundfile) 1829 soundlenght = os.popen('mediainfo --Inform="General;%Duration%" ' + usersound + soundfile).read() 1830 print('sound lenght:' + str(soundlenght)) 1831 soundtype = os.popen('mediainfo --Inform="General;%Format%" ' + usersound + soundfile).read() 1832 print(soundtype) 1833 if 'Ogg' in soundtype: 1834 #os.system('ffmpeg -i '+usersound+soundfile+' '+usersound+soundname+'.wav') 1835 print('ogg file found, converting to flac') 1836 os.system('ffmpeg -i ' + usersound + soundfile +' '+ usersound + soundname + '.flac') 1837 print('converting to mp3') 1838 os.system('ffmpeg -y -loglevel 1 -i ' + usersound + soundname + '.flac -c:a libmp3lame -b:a 192k ' + usersound + soundname + '.mp3') 1839 if 'MPEG Audio' in soundtype: 1840 print('mp3 file found, converting to flac') 1841 os.system('ffmpeg -i ' + usersound + soundfile + ' ' + usersound + soundname + '.flac') 1842 print('converting to ogg') 1843 os.system('ffmpeg -i ' + usersound + soundname +'.flac '+ usersound + soundname + '.ogg') 1844 print('Wave file found, converting to flac') 1845 if 'Wave' in soundtype: 1846 print('Wave file found, converting to flac') 1847 os.system('flac ' + usersound + soundfile + ' ' + usersound + soundname + '.flac') 1848 os.system('sox -V1 ' + usersound + soundfile + ' ' + usersound + soundname + '.ogg') 1849 os.system('ffmpeg -y -loglevel 1 -i ' + usersound + soundfile + ' -c:a libmp3lame -b:a 192k ' + usersound + soundname + '.mp3') 1850 if 'FLAC' in soundtype: 1851 print('FLAC file found, converting to mp3 and ogg') 1852 os.system('sox -V1 ' + usersound + soundfile + ' ' + usersound + soundname + '.ogg') 1853 os.system('ffmpeg -y -loglevel 1 -i ' + usersound + soundfile + ' -c:a libmp3lame -b:a 192k ' + usersound + soundname + '.mp3') 1854 saved_files.append(safe_name) 1855 print(f"✅ Saved: {safe_name}") # This will show in console for debugging 1856 else: 1857 print("⚠️ Skipped invalid file object") 1858 except Exception as e: 1859 print("Upload error:", str(e)) 1860 return f"❌ Error: {str(e)}" 1861 if saved_files: 1862 return f"✅ Successfully uploaded {len(saved_files)} file(s): {', '.join(saved_files)}" 1863 else: 1864 return "❌ No files received. Check console for details." 1865 else: 1866 print('access denied') 1867 1868 class uploads: 1869 def GET(self): 1870 if logged(): 1871 uploaded = getfiles(staticdir+'upload/') 1872 return render.uploads(uploaded) 1873 1874 class pull: 1875 def GET(self): 1876 if logged(): 1877 i = web.input(name=None,postid=None,passcode=None) 1878 posts = get_dirs_by_time(basedir+'p/posts')[postid:] 1879 zippandshipp=[] 1880 if i.name!=None: 1881 users = os.listdir(basedir+'r/users/') 1882 for r in users: 1883 if r['name']==i.name: 1884 for p in posts: 1885 if p['creator']==p.name: 1886 zippandshipp.append(p['postid']) 1887 print(zippandshipp) 1888 for z in zippandshipp: 1889 zippitandshippit(z) 1890 1891 #Load from settings 1892 standalone = settings.standaloneserver 1893 if standalone == 'yes' or standalone == 'True' or standalone == 'y' or standalone == 'Y': 1894 app.run() 1895 else: 1896 application = app.wsgifunc()