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