The following code used to work using an SQLite file, but now that I've converted the db into a Postgres db, it doesn't work anymore. There are two types employed in the new db (text and bigint) compared to the old db (text and number). The voll column (text) contains a JSON string.

import requests,json,re,sqlite3,gc,os,sys,time,unicodedata,traceback,html,random

from lxml.html import fromstring
from flask import Flask,request,session,send_from_directory,render_template,send_file,redirect
from threading import Thread,Event,local
from datetime import datetime
import psycopg2
import psycopg2.extras

import grab_server,grab_search,grab_watch,settings,imagesize

s = requests.Session()
'''		
connection=sqlite3.connect(settings.filename, check_same_thread=False)
connection.execute('PRAGMA journal_mode=WAL;')
connection.text_factory = sqlite3.Binary
cursor=connection.cursor()
'''

# Connection parameters
conn_params = {
    'dbname': 'blabla',
    'user': 'bla',
    'host': 'localhost',  # or the address of your PostgreSQL server
    'port': '5432'        # default PostgreSQL port
}

# Connect to PostgreSQL
connection = psycopg2.connect(**conn_params)

# Create a cursor
cursor = connection.cursor()

# Enable autocommit if needed (optional)
connection.autocommit = True

# Use psycopg2's Binary for binary data
psycopg2_extras = psycopg2.extras

_thread_locals = local()


app = Flask(__name__)
app.config['SECRET_KEY']='ENTERSOMETHINGRANDOM'

app.template_folder = "."

app.url_map.strict_slashes = False


page=1

def setup_fts():
    # Create the FTS table
    cursor.execute('''
    CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
        id1 UNINDEXED,
		id2 UNINDEXED,
        board UNINDEXED,
        message,
        subject,
		files,
		date,
        tokenize = 'unicode61'
    );
    ''')
    connection.commit()

    # Clear existing data from the FTS table
    cursor.execute('DELETE FROM messages_fts;')
    connection.commit()

    boards = []

    cursor.execute("SELECT name FROM sqlite_schema WHERE type ='table' AND name NOT LIKE 'sqlite_%'")
    rows = cursor.fetchall()

    for row in rows:
        json_like_memoryview = row[0]
        json_like_bytes = bytes(json_like_memoryview)  # Convert memoryview to bytes
        json_like_string = json_like_bytes.decode('utf-8')  # Convert bytes to str
        boards.append(json_like_string)

    # Populate the FTS table
    for board in boards:
        if board != 'fadenWache' and "messages_fts" not in board:
            # Insert top-level messages and subjects
            cursor.execute(f'''
            INSERT INTO messages_fts (id1, board, message, subject, files, date)
            SELECT {board}.id AS id1,
                   '{board}' AS board,
                   json_extract({board}.voll, '$.message') AS message,
                   json_extract({board}.voll, '$.subject') AS subject,
				   json_extract({board}.voll, '$.files') AS files,
				   zeit as date
            FROM {board}
            WHERE json_valid({board}.voll)
              AND (json_extract({board}.voll, '$.message') IS NOT NULL
                   OR json_extract({board}.voll, '$.subject') IS NOT NULL);
            ''')

            # Insert messages and subjects from nested posts
            cursor.execute(f'''
            INSERT INTO messages_fts (id1, id2, board, message, subject, files, date)
            SELECT {board}.id AS id1,
			       json_extract(p.value, '$.postId') AS id2,
                   '{board}' AS board,
                   json_extract(p.value, '$.message') AS message,
                   json_extract(p.value, '$.subject') AS subject,
				   json_extract(p.value, '$.files') AS files,
				   zeit as date
            FROM {board}
            JOIN json_each({board}.voll, '$.posts') AS p
            WHERE json_valid({board}.voll)
              AND (json_extract(p.value, '$.message') IS NOT NULL
                   OR json_extract(p.value, '$.subject') IS NOT NULL);
            ''')
    connection.commit()

    # Set up triggers for each board
    for board in boards:
        if board != 'fadenWache':
            # Drop existing triggers if they exist
            cursor.executescript(f'''
            DROP TRIGGER IF EXISTS {board}_after_insert;
            DROP TRIGGER IF EXISTS {board}_after_update;
            DROP TRIGGER IF EXISTS {board}_after_delete;
            ''')

            # Create triggers (same as in step 3)
            # ... (Use the trigger creation code provided earlier)

    connection.commit()
    print('completed setup_fts')
	

def fetchURL(session,url):
	while True:
		try:
			#print('Fetch '+url+' with '+str(proxies))
			res=session.get(url,proxies=settings.proxies,headers=settings.headers)
			#print(res)
			return json.loads(res.text)
		except Exception as e:
			print(e)
			time.sleep(10)

def fetchThreads(c,conn):

			for v in settings.temp_prefs:

				imgList=""

				data=fetchURL(s,f'{settings.board}/{v}/catalog.json')

				data=sorted(data, key=lambda thread: thread['lastBump'],reverse=False)

				c.execute(f"SELECT id,zeit,antworten,gesichert,erreichbar FROM {v} where erreichbar = ?", (1,))
				result2=c.fetchall()

				for item in result2:
					if re.search('"threadId": '+str(item[0])+'', json.dumps(data), re.M):
					#	print(str(item[0])+" ist da!")
						continue
					else:
						print(str(item[0])+' has to go!')
						c.execute(f'''UPDATE {v} SET erreichbar = ? WHERE id = ? ''',('0',item[0]))
						conn.commit()
				
				c.execute(f"SELECT id,zeit,antworten,gesichert,erreichbar FROM {v} where erreichbar = ? and gesichert = ?", (0,0,))
				result2=c.fetchall()

				for item in result2:
					c.execute(f'''DELETE from {v} WHERE id = ? ''',(item[0],))
					conn.commit()
					print(str(item[0])+" removed!")

				for row in data:

					if 'message' in row:
						message=normalizeString(row['message'],'y',0,50,'utf-8')+'...' if len(row['message'])>50 else normalizeString(row['message'],'n',0,0,'utf-8')
						lastbump=normalizeString(row['lastBump'],'n',0,0,'utf-8')
						threadid=str(row['threadId'])
						if 'postCount' in row:
							row['postCount']+=1
							replies=str(row['postCount'])
						else:
							replies=1

				#		print message+'... ('+lastbump+') ('+threadid+') '+replies+' 0'
						try:
							c.execute(f"SELECT id,zeit,antworten,gesichert,erreichbar FROM {v} where id = ? LIMIT 1", (threadid,))
							result=c.fetchone()
						except:
							break
						if (result):
							if(result[0]==int(threadid)):
								#print('!!!'+str(result[0])+' ('+zeit+') '+str(result[2])+' | online: '+str(threadid)+' ('+str(lastbump)+') '+str(replies))
								if(result[2]!=int(replies)):
									while(True):
										try:
											c.execute(f'''UPDATE {v} SET zeit = ?,antworten = ? WHERE id = ? ''',(lastbump,replies,threadid))
											conn.commit()
											break
										except:
											print("Database can't be opened")
											break

								if(result[3]!=int(replies) and result[4]==True):
									try:
										res=s.get(f'{settings.board}/{v}/res/'+threadid+'.json',proxies=settings.proxies,headers=settings.headers)
										tree = fromstring(res.content)
										if (tree.findtext('.//title'))=='File not found':
											print(threadid+" isn't available anymore")
											c.execute(f'''UPDATE {v} SET erreichbar = ? WHERE id = ? ''',('0',threadid))
											conn.commit()
										else: 
											data=json.loads(res.content)

											sizeImages=0
											countImages=1
											currentIteration=0

											sizeImages+=len(data['files'])

											for b in data['posts']:
												if ('files' in b.keys()):
													sizeImages+=len(b['files'])

											imgList,countImages,currentIteration=grab_server.gatherThumbs(data,imgList,sizeImages,countImages,currentIteration)

											for b in reversed(data['posts']):
												imgList,countImages,currentIteration=grab_server.gatherThumbs(b,imgList,sizeImages,countImages,currentIteration)

											imgList=""
											
											try:
												lastchecked=data['posts'][-1]['creation']
											except:
												lastchecked=data['creation']
												

											c.execute(f'''UPDATE {v} SET voll = ?,gesichert = ?, zeit = ? WHERE id = ? ''',(res.content,replies,lastchecked,threadid))
											conn.commit()
											sys.stdout.write('\n'+threadid+' saved ('+str(round(time.time()))+')')
											sys.stdout.flush()
											
											sys.stdout.write("\r\n")
											sys.stdout.write(f"{v} refreshed ("+str(round(time.time()))+")")
											sys.stdout.flush()
											
											

									except Exception:
										print(traceback.format_exc())
										print('Error (trying again...)')
										break

							continue
						else:
							try:
								print ('Add (',cMessage(message),', ',threadid,')')
								sys.stdout.flush()
								conn.execute(f"INSERT INTO {v} VALUES (?, ?, ?, ?, ?, '0', '1')", (message,message,lastbump,threadid,replies,))
								conn.commit() 
							except:
								break

				

				
				print("\nplease wait...")
				time.sleep(2)

				#saveRest(c)
				
				print("\nrefresh")


def saveRest(c):
	for v in settings.temp_prefs:
		c.execute(f"SELECT voll,zeit from {v} WHERE erreichbar!=0 ORDER by zeit ASC")
		result=c.fetchall()
		data={}
		
		count=0
		totalIteration=0

		for row in result:
			json_like_memoryview = row[0]
			json_like_bytes = bytes(json_like_memoryview)  # Convert memoryview to bytes
			json_like_string = json_like_bytes.decode('utf-8')  # Convert bytes to str

			try:
				data[count]=json.loads(json_like_string)
				
			except Exception as e:
				continue
				
			count+=1

		for a in data:
			if totalIteration>20:
				break

			imgList=""

			sizeImages=0
			countImages=1
			currentIteration=0

			sizeImages+=len(data[a]['files'])
				
			for b in data[a]['posts']:
				if ('files' in b.keys()):
					sizeImages+=len(b['files'])

			imgList,countImages,currentIteration=grab_server.gatherThumbs(a,imgList,sizeImages,countImages,currentIteration)

			for b in reversed(data[a]['posts']):
				imgList,countImages,currentIteration=grab_server.gatherThumbs(b,imgList,sizeImages,countImages,currentIteration)
				
				if currentIteration==5:
					#print('iteriere')
					totalIteration+=1
					break



def createTable(c,conn):
	c.execute('''SELECT tbl_name FROM sqlite_master WHERE type='table'
	''')
	liste=c.fetchall()
	
	for v in settings.temp_prefs:
		if v in liste:
			continue
		else:
			c.execute(f'''CREATE TABLE {v} (nachricht text,voll text,zeit text,id number,antworten number,gesichert number,erreichbar number)''')
	conn.commit()



def cMessage(value):
    return ''.join(value.splitlines())



def normalizeString(array,cut,start,stop,encoding):
	if (cut)=='y': 
		normal=bytes(unicodedata.normalize('NFKD',array)[start:stop].encode(encoding,'ignore'))
		return normal.decode('utf-8')
	else: 
		normal=bytes(unicodedata.normalize('NFKD',array).encode(encoding,'ignore'))
		return normal.decode('utf-8')
	


@app.route('/<bid>/t/<fid>')
def doThread(fid,bid):

	faden = fid

	cursor=connection.cursor()

	cursor.execute(f'''SELECT COUNT(*) FROM "{bid}" WHERE gesichert > 0 AND voll!=nachricht AND antworten==gesichert''')
	tsNum=cursor.fetchone()[0]

	cursor.execute(f'''SELECT "_rowid_",* FROM "{bid}" WHERE id=={faden} ''')
	tsRec=cursor.fetchall()

	avail=bool(tsRec[0][7])

	threadPage={}

	for a in range(0,len(tsRec)):
		for b in tsRec[a]:
			if (type(b)==bytes):
				threadPage[a]=bytes(b)
		threadPage[a]=json.loads(threadPage[a])

	cursor.execute(f'''UPDATE "fadenWache" SET antworten={len(threadPage[a]['posts'])+1} WHERE id=={faden} AND brett=="{bid}"''')

	connection.commit()

	response=grab_server.showThread(tsNum,threadPage,page,bid,avail)
	return response


@app.route('/<bid>')
def doServer(bid):
	cursor=connection.cursor()

	cursor.execute(f'''SELECT COUNT(*) FROM "{bid}" WHERE gesichert > 0 AND voll!=nachricht AND antworten==gesichert''')
	tsNum=cursor.fetchone()[0]

	cursor.execute(f'''SELECT "_rowid_",* FROM "{bid}" WHERE gesichert > 0 AND voll!=nachricht AND antworten==gesichert ORDER BY "zeit" DESC LIMIT 0, 10 ''')
	tsRec=cursor.fetchall()

	#print(firstThread)

	threadsList={}
	threadsListB={}

	for a in range(0,len(tsRec)):
		#print('Länge'+str(len(tsRec)))
		for b in tsRec[a]:
			if (type(b)==bytes):
				threadsListB[a]=bytes(b)
		threadsList[a]=json.loads(threadsListB[a])

	for a in range(0,len(threadsList)):
		if (str(bid)!=str(threadsList[a]['boardUri'])):
			cursor.execute(f'''SELECT id FROM "{bid}" WHERE voll == ?''', (threadsListB[a],))
			result=cursor.fetchone()

			threadsList[a]['threadId']=result[0]


	response=grab_server.sendList(tsNum,threadsList,page,bid)
	return response
	
	

@app.route('/<bid>/p/<pid>')
def doTPage(bid,pid):
	page = int(pid)

	pageMin=page*10-10

	cursor=connection.cursor()

	cursor.execute(f'''SELECT COUNT(*) FROM "{bid}" WHERE gesichert > 0 AND voll!=nachricht AND antworten==gesichert''')
	tsNum=cursor.fetchone()[0]

	cursor.execute(f'''SELECT "_rowid_",* FROM "{bid}" WHERE gesichert > 0 AND voll!=nachricht AND antworten==gesichert ORDER BY "zeit" DESC LIMIT {pageMin},10 ''')
	tsRec=cursor.fetchall()

	#print(firstThread)

	threadsList={}
	threadsListB={}

	for a in range(0,len(tsRec)):
		#print('Länge'+str(len(tsRec)))
		for b in tsRec[a]:
			if (type(b)==bytes):
				threadsListB[a]=bytes(b)
		threadsList[a]=json.loads(threadsListB[a])

	for a in range(0,len(threadsList)):
		if (str(bid)!=str(threadsList[a]['boardUri'])):
			cursor.execute(f'''SELECT id FROM "{bid}" WHERE voll == ?''', (threadsListB[a],))
			result=cursor.fetchone()

			threadsList[a]['threadId']=result[0]


	response=grab_server.sendList(tsNum,threadsList,page,bid)
	return response
	
	
@app.route('/<bid>/catalog')
def doCatalog(bid):

	cursor=connection.cursor()

	cursor.execute(f'''SELECT COUNT(*) FROM "{bid}" WHERE gesichert > 0 AND voll!=nachricht AND antworten==gesichert''')
	tsNum=cursor.fetchone()[0]

	cursor.execute(f'''SELECT "_rowid_",* FROM "{bid}" WHERE gesichert > 0 AND voll!=nachricht AND antworten==gesichert ORDER BY "zeit" DESC LIMIT 150 OFFSET 0''')
	tsRec=cursor.fetchall()

	#print(firstThread)

	threadsList={}
	threadsListB={}

	for a in range(0,len(tsRec)):
		#print('Länge'+str(len(tsRec)))
		for b in tsRec[a]:
			if (type(b)==bytes):
				threadsListB[a]=bytes(b)
		threadsList[a]=json.loads(threadsListB[a])

	for a in range(0,len(threadsList)):
		if (str(bid)!=str(threadsList[a]['boardUri'])):
			cursor.execute(f'''SELECT id FROM "{bid}" WHERE voll == ?''', (threadsListB[a],))
			result=cursor.fetchone()

			threadsList[a]['threadId']=result[0]
            

	response=grab_server.sendList(tsNum,threadsList,1,bid,True)
	return response

@app.route('/<bid>/catalog/p/<pid>')
def doCPage(bid,pid):
	page = int(pid)

	pageMin=page*150-150

	cursor=connection.cursor()

	cursor.execute(f'''SELECT COUNT(*) FROM "{bid}" WHERE gesichert > 0 AND voll!=nachricht AND antworten==gesichert''')
	tsNum=cursor.fetchone()[0]

	cursor.execute(f'''SELECT "_rowid_",* FROM "{bid}" WHERE gesichert > 0 AND voll!=nachricht AND antworten==gesichert ORDER BY "zeit" DESC LIMIT 150 OFFSET {pageMin}''')
	tsRec=cursor.fetchall()

	#print(firstThread)

	threadsList={}
	threadsListB={}

	for a in range(0,len(tsRec)):
		#print('Länge'+str(len(tsRec)))
		for b in tsRec[a]:
			if (type(b)==bytes):
				threadsListB[a]=bytes(b)
		threadsList[a]=json.loads(threadsListB[a])

	for a in range(0,len(threadsList)):
		if (str(bid)!=str(threadsList[a]['boardUri'])):
			cursor.execute(f'''SELECT id FROM "{bid}" WHERE voll == ?''', (threadsListB[a],))
			result=cursor.fetchone()

			threadsList[a]['threadId']=result[0]
            

	response=grab_server.sendList(tsNum,threadsList,page,bid,True)
	return response



@app.route('/')
def getHome():
	return redirect("/"+settings.temp_prefs[0], code=302)
	


@app.route('/search', methods=['GET'])
def doSearch():
	#bid=request.form['fbid']
	
	tic = time.perf_counter()
	search_term=request.args.get('q')
	
	cursor=connection.cursor()

	cursor.execute("SELECT name FROM sqlite_schema WHERE type ='table' AND name NOT LIKE 'sqlite_%'")
	rows = cursor.fetchall()
	
	boards = []
	
	for row in rows:
		json_like_memoryview = row[0]
		json_like_bytes = bytes(json_like_memoryview)  # Convert memoryview to bytes
		json_like_string = json_like_bytes.decode('utf-8')  # Convert bytes to str
		boards.append(json_like_string)

	search_term=" ".join(search_term.split())

	if not search_term:
		# Retrieve all records without using the MATCH clause
		query = '''
			SELECT id1, id2, board, message, subject, files, date
			FROM messages_fts;
		'''
		cursor.execute(query)
	else:
		# Use the MATCH clause as usual
		query = '''
			SELECT id1, id2, board, message, subject, files, date
			FROM messages_fts
			WHERE messages_fts MATCH ?;
		'''
		cursor.execute(query, (search_term,))


	results = cursor.fetchall()

	out=""
	outnum=0

	# Define the regex pattern to match HTML tags and text outside of tags
	pattern = r'(<[^>]*>)|([^<]+)'

	# Determine if 'search_term' is enclosed in quotation marks
	if (search_term.startswith('"') and search_term.endswith('"')) or (search_term.startswith("'") and search_term.endswith("'")):
		is_quoted = True
		# Remove quotation marks
		search_term_clean = search_term[1:-1].strip()
	else:
		is_quoted = False
		# Remove any stray quotation marks within the term
		search_term_clean = search_term.replace('"', '').replace("'", '').strip()

	# Build the 'words_pattern' based on whether the term is quoted
	if is_quoted:
		# Only match the full phrase
		escaped_phrase = re.escape(search_term_clean)
		words_pattern = r'\b{}\b'.format(escaped_phrase)
	else:
		# Match both the full phrase and its individual words
		search_terms = [search_term_clean] + search_term_clean.split()
		escaped_terms = [re.escape(term) for term in search_terms]
		words_pattern = r'\b(' + '|'.join(escaped_terms) + r')\b'

    # Process the results
	for id1, id2, board, message, subject, files, date in results:
		if (outnum<1000):
			board = bytes(board).decode('utf-8')
			#out += (f'Board: {board} ')
			#out += (f'TID: <a href="/{board}/t/{id1}">{id1}</a> ')
			#out += (f'PID: <a href="/{board}/t/{id1}#{id2}">{id2}</a> ')



			# Process the subject
			subject_text = (
				bytes(subject).decode("utf-8") + ' ' if isinstance(subject, memoryview) else ""
			)
			# Use the updated 'words_pattern' in the lambda function
			subject_highlighted = re.sub(
				pattern,
				lambda match: (
					match.group(0) if match.group(1) else
					re.sub(words_pattern, r'<mark>\g<0></mark>', match.group(2), flags=re.IGNORECASE)
				),
				subject_text,
				flags=re.IGNORECASE | re.MULTILINE
			)

			# Process the message
			if isinstance(message, memoryview):
				message_content = (

					grab_server.escapeMessage(
						bytes(message).decode("utf-8"),
						id1,
						board
					) 
				)
			else:
				message_content = ""
			message_highlighted = re.sub(
				pattern,
				lambda match: (
					match.group(0) if match.group(1) else
					re.sub(words_pattern, r'<mark>\g<0></mark>', match.group(2), flags=re.IGNORECASE)
				),
				message_content,
				flags=re.IGNORECASE | re.MULTILINE
			)
			
			filelist=""
			
			if isinstance(files, memoryview):
				files=bytes(files).decode("utf-8")

			files=json.loads(files)

			for file in files:
				try:
					localpath=f"./media/{file['thumb'][7:]}.webp"
					localpath2=f"./media/{file['thumb'][7:]}.png"

					

					if os.path.exists(localpath)==True:
						width,height=imagesize.get(localpath)
						filelist+=f'<img src="{localpath}" style="max-width:165px;float:left;margin-right:10px;margin-bottom:10px;"  width="{width}" height="{height*(165/width) if width>165 else height}" loading="lazy">'
					elif os.path.exists(localpath2)==True:
						width,height=imagesize.get(localpath2)
						filelist+=f'<img src="{localpath2}" style="max-width:165px;float:left;margin-right:10px;margin-bottom:10px;"  width="{width}" height="{height*(165/width) if width>165 else height}" loading="lazy">'

				except:
					continue
			
			if message_highlighted != "":
				if len(message_highlighted)>1000:
					message_highlighted=message_highlighted[:1000]
					message_highlighted+=f'...</pre><pre style="display:inline;"><a href="/{board}/t/{id1}"><b>[expand thread]</b></a>'
				out += (f'<div id="post" class="post"><i><b id="postTitle">{subject_highlighted}</b> found in <a href="/{board}/t/{id1}#{id2}">{id1}</a> ({bytes(date).decode("utf-8")})<br><pre>{filelist}{message_highlighted}</pre></i></div><br>')
			#out+=('<br>')
				outnum+=1

	if (outnum>=500):
		out+="Too many results"



	arrPrep={}

	if 'wide' in session:
		if session['wide']==True:
			wideFormat='max-width:780px;'
		elif session['wide']==False:
			wideFormat=''
	else:
		wideFormat='max-width:780px;'
	
	if search_term==" ":
		search_term="<i>-blank-</i>"
	
	arrPrep=f'''<table style="width:100%;margin-top:-5px;{wideFormat}font-size:10pt;"><tbody><tr><td style="text-align: left;width:26%;"><a href="/">↑ Home</a>'''+grab_server.generateSettings()+f'''       </td><td style="text-align:center;">Searched for <b>{search_term}</b> ({str(len(results))} results)</td><td style="text-align: right;width:35%;"><div class="boardList" style="max-width: 30%;">'''

	arrPrep+=f'<span>↷ /all/</span><div class="boardList-content">'

	for v in settings.temp_prefs:
		arrPrep+=f'<p><a href="/{v}">/{v}/</a></p>'    
	
	arrPrep+=f'</div></div> <form action="/search" method="get" style="display:inline;"><input style="width:61%;min-width:87.95px;font-size:10pt;max-width: 50%" type="text" placeholder="Search all boards" id="q" name="q"> <input style="font-size:10pt;" type="submit" value="Find"></form></td></tr></tbody></table><hr><div id="empty" style="padding-top:4px"></div>'


	return '<!DOCTYPE html><head><title>KC-Archive</title><meta name = "viewport" content = "width = device-width"></head>'+grab_server.generatePageFormat(session)+arrPrep+str(out)
					

	'''
	query1=""

	for a in range(0,len(boards)):
		if a==0 and boards[a]!='fadenWache':
			query1+=f'SELECT "voll","zeit" FROM "{boards[a]}" '
		elif boards[a]!='fadenWache':
			query1+=f'UNION SELECT "voll","zeit" FROM "{boards[a]}" '
			
		if a==(len(boards)-1):
			query1+=f'ORDER BY "zeit" DESC'

	#print(query1)

	cursor.execute(query1)
	rows = cursor.fetchall()

	results = 0

	threadsList={}

	matching_rows = []
	

	for row in rows:
		json_like_memoryview = row[0]
		json_like_bytes = bytes(json_like_memoryview)  # Convert memoryview to bytes
		json_like_string = json_like_bytes.decode('utf-8')  # Convert bytes to str

		try:
			threadsList[results]=json.loads(json_like_string)
			
		except Exception as e:
			continue
			
		results+=1
		
	
	#threadsList = sorted(threadsList, key=lambda k: threadsList[k]['message'], reverse=True)	
	
	search_term=" ".join(search_term.split())

	if not search_term:
		search_term=' '
	
	response=grab_search.searchTables(session,search_term,threadsList,tic)
	'''

	return response
	

@app.route('/settings', methods=['POST'])
def setPref():
	if 'fwidth' in request.form:
		if 'wide' in session:
			session['wide']=True
	else:
		session['wide']=False

	if 'fwatch' in request.form:
		if 'watch' in session:
			session['watch']=True
	else:
		session['watch']=False
		
	if 'fcompress' in request.form:
		settings.compress_pref=True
	else:
		settings.compress_pref=False

	return redirect(request.headers.get("Referer"), code=302)

@app.route('/watch')
def getWatchList():

	cursor=connection.cursor()

	cursor.execute("SELECT brett FROM 'fadenWache'")
	rowsWatched = cursor.fetchall()

	boards=set()

	for row in rowsWatched:
		boards.add(bytes(row[0]).decode('utf-8'))

	query0=""

	iter=0

	for row in boards:
		if iter==0:
			query0+=f'SELECT fadenWache.id,fadenWache.brett,fadenWache.antworten,{row}.zeit,{row}.gesichert,{row}.nachricht FROM fadenWache INNER JOIN {row} ON fadenWache.id={row}.id'
		else:
			query0+=f' UNION SELECT fadenWache.id,fadenWache.brett,fadenWache.antworten,{row}.zeit,{row}.gesichert,{row}.nachricht FROM fadenWache INNER JOIN {row} ON fadenWache.id={row}.id'

		iter+=1

	if iter>0:
		query0+=' ORDER BY zeit DESC'

	cursor.execute(query0)
	rowsWatched = cursor.fetchall()

	"""
	before=""
	beforeList=[]
	afterList={}
	query1=""

	iter=0

	for row in rowsWatched:
		if iter==0:
			query1+='SELECT "'+str(bytes(row[0]).decode('utf-8'))+'" AS "brett","nachricht","id","antworten","zeit" FROM "'+str(bytes(row[0]).decode('utf-8'))+'" WHERE "id" =='+str(row[1])
		else:
			query1+=' UNION SELECT "'+str(bytes(row[0]).decode('utf-8'))+'" AS "brett","nachricht","id","antworten","zeit" FROM "'+str(bytes(row[0]).decode('utf-8'))+'" WHERE "id" =='+str(row[1])

		iter+=1

	if iter>0:
		query1+=' ORDER BY zeit DESC'
		#print(query1)
	
	cursor.execute(query1)
	rowsDB = cursor.fetchall()

	for row in rowsWatched:
		board=str(bytes(row[0]).decode('utf-8'))

		beforeList.append((str(board),f'{board}:{row[1]}',row[2]))
		before+=str(bytes(row[0]).decode('utf-8'))+' '+str(row[1])+' '+str(row[2])+' <br>'
		#print(str(bytes(row[0]).decode('utf-8'))+' '+str(row[1])+' '+str(row[2])+' '+str(bytes(row[3]).decode('utf-8')))


	for row in rowsDB:
		board=f"{(bytes(row[0])).decode('utf-8')}:{row[2]}"
		afterList[board]=(str(bytes(row[1]).decode('utf-8')),row[3],str(bytes(row[4]).decode('utf-8')))

			#print('match!')
	"""
	response=grab_watch.watchThreads(session,rowsWatched)
	return response
	

@app.route('/<bid>/t/<fid>/<watchpref>')
def doWatch(bid,fid,watchpref):
	
	cursor=connection.cursor()

	cursor.execute(f'SELECT COUNT(*) FROM "fadenWache" WHERE "id"=={fid} AND "brett"=="{bid}"')
	match=cursor.fetchone()

	if match[0] and watchpref=="watch":
		return redirect(request.headers.get("Referer"), code=302)

	cursor.execute(f'SELECT antworten,zeit FROM "{bid}" WHERE "id"=={fid}')
	result = cursor.fetchall()

	time=bytes(result[0][1]).decode("utf-8")

	if watchpref=="watch":
		cursor.execute(f'INSERT INTO "fadenWache" (brett, id, antworten) VALUES ("{bid}", {fid}, {result[0][0]})')
	elif watchpref=="nowatch":
		cursor.execute(f'DELETE FROM "fadenWache" WHERE "id"=={fid} AND "brett"=="{bid}"')

	connection.commit()

	#return('hier:'+str(bytes(result[0][1])))

	return redirect(request.headers.get("Referer"), code=302)



@app.route('/media/<fn>')
@app.route('/.media/<fn>')
def sendImage(fn):
	try:
		return send_file(f'./media/{fn}')
	except Exception as e:
		return str(e)


@app.route('/media/flags/<path:fn>',methods=['GET'])
def sendFlag(fn):
	try:
		flagpath=""
		flagpath=f"./media/flags/{fn}"
		if os.path.exists(flagpath)==False:
			myfile2 = requests.get(f"{settings.board}/.static/flags/{fn}",proxies=settings.proxies,headers=settings.headers)
			print('loading '+flagpath)
			# Assuming flagpath is the full path including the filename
			directory = os.path.dirname(flagpath)

			# Create the directory if it doesn't exist
			os.makedirs(directory, exist_ok=True)
			open(flagpath, 'wb').write(myfile2.content)

			sys.stdout.write('\n'+flagpath+' saved ('+str(round(time.time()))+')')
			sys.stdout.flush()
		elif os.path.exists(flagpath)==True:
			#print('flag already exists')
			pass
		return send_file(f'./media/flags/{fn}')
	except Exception as e:
		return str(e)
	

@app.route('/favicon.ico')
def sendFavicon():
	return send_file(f'./favicon.ico')


@app.errorhandler(500)
def internal_server_error(error):
    return f'This didn\'t work.<br><a href="/">Back to home</a>', 500


thread=Thread()
thread_stop_event=Event()

thread2=Thread()
thread2_stop_event=Event()

class TimerThread(Thread):

	def __init__(self, session, connection, *args, **kwargs):
		self.session = session
		self.connection = connection
		super().__init__(*args, **kwargs)


	def run(self):
		cursor = self.session.get_thread_local_cursor(self.connection)

		if len(sys.argv)<=1:
				sys.argv.insert(1,"none")

		while(True):
			if os.path.exists(settings.filename) and os.path.getsize(settings.filename) > 0 and sys.argv[1]!='serveonly':
				random.shuffle(settings.temp_prefs)
				fetchThreads(cursor,self.connection)
				#print('freeing up memory...')
				gc.collect()
			elif sys.argv[1]!='serveonly':
				connection=sqlite3.connect(settings.filename, check_same_thread=False)
				connection.text_factory = sqlite3.Binary
				cursor=connection.cursor()
				createTable(cursor,connection) # for all prefs	
			else:
				print('Serving threads...')
				try:
					input()
				except EOFError:
					return

class restThread(Thread):

	def __init__(self, session, connection, *args, **kwargs):
		self.session = session
		self.connection = connection
		super().__init__(*args, **kwargs)


	def run(self):
		cursor = self.session.get_thread_local_cursor(self.connection)


	def run(self):

		while(True) and sys.argv[1]!='serveonly':
			saveRest(cursor)
			#print('freeing up memory...')
			gc.collect()


class Session:

	def __init__(self,inst):
		print('init')

	def get_thread_local_cursor(self, connection):
		if not hasattr(_thread_locals, 'cursor'):
			_thread_locals.cursor = connection.cursor()
		return _thread_locals.cursor


if not thread.is_alive():
    print("Starting KC-Archiver")
    first = Session('Main')
    thread = TimerThread(first, connection)
    #setup_fts()
    thread.start()

if not thread2.is_alive():
    second = Session('Second')
    thread2 = restThread(second, connection)
    thread2.start()