from flask import Flask, render_template_string, request, redirect, url_for, session, flash, jsonify
from flask_caching import Cache
import json
import os
import logging
import threading
import time
from datetime import datetime, timedelta
from huggingface_hub import HfApi, hf_hub_download
from werkzeug.utils import secure_filename
import random
app = Flask(__name__)
app.secret_key = os.getenv("FLASK_SECRET_KEY", "supersecretkey")
DATA_FILE = 'data_adusis.json'
REPO_ID = "Eluza133/A12d12s12" # Use your actual repo ID
HF_TOKEN_WRITE = os.getenv("HF_TOKEN")
HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") or HF_TOKEN_WRITE
cache = Cache(app, config={'CACHE_TYPE': 'simple'})
logging.basicConfig(level=logging.INFO)
@cache.memoize(timeout=300)
def load_data():
try:
download_db_from_hf()
with open(DATA_FILE, 'r', encoding='utf-8') as file:
data = json.load(file)
if not isinstance(data, dict):
logging.warning("Data is not in dict format, initializing an empty database")
return {'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}}
data.setdefault('posts', [])
data.setdefault('users', {})
data.setdefault('general_chat', [])
data.setdefault('private_chats', {})
for user in data['users']:
data['users'][user].setdefault('last_chat_visit', '1970-01-01 00:00:00')
data['users'][user].setdefault('last_private_visit', '1970-01-01 00:00:00')
data['users'][user].setdefault('last_seen', '1970-01-01 00:00:00')
logging.info("Data loaded successfully")
return data
except Exception as e:
logging.error(f"Error loading data: {e}")
return {'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}}
def save_data(data):
try:
with open(DATA_FILE, 'w', encoding='utf-8') as file:
json.dump(data, file, ensure_ascii=False, indent=4)
upload_db_to_hf()
cache.clear()
logging.info("Data saved and uploaded to HF")
except Exception as e:
logging.error(f"Error saving data: {e}")
raise
def upload_db_to_hf():
try:
api = HfApi()
api.upload_file(
path_or_fileobj=DATA_FILE,
path_in_repo=DATA_FILE,
repo_id=REPO_ID,
repo_type="dataset",
token=HF_TOKEN_WRITE,
commit_message=f"Backup {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
)
logging.info("Database uploaded to Hugging Face")
except Exception as e:
logging.error(f"Error uploading database: {e}")
def download_db_from_hf():
try:
hf_hub_download(
repo_id=REPO_ID,
filename=DATA_FILE,
repo_type="dataset",
token=HF_TOKEN_READ,
local_dir=".",
local_dir_use_symlinks=False
)
logging.info("Database downloaded from Hugging Face")
except Exception as e:
logging.error(f"Error downloading database: {e}")
if not os.path.exists(DATA_FILE):
with open(DATA_FILE, 'w', encoding='utf-8') as f:
json.dump({'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}}, f)
def periodic_backup():
while True:
upload_db_to_hf()
time.sleep(1800)
def get_unread_count(data, username):
if username not in data['users']:
return 0
last_visit = datetime.strptime(data['users'][username]['last_chat_visit'], '%Y-%m-%d %H:%M:%S')
return sum(1 for msg in data['general_chat'] if datetime.strptime(msg['time'], '%Y-%m-%d %H:%M:%S') > last_visit)
def get_private_unread_count(data, username):
if username not in data['users']:
return 0
last_visit = datetime.strptime(data['users'][username]['last_private_visit'], '%Y-%m-%d %H:%M:%S')
unread = 0
for chat_key, messages in data['private_chats'].items():
users = chat_key.split('_')
if username in users:
unread += sum(1 for msg in messages if datetime.strptime(msg['time'], '%Y-%m-%d %H:%M:%S') > last_visit and msg['sender'] != username)
return unread
def is_user_online(data, username):
if username not in data['users']:
return False
last_seen = datetime.strptime(data['users'][username]['last_seen'], '%Y-%m-%d %H:%M:%S')
return (datetime.now() - last_seen).total_seconds() < 300
def update_last_seen(data, username):
if username in data['users']:
data['users'][username]['last_seen'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
save_data(data)
BASE_STYLE = '''
:root {
--primary: #ff4d6d;
--secondary: #00ddeb;
--accent: #8b5cf6;
--background-light: #f5f6fa;
--background-dark: #1a1625;
--card-bg: rgba(255, 255, 255, 0.95);
--card-bg-dark: rgba(40, 35, 60, 0.95);
--text-light: #2a1e5a;
--text-dark: #e8e1ff;
--shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
--glass-bg: rgba(255, 255, 255, 0.15);
--transition: all 0.3s ease;
--online: #34c759;
--offline: #ff3b30;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Inter', sans-serif;
background: var(--background-light);
color: var(--text-light);
line-height: 1.6;
overflow-x: hidden;
}
body.dark {
background: var(--background-dark);
color: var(--text-dark);
}
.sidebar {
position: fixed;
top: 0;
left: 0;
width: 300px;
height: 100%;
background: var(--glass-bg);
backdrop-filter: blur(20px);
padding: 25px;
box-shadow: var(--shadow);
z-index: 1000;
transition: transform var(--transition);
}
.sidebar.hidden {
transform: translateX(-100%);
}
.sidebar-header {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 35px;
}
.nav-brand {
font-size: 1.8em;
font-weight: 900;
background: linear-gradient(135deg, var(--primary), var(--accent));
-webkit-background-clip: text;
color: transparent;
}
.logo {
width: 40px;
height: 40px;
border-radius: 14px;
box-shadow: var(--shadow);
}
.nav-links {
display: flex;
flex-direction: column;
gap: 15px;
}
.nav-link {
display: flex;
align-items: center;
gap: 15px;
padding: 14px 25px;
background: var(--card-bg);
color: var(--text-light);
text-decoration: none;
border-radius: 14px;
font-size: 1.1em;
font-weight: 600;
transition: var(--transition);
position: relative;
}
body.dark .nav-link {
background: var(--card-bg-dark);
color: var(--text-dark);
}
.nav-link:hover {
transform: translateX(5px);
background: var(--primary);
color: white;
box-shadow: 0 6px 20px rgba(255, 77, 109, 0.4);
}
.nav-link .badge {
position: absolute;
right: 15px;
background: var(--secondary);
color: white;
padding: 4px 10px;
border-radius: 12px;
font-size: 0.8em;
font-weight: 700;
}
.logout-btn {
background: var(--secondary);
color: white;
}
.logout-btn:hover {
background: #00b8c5;
}
.menu-btn {
display: none;
font-size: 28px;
background: var(--glass-bg);
border: none;
color: var(--primary);
cursor: pointer;
position: fixed;
top: 20px;
left: 20px;
z-index: 1001;
padding: 12px;
border-radius: 50%;
box-shadow: var(--shadow);
transition: var(--transition);
}
.menu-btn:hover {
background: var(--primary);
color: white;
}
.container {
margin: 20px auto 20px 320px;
max-width: 1200px;
padding: 25px;
transition: var(--transition);
}
.btn {
padding: 14px 28px;
background: var(--primary);
color: white;
border: none;
border-radius: 14px;
cursor: pointer;
font-size: 1.1em;
font-weight: 600;
transition: var(--transition);
display: inline-flex;
align-items: center;
gap: 10px;
box-shadow: var(--shadow);
}
.btn:hover {
transform: scale(1.05);
background: #e6415f;
box-shadow: 0 8px 25px rgba(255, 77, 109, 0.5);
}
input, textarea, select {
width: 100%;
padding: 14px;
margin: 12px 0;
border: none;
border-radius: 14px;
background: var(--glass-bg);
color: var(--text-light);
font-size: 1.1em;
transition: var(--transition);
box-shadow: inset 0 3px 10px rgba(0, 0, 0, 0.1);
}
body.dark input, body.dark textarea, body.dark select {
color: var(--text-dark);
}
input:focus, textarea:focus, select:focus {
outline: none;
background: rgba(255, 255, 255, 0.2);
box-shadow: 0 0 0 4px var(--primary);
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.85);
z-index: 2000;
justify-content: center;
align-items: center;
transition: opacity var(--transition);
}
.modal img, .modal video {
max-width: 95%;
max-height: 95%;
object-fit: contain;
border-radius: 20px;
box-shadow: var(--shadow);
animation: zoomIn 0.3s ease;
}
.theme-toggle {
position: fixed;
top: 20px;
right: 20px;
background: var(--glass-bg);
border: none;
padding: 12px;
border-radius: 50%;
cursor: pointer;
font-size: 24px;
box-shadow: var(--shadow);
transition: var(--transition);
}
.theme-toggle:hover {
transform: rotate(90deg);
background: var(--accent);
color: white;
}
.status-dot {
width: 10px;
height: 10px;
border-radius: 50%;
display: inline-block;
margin-left: 8px;
}
.online { background: var(--online); }
.offline { background: var(--offline); }
@keyframes zoomIn {
from { opacity: 0; transform: scale(0.9); }
to { opacity: 1; transform: scale(1); }
}
@media (max-width: 900px) {
.sidebar {
width: 100%;
max-width: 300px;
transform: translateX(-100%);
}
.sidebar.active {
transform: translateX(0);
}
.menu-btn {
display: block;
}
.container {
margin: 80px 20px 20px 20px;
max-width: calc(100% - 40px);
}
.theme-toggle {
top: 80px;
}
}
@media (max-width: 480px) {
.nav-brand { font-size: 1.5em; }
.nav-link { font-size: 1em; padding: 12px 20px; }
.btn { padding: 12px 20px; font-size: 1em; }
}
'''
NAV_HTML = '''
'''
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
data = load_data()
if username in data['users']:
flash('User already exists!')
return redirect(url_for('register'))
data['users'][username] = {
'password': password,
'bio': '',
'link': '',
'avatar': None,
'last_chat_visit': '1970-01-01 00:00:00',
'last_private_visit': '1970-01-01 00:00:00',
'last_seen': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
}
save_data(data)
flash('Registration successful! Please login.')
return redirect(url_for('login'))
is_authenticated = 'username' in session
username = session.get('username', None)
data = load_data()
unread_count = get_unread_count(data, username) if is_authenticated else 0
private_unread_count = get_private_unread_count(data, username) if is_authenticated else 0
user_count = len(data['users'])
is_online = is_user_online(data, username) if is_authenticated else False
html = '''
Register - Content Hub
''' + NAV_HTML + '''
🌙
Register
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
{{ message }}
{% endfor %}
{% endif %}
{% endwith %}
Already have an account? Login
'''
return render_template_string(html, is_authenticated=is_authenticated, username=username, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online)
@app.route('/login', methods=['GET', 'POST'])
def login():
data = load_data()
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
if username in data['users'] and data['users'][username]['password'] == password:
session['username'] = username
session.permanent = True
update_last_seen(data, username)
return redirect(url_for('feed'))
flash('Invalid username or password!')
return redirect(url_for('login'))
is_authenticated = 'username' in session
username = session.get('username', None)
if is_authenticated:
update_last_seen(data, username)
unread_count = get_unread_count(data, username) if is_authenticated else 0
private_unread_count = get_private_unread_count(data, username) if is_authenticated else 0
user_count = len(data['users'])
is_online = is_user_online(data, username) if is_authenticated else False
html = '''
Login - Content Hub
''' + NAV_HTML + '''
🌙
Login
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
{{ message }}
{% endfor %}
{% endif %}
{% endwith %}
Don't have an account? Register
'''
return render_template_string(html, is_authenticated=is_authenticated, username=username, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online)
@app.route('/logout')
def logout():
data = load_data()
username = session.get('username', None)
if username:
update_last_seen(data, username)
session.pop('username', None)
return redirect(url_for('feed'))
@app.route('/toggle_like/', methods=['POST'])
def toggle_like(post_id):
if 'username' not in session:
return jsonify({'error': 'Not authenticated'}), 401
data = load_data()
username = session['username']
post = next((p for p in data['posts'] if p['id'] == post_id), None)
if not post:
return jsonify({'error': 'Post not found'}), 404
likes = post.get('likes', [])
if username in likes:
post['likes'] = [u for u in likes if u != username]
liked = False
else:
post['likes'] = likes + [username]
liked = True
save_data(data)
return jsonify({'liked': liked, 'likes_count': len(post['likes'])})
@app.route('/increment_view/', methods=['POST'])
def increment_view(post_id):
data = load_data()
post = next((p for p in data['posts'] if p['id'] == post_id), None)
if post:
post['views'] = post.get('views', 0) + 1
save_data(data)
return jsonify({'views': post['views']})
return jsonify({'error': 'Post not found'}), 404
@app.route('/', methods=['GET', 'POST'])
def feed():
data = load_data()
username = session.get('username', None)
if username:
update_last_seen(data, username)
posts = sorted(data.get('posts', []), key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True)
is_authenticated = 'username' in session
unread_count = get_unread_count(data, username) if is_authenticated else 0
private_unread_count = get_private_unread_count(data, username) if is_authenticated else 0
user_count = len(data['users'])
is_online = is_user_online(data, username) if is_authenticated else False
search_query = request.args.get('search', '').strip().lower() # Get search query from URL parameters
if search_query:
posts = [post for post in posts if search_query in post['title'].lower() or search_query in post['description'].lower()]
if request.method == 'POST' and is_authenticated:
post_id = request.form.get('post_id')
post = next((p for p in data['posts'] if p['id'] == post_id), None)
if not post:
return "Post not found", 404
if 'comment' in request.form:
comment_text = request.form.get('comment')
if comment_text:
post['comments'] = post.get('comments', []) + [{
'user': username,
'text': comment_text,
'date': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
}]
# Removed share handling
save_data(data)
return redirect(url_for('feed', search=search_query)) # Maintain search query
html = '''
Adusis - QoS, BBC, BNWO HUB
''' + NAV_HTML + '''
🌙
{% for post in posts %}
{% if post['type'] == 'video' %}
▶
{% else %}
{% endif %}
{{ post['title'] }}
{{ post['description']|truncate(150) }}
By: {{ post['uploader'] }}
♥
{{ post['likes']|length }}
💬
{{ post['comments']|length }}
➤
{{ post['views'] }}
{% endfor %}
'''
return render_template_string(html,
posts=posts,
is_authenticated=is_authenticated,
username=username,
repo_id=REPO_ID,
unread_count=unread_count,
user_count=user_count,
private_unread_count=private_unread_count,
is_online=is_online,
is_user_online=lambda u: is_user_online(data, u),
data=data)
@app.route('/post/', methods=['GET', 'POST'])
def post_page(post_id):
data = load_data()
username = session.get('username', None)
if username:
update_last_seen(data, username)
post = next((p for p in data['posts'] if p['id'] == post_id), None)
if not post:
return "Post not found", 404
is_authenticated = 'username' in session
unread_count = get_unread_count(data, username) if is_authenticated else 0
private_unread_count = get_private_unread_count(data, username) if is_authenticated else 0
user_count = len(data['users'])
is_online = is_user_online(data, username) if is_authenticated else False
post['views'] = post.get('views', 0) + 1
save_data(data)
if request.method == 'POST' and is_authenticated:
if 'comment' in request.form:
comment_text = request.form.get('comment')
if comment_text:
post['comments'] = post.get('comments', []) + [{'user': username, 'text': comment_text, 'date': datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]
save_data(data)
html = '''
{{ post['title'] }} - Content Hub
''' + NAV_HTML + '''
🌙
{{ post['title'] }}
{% if post['type'] == 'video' %}
{% else %}
{% endif %}
{{ post['description'] }}
By: {{ post['uploader'] }} | {{ post['upload_date'] }}
Views: {{ post['views'] }} | Likes: {{ post['likes']|length }}
{% if is_authenticated %}
{% if username in post['likes'] %}Unlike{% else %}Like{% endif %}
{% endif %}
Comments
{% for comment in post.get('comments', []) %}
{% endfor %}
Back to Feed
{% if not is_authenticated %}
Login to like and comment.
{% endif %}
'''
return render_template_string(html, post=post, repo_id=REPO_ID, is_authenticated=is_authenticated, username=username, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online, is_user_online=lambda u: is_user_online(data, u))
@app.route('/profile', methods=['GET', 'POST'])
def profile():
if 'username' not in session:
flash('Login to view your profile!')
return redirect(url_for('login'))
data = load_data()
username = session['username']
update_last_seen(data, username)
user_posts = sorted([p for p in data['posts'] if p['uploader'] == username], key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True)
is_authenticated = 'username' in session
unread_count = get_unread_count(data, username)
private_unread_count = get_private_unread_count(data, username)
user_count = len(data['users'])
is_online = is_user_online(data, username)
total_views = sum(post.get('views', 0) for post in user_posts)
total_likes = sum(len(post.get('likes', [])) for post in user_posts)
user_data = data['users'].get(username, {})
bio = user_data.get('bio', '')
link = user_data.get('link', '')
avatar = user_data.get('avatar', None)
last_seen = user_data.get('last_seen', 'Never')
if request.method == 'POST':
if 'delete_post' in request.form:
post_id = request.form.get('post_id')
if post_id:
data['posts'] = [p for p in data['posts'] if p['id'] != post_id or p['uploader'] != username]
save_data(data)
return redirect(url_for('profile'))
elif 'update_profile' in request.form:
bio = request.form.get('bio', '')
link = request.form.get('link', '')
avatar_file = request.files.get('avatar')
if avatar_file and avatar_file.filename:
filename = secure_filename(avatar_file.filename)
temp_path = os.path.join('uploads', filename)
os.makedirs('uploads', exist_ok=True)
avatar_file.save(temp_path)
api = HfApi()
avatar_path = f"avatars/{username}/{filename}"
api.upload_file(
path_or_fileobj=temp_path,
path_in_repo=avatar_path,
repo_id=REPO_ID,
repo_type="dataset",
token=HF_TOKEN_WRITE,
commit_message=f"Uploaded avatar for {username}"
)
data['users'][username]['avatar'] = avatar_path
if os.path.exists(temp_path):
os.remove(temp_path)
data['users'][username]['bio'] = bio
data['users'][username]['link'] = link
save_data(data)
return redirect(url_for('profile'))
html = '''
Profile - {{ username }} - Content Hub
''' + NAV_HTML + '''
🌙
'''
return render_template_string(html,
username=username,
user_posts=user_posts,
bio=bio,
link=link,
avatar=avatar,
total_views=total_views,
total_likes=total_likes,
last_seen=last_seen,
is_authenticated=is_authenticated,
repo_id=REPO_ID,
unread_count=unread_count,
user_count=user_count,
private_unread_count=private_unread_count,
is_online=is_online)
@app.route('/user/', methods=['GET'])
def user_profile(username):
data = load_data()
current_user = session.get('username', None)
if current_user:
update_last_seen(data, current_user)
if username not in data['users']:
return "User not found", 404
user_posts = sorted([p for p in data['posts'] if p['uploader'] == username], key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True)
is_authenticated = 'username' in session
unread_count = get_unread_count(data, current_user) if is_authenticated else 0
private_unread_count = get_private_unread_count(data, current_user) if is_authenticated else 0
user_count = len(data['users'])
is_online = is_user_online(data, current_user) if is_authenticated else False
total_views = sum(post.get('views', 0) for post in user_posts)
total_likes = sum(len(post.get('likes', [])) for post in user_posts)
user_data = data['users'].get(username, {})
bio = user_data.get('bio', '')
link = user_data.get('link', '')
avatar = user_data.get('avatar', None)
last_seen = user_data.get('last_seen', 'Never')
user_online = is_user_online(data, username)
html = '''
{{ username }}'s Profile - Content Hub
''' + NAV_HTML + '''
🌙
'''
return render_template_string(html,
username=username,
user_posts=user_posts,
bio=bio,
link=link,
avatar=avatar,
total_views=total_views,
total_likes=total_likes,
last_seen=last_seen,
is_authenticated=is_authenticated,
current_user=current_user,
repo_id=REPO_ID,
unread_count=unread_count,
user_count=user_count,
private_unread_count=private_unread_count,
is_online=is_online,
user_online=user_online)
@app.route('/upload', methods=['GET', 'POST'])
def upload():
if 'username' not in session:
flash('Login to upload content!')
return redirect(url_for('login'))
data = load_data()
username = session['username']
update_last_seen(data, username)
is_authenticated = 'username' in session
unread_count = get_unread_count(data, username)
private_unread_count = get_private_unread_count(data, username)
user_count = len(data['users'])
is_online = is_user_online(data, username)
if request.method == 'POST':
title = request.form.get('title')
description = request.form.get('description')
file = request.files.get('file')
if not file or not title:
flash('Title and file are required!')
return redirect(url_for('upload'))
filename = secure_filename(file.filename)
temp_path = os.path.join('uploads', filename)
os.makedirs('uploads', exist_ok=True)
file.save(temp_path)
file_type = 'video' if filename.lower().endswith(('.mp4', '.mov', '.avi')) else 'photo'
api = HfApi()
file_path = f"{file_type}s/{username}/{filename}"
api.upload_file(
path_or_fileobj=temp_path,
path_in_repo=file_path,
repo_id=REPO_ID,
repo_type="dataset",
token=HF_TOKEN_WRITE,
commit_message=f"Uploaded {file_type} by {username}"
)
post_id = str(random.randint(100000, 999999))
while any(p['id'] == post_id for p in data['posts']):
post_id = str(random.randint(100000, 999999))
data['posts'].append({
'id': post_id,
'title': title,
'description': description,
'filename': f"{username}/{filename}",
'type': file_type,
'uploader': username,
'upload_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'views': 0,
'likes': [],
'comments': []
})
save_data(data)
if os.path.exists(temp_path):
os.remove(temp_path)
return redirect(url_for('profile'))
html = '''
Upload Content - Content Hub
''' + NAV_HTML + '''
🌙
'''
return render_template_string(html,
username=username,
is_authenticated=is_authenticated,
repo_id=REPO_ID,
unread_count=unread_count,
user_count=user_count,
private_unread_count=private_unread_count,
is_online=is_online)
@app.route('/chat', methods=['GET', 'POST'])
def chat():
data = load_data()
username = session.get('username', None)
if username:
update_last_seen(data, username)
is_authenticated = 'username' in session
chat_messages = data['general_chat']
if is_authenticated:
data['users'][username]['last_chat_visit'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
save_data(data)
unread_count = get_unread_count(data, username) if is_authenticated else 0
private_unread_count = get_private_unread_count(data, username) if is_authenticated else 0
user_count = len(data['users'])
is_online = is_user_online(data, username) if is_authenticated else False
if request.method == 'POST' and is_authenticated:
message = request.form.get('message')
file = request.files.get('file')
post_id = request.form.get('post_id')
message_data = {
'sender': username,
'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
}
if message:
message_data['text'] = message
if file and file.filename:
filename = secure_filename(file.filename)
temp_path = os.path.join('uploads', filename)
os.makedirs('uploads', exist_ok=True)
file.save(temp_path)
file_type = 'video' if filename.lower().endswith(('.mp4', '.mov', '.avi')) else 'photo'
api = HfApi()
file_path = f"chat_files/general/{filename}"
api.upload_file(
path_or_fileobj=temp_path,
path_in_repo=file_path,
repo_id=REPO_ID,
repo_type="dataset",
token=HF_TOKEN_WRITE,
commit_message=f"Uploaded chat file by {username}"
)
message_data['file'] = file_path
message_data['file_type'] = file_type
if os.path.exists(temp_path):
os.remove(temp_path)
if post_id:
post = next((p for p in data['posts'] if p['id'] == post_id and p['uploader'] == username), None)
if post:
message_data['post_id'] = post_id
if 'text' in message_data or 'file' in message_data or 'post_id' in message_data:
data['general_chat'].append(message_data)
save_data(data)
return redirect(url_for('chat'))
html = '''
General Chat - Content Hub
''' + NAV_HTML + '''
🌙
General Chat
{% for message in chat_messages %}
{% if message['sender'] == username %}
{% if avatar %}
{% else %}
{% endif %}
{% else %}
{% if data['users'][message['sender']].get('avatar') %}
{% else %}
{% endif %}
{% endif %}
{{ message['sender'] }}
{% if 'text' in message %}
{{ message['text'] }}
{% endif %}
{% if 'file' in message %}
{% if message['file_type'] == 'video' %}
{% else %}
{% endif %}
{% endif %}
{% if 'post_id' in message %}
{% with post = posts|selectattr('id', 'equalto', message['post_id'])|first %}
{% if post %}
Shared: {{ post['title'] }}
{% endif %}
{% endwith %}
{% endif %}
{{ message['time'] }}
{% endfor %}
{% if is_authenticated %}
Share a post (optional)
{% for post in user_posts %}
{{ post['title'] }}
{% endfor %}
Send
{% else %}
Login to send messages.
{% endif %}
'''
user_posts = [p for p in data['posts'] if p['uploader'] == username] if is_authenticated else []
avatar = data['users'][username].get('avatar') if is_authenticated else None
return render_template_string(html,
chat_messages=chat_messages,
username=username,
is_authenticated=is_authenticated,
repo_id=REPO_ID,
posts=data['posts'],
user_posts=user_posts,
unread_count=unread_count,
user_count=user_count,
private_unread_count=private_unread_count,
is_online=is_online,
is_user_online=lambda u: is_user_online(data, u),
data=data,
avatar=avatar)
@app.route('/users', methods=['GET', 'POST'])
def users():
data = load_data()
username = session.get('username', None)
if username:
update_last_seen(data, username)
is_authenticated = 'username' in session
unread_count = get_unread_count(data, username) if is_authenticated else 0
private_unread_count = get_private_unread_count(data, username) if is_authenticated else 0
user_count = len(data['users'])
is_online = is_user_online(data, username) if is_authenticated else False
search_query = request.form.get('search', '').strip().lower() if request.method == 'POST' else ''
user_list = [(user, data['users'][user].get('avatar'), is_user_online(data, user))
for user in data['users'] if not search_query or search_query in user.lower()]
html = '''
Users - Content Hub
''' + NAV_HTML + '''
🌙
Users ({{ user_count }})
🔍
{% for user, avatar, online in user_list %}
{% if user != username %}
{% if avatar %}
{% else %}
{% endif %}
{% endif %}
{% endfor %}
'''
return render_template_string(html,
user_list=user_list,
username=username,
is_authenticated=is_authenticated,
repo_id=REPO_ID,
unread_count=unread_count,
user_count=user_count,
private_unread_count=private_unread_count,
is_online=is_online,
search_query=search_query)
@app.route('/messages', methods=['GET'])
def messages():
if 'username' not in session:
flash('Login to view messages!')
return redirect(url_for('login'))
data = load_data()
username = session['username']
update_last_seen(data, username)
is_authenticated = 'username' in session
unread_count = get_unread_count(data, username)
private_unread_count = get_private_unread_count(data, username)
user_count = len(data['users'])
is_online = is_user_online(data, username)
dialogs = {}
for chat_key, messages in data['private_chats'].items():
user1, user2 = chat_key.split('_')
if username not in (user1, user2):
continue
other_user = user1 if user2 == username else user2
last_message = messages[-1] if messages else None
unread = sum(1 for msg in messages if datetime.strptime(msg['time'], '%Y-%m-%d %H:%M:%S') > datetime.strptime(data['users'][username]['last_private_visit'], '%Y-%m-%d %H:%M:%S') and msg['sender'] != username)
dialogs[other_user] = {
'last_message': last_message,
'unread': unread,
'avatar': data['users'][other_user].get('avatar'),
'online': is_user_online(data, other_user)
}
html = '''
Messages - Content Hub
''' + NAV_HTML + '''
🌙
Messages
{% for other_user, info in dialogs.items() %}
{% if info.avatar %}
{% else %}
{% endif %}
{{ other_user }}
{% if info.last_message %}
{{ info.last_message['time'] }}:
{% if 'text' in info.last_message %}{{ info.last_message['text']|truncate(30) }}{% elif 'file' in info.last_message %}File{% elif 'post_id' in info.last_message %}Post{% endif %}
{% endif %}
{% if info.unread > 0 %}
{{ info.unread }}
{% endif %}
{% endfor %}
{% if not dialogs %}
No messages yet.
{% endif %}
'''
return render_template_string(html,
dialogs=dialogs,
username=username,
is_authenticated=is_authenticated,
repo_id=REPO_ID,
unread_count=unread_count,
user_count=user_count,
private_unread_count=private_unread_count,
is_online=is_online)
@app.route('/chat/', methods=['GET', 'POST'])
def private_chat(recipient):
if 'username' not in session:
flash('Login to start a chat!')
return redirect(url_for('login'))
data = load_data()
username = session['username']
update_last_seen(data, username)
if recipient not in data['users']:
return "User not found", 404
is_authenticated = 'username' in session
unread_count = get_unread_count(data, username)
private_unread_count = get_private_unread_count(data, username)
user_count = len(data['users'])
is_online = is_user_online(data, username)
data['users'][username]['last_private_visit'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
save_data(data)
chat_key = f"{min(username, recipient)}_{max(username, recipient)}"
if chat_key not in data['private_chats']:
data['private_chats'][chat_key] = []
private_messages = data['private_chats'][chat_key]
if request.method == 'POST':
message = request.form.get('message')
file = request.files.get('file')
post_id = request.form.get('post_id')
message_data = {
'sender': username,
'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
}
if message:
message_data['text'] = message
if file and file.filename:
filename = secure_filename(file.filename)
temp_path = os.path.join('uploads', filename)
os.makedirs('uploads', exist_ok=True)
file.save(temp_path)
file_type = 'video' if filename.lower().endswith(('.mp4', '.mov', '.avi')) else 'photo'
api = HfApi()
file_path = f"chat_files/private/{chat_key}/{filename}"
api.upload_file(
path_or_fileobj=temp_path,
path_in_repo=file_path,
repo_id=REPO_ID,
repo_type="dataset",
token=HF_TOKEN_WRITE,
commit_message=f"Uploaded private chat file by {username} to {recipient}"
)
message_data['file'] = file_path
message_data['file_type'] = file_type
if os.path.exists(temp_path):
os.remove(temp_path)
if post_id:
post = next((p for p in data['posts'] if p['id'] == post_id and p['uploader'] == username), None)
if post:
message_data['post_id'] = post_id
if 'text' in message_data or 'file' in message_data or 'post_id' in message_data:
data['private_chats'][chat_key].append(message_data)
save_data(data)
return redirect(url_for('private_chat', recipient=recipient))
recipient_avatar = data['users'][recipient].get('avatar')
recipient_online = is_user_online(data, recipient)
avatar = data['users'][username].get('avatar') if is_authenticated else None
html = '''
Chat with {{ recipient }} - Content Hub
''' + NAV_HTML + '''
🌙
{% if recipient_avatar %}
{% else %}
{% endif %}
Chat with {{ recipient }}
{% for message in private_messages %}
{% if message['sender'] == username %}
{% if avatar %}
{% else %}
{% endif %}
{% else %}
{% if recipient_avatar %}
{% endif %}
{% endif %}
{{ message['sender'] }}
{% if 'text' in message %}
{{ message['text'] }}
{% endif %}
{% if 'file' in message %}
{% if message['file_type'] == 'video' %}
{% else %}
{% endif %}
{% endif %}
{% if 'post_id' in message %}
{% with post = posts|selectattr('id', 'equalto', message['post_id'])|first %}
{% if post %}
Shared: {{ post['title'] }}
{% endif %}
{% endwith %}
{% endif %}
{{ message['time'] }}
{% endfor %}
Share a post (optional)
{% for post in user_posts %}
{{ post['title'] }}
{% endfor %}
Send
'''
user_posts = [p for p in data['posts'] if p['uploader'] == username]
return render_template_string(html,
private_messages=private_messages,
recipient=recipient,
username=username,
is_authenticated=is_authenticated,
repo_id=REPO_ID,
posts=data['posts'],
user_posts=user_posts,
unread_count=unread_count,
user_count=user_count,
private_unread_count=private_unread_count,
is_online=is_online,
is_user_online=lambda u: is_user_online(data, u),
recipient_avatar=recipient_avatar,
recipient_online=recipient_online,
avatar=avatar)
if __name__ == '__main__':
threading.Thread(target=periodic_backup, daemon=True).start()
app.run(debug=False, host='0.0.0.0', port=7860)