diff --git "a/app (6).py" "b/app (6).py"
new file mode 100644--- /dev/null
+++ "b/app (6).py"
@@ -0,0 +1,2997 @@
+
+
+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", "verysecretkey")
+app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=30)
+DATA_FILE = 'data_adusis.json'
+REPO_ID = "Eluza133/A12d12s12"
+HF_TOKEN_WRITE = os.getenv("HF_TOKEN")
+HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") or HF_TOKEN_WRITE
+UPLOAD_FOLDER = 'uploads'
+os.makedirs(UPLOAD_FOLDER, exist_ok=True)
+
+cache = Cache(app, config={'CACHE_TYPE': 'simple'})
+logging.basicConfig(level=logging.INFO)
+
+def initialize_data_structure(data):
+ if not isinstance(data, dict):
+ logging.warning("Data is not a dict, initializing empty database")
+ return {'posts': [], 'users': {}}
+
+ data.setdefault('posts', [])
+ data.setdefault('users', {})
+
+ for post in data['posts']:
+ post.setdefault('likes', [])
+ post.setdefault('views', 0)
+ post.setdefault('comments', [])
+ post.setdefault('jerked_off_count', 0)
+ post.setdefault('id', str(random.randint(100000, 999999)))
+
+ for user in data['users']:
+ data['users'][user].setdefault('last_seen', '1970-01-01 00:00:00')
+ data['users'][user].setdefault('bio', '')
+ data['users'][user].setdefault('link', '')
+ data['users'][user].setdefault('avatar', None)
+
+ return data
+
+@cache.memoize(timeout=120)
+def load_data():
+ try:
+ download_db_from_hf()
+ if os.path.exists(DATA_FILE) and os.path.getsize(DATA_FILE) > 0:
+ with open(DATA_FILE, 'r', encoding='utf-8') as file:
+ data = json.load(file)
+ else:
+ data = {'posts': [], 'users': {}}
+
+ data = initialize_data_structure(data)
+ logging.info("Data loaded successfully")
+ return data
+ except json.JSONDecodeError:
+ logging.error(f"Error decoding JSON from {DATA_FILE}. Initializing empty data.")
+ return initialize_data_structure({})
+ except Exception as e:
+ logging.error(f"Error loading data: {e}")
+ return initialize_data_structure({})
+
+def save_data(data):
+ try:
+ temp_file = DATA_FILE + '.tmp'
+ with open(temp_file, 'w', encoding='utf-8') as file:
+ json.dump(data, file, ensure_ascii=False, indent=4)
+ os.replace(temp_file, DATA_FILE)
+ 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}")
+ if os.path.exists(temp_file):
+ os.remove(temp_file)
+ raise
+
+def upload_db_to_hf():
+ if not HF_TOKEN_WRITE:
+ logging.warning("HF_TOKEN_WRITE not set. Skipping upload.")
+ return
+ 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():
+ if not HF_TOKEN_READ:
+ logging.warning("HF_TOKEN_READ not set. Skipping download.")
+ if not os.path.exists(DATA_FILE):
+ with open(DATA_FILE, 'w', encoding='utf-8') as f:
+ json.dump(initialize_data_structure({}), f)
+ return
+ 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,
+ force_download=True
+ )
+ 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):
+ logging.info("Creating empty database file as download failed and file doesn't exist.")
+ with open(DATA_FILE, 'w', encoding='utf-8') as f:
+ json.dump(initialize_data_structure({}), f)
+
+def periodic_backup():
+ while True:
+ time.sleep(1800)
+ logging.info("Initiating periodic backup.")
+ try:
+ data = load_data()
+ save_data(data)
+ except Exception as e:
+ logging.error(f"Error during periodic backup: {e}")
+
+def is_user_online(data, username):
+ if username not in data.get('users', {}):
+ return False
+ last_seen_str = data['users'][username].get('last_seen', '1970-01-01 00:00:00')
+ try:
+ last_seen = datetime.strptime(last_seen_str, '%Y-%m-%d %H:%M:%S')
+ return (datetime.now() - last_seen).total_seconds() < 300
+ except ValueError:
+ logging.error(f"Invalid last_seen format for user {username}: {last_seen_str}")
+ return False
+
+def update_last_seen(username):
+ if username:
+ try:
+ data = load_data()
+ if username in data['users']:
+ data['users'][username]['last_seen'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+ save_data(data)
+ else:
+ logging.warning(f"Attempted to update last_seen for non-existent user: {username}")
+ except Exception as e:
+ logging.error(f"Error updating last_seen for {username}: {e}")
+
+@app.before_request
+def before_request_func():
+ if 'username' in session:
+ update_last_seen(session['username'])
+
+BASE_STYLE = '''
+:root {
+ --primary: #ff1f8f;
+ --secondary: #a0f;
+ --accent: #ffcc00;
+ --background-light: #1a1a1a;
+ --background-dark: #0d0d0d;
+ --card-bg: rgba(30, 30, 30, 0.9);
+ --card-bg-dark: rgba(15, 15, 15, 0.95);
+ --text-light: #f0f0f0;
+ --text-dark: #cccccc;
+ --shadow: 0 8px 25px rgba(255, 31, 143, 0.3);
+ --glass-bg: rgba(40, 40, 40, 0.5);
+ --transition: all 0.3s ease;
+ --online: #0f0;
+ --offline: #ff3b30;
+ --like-color: #ff1f8f;
+ --jerk-color: #ffcc00;
+}
+* { margin: 0; padding: 0; box-sizing: border-box; }
+body {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ background: var(--background-dark);
+ color: var(--text-light);
+ line-height: 1.6;
+ overflow-x: hidden;
+}
+body.dark {
+ background: var(--background-dark);
+ color: var(--text-light);
+}
+.sidebar {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 280px;
+ height: 100%;
+ background: var(--card-bg-dark);
+ backdrop-filter: blur(10px);
+ padding: 20px;
+ box-shadow: 5px 0 15px rgba(0, 0, 0, 0.5);
+ z-index: 1000;
+ transition: transform var(--transition);
+ border-right: 1px solid var(--primary);
+}
+.sidebar.hidden {
+ transform: translateX(-100%);
+}
+.sidebar-header {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ margin-bottom: 30px;
+ border-bottom: 1px solid var(--primary);
+ padding-bottom: 15px;
+}
+.nav-brand {
+ font-size: 1.6em;
+ font-weight: 700;
+ color: var(--primary);
+ text-shadow: 0 0 10px var(--primary);
+}
+.logo {
+ width: 45px;
+ height: 45px;
+ border-radius: 10px;
+ border: 2px solid var(--primary);
+}
+.nav-links {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+.nav-link {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 12px 20px;
+ background: transparent;
+ color: var(--text-light);
+ text-decoration: none;
+ border-radius: 8px;
+ font-size: 1.05em;
+ font-weight: 500;
+ transition: var(--transition);
+ border-left: 3px solid transparent;
+}
+body.dark .nav-link {
+ color: var(--text-light);
+}
+.nav-link:hover, .nav-link.active {
+ background: var(--glass-bg);
+ color: var(--primary);
+ border-left: 3px solid var(--primary);
+ transform: translateX(5px);
+}
+.nav-link span:first-child {
+ font-size: 1.2em;
+}
+.logout-btn {
+ color: var(--secondary);
+}
+.logout-btn:hover {
+ background: var(--secondary);
+ color: white;
+ border-left: 3px solid var(--secondary);
+}
+.menu-btn {
+ display: none;
+ font-size: 26px;
+ background: var(--card-bg-dark);
+ border: 1px solid var(--primary);
+ color: var(--primary);
+ cursor: pointer;
+ position: fixed;
+ top: 15px;
+ left: 15px;
+ z-index: 1001;
+ padding: 10px;
+ border-radius: 50%;
+ box-shadow: var(--shadow);
+ transition: var(--transition);
+}
+.menu-btn:hover {
+ background: var(--primary);
+ color: var(--background-dark);
+}
+.container {
+ margin: 20px auto 20px 300px;
+ max-width: 1100px;
+ padding: 20px;
+ transition: margin-left var(--transition);
+}
+.btn {
+ padding: 12px 25px;
+ background: var(--primary);
+ color: white;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 1em;
+ font-weight: 600;
+ transition: var(--transition);
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ box-shadow: 0 4px 15px rgba(255, 31, 143, 0.4);
+ text-transform: uppercase;
+ letter-spacing: 1px;
+}
+.btn:hover {
+ transform: translateY(-2px);
+ background: #e01b7e;
+ box-shadow: var(--shadow);
+}
+.btn-secondary {
+ background: var(--secondary);
+ box-shadow: 0 4px 15px rgba(170, 0, 255, 0.4);
+}
+.btn-secondary:hover {
+ background: #90f;
+ box-shadow: 0 6px 20px rgba(170, 0, 255, 0.5);
+}
+.btn-accent {
+ background: var(--accent);
+ color: var(--background-dark);
+ box-shadow: 0 4px 15px rgba(255, 204, 0, 0.4);
+}
+.btn-accent:hover {
+ background: #e6b800;
+ box-shadow: 0 6px 20px rgba(255, 204, 0, 0.5);
+}
+input, textarea, select {
+ width: 100%;
+ padding: 12px 15px;
+ margin: 10px 0;
+ border: 1px solid var(--glass-bg);
+ border-radius: 8px;
+ background: var(--glass-bg);
+ color: var(--text-light);
+ font-size: 1em;
+ transition: var(--transition);
+}
+body.dark input, body.dark textarea, body.dark select {
+ color: var(--text-light);
+}
+input:focus, textarea:focus, select:focus {
+ outline: none;
+ border-color: var(--primary);
+ background: rgba(50, 50, 50, 0.5);
+ box-shadow: 0 0 0 3px rgba(255, 31, 143, 0.3);
+}
+textarea {
+ min-height: 100px;
+ resize: vertical;
+}
+.modal {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.9);
+ z-index: 2000;
+ justify-content: center;
+ align-items: center;
+ transition: opacity var(--transition);
+}
+.modal img, .modal video {
+ max-width: 90%;
+ max-height: 90%;
+ object-fit: contain;
+ border-radius: 10px;
+ box-shadow: 0 0 30px rgba(255, 31, 143, 0.5);
+ animation: zoomIn 0.3s ease;
+}
+.theme-toggle {
+ position: fixed;
+ top: 15px;
+ right: 15px;
+ background: var(--glass-bg);
+ border: 1px solid var(--secondary);
+ padding: 10px;
+ border-radius: 50%;
+ cursor: pointer;
+ font-size: 20px;
+ color: var(--secondary);
+ box-shadow: var(--shadow);
+ transition: var(--transition);
+ z-index: 1001;
+}
+.theme-toggle:hover {
+ transform: rotate(180deg);
+ background: var(--secondary);
+ color: white;
+}
+.status-dot {
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+ display: inline-block;
+ margin-left: 6px;
+ vertical-align: middle;
+ box-shadow: 0 0 5px currentColor;
+}
+.online { background: var(--online); }
+.offline { background: var(--offline); }
+.badge {
+ background-color: var(--primary);
+ color: white;
+ padding: 2px 6px;
+ border-radius: 4px;
+ font-size: 0.8em;
+ margin-left: 8px;
+}
+.flash {
+ padding: 15px;
+ margin-bottom: 20px;
+ border-radius: 8px;
+ font-weight: 600;
+ text-align: center;
+}
+.flash.success {
+ background-color: rgba(0, 255, 0, 0.2);
+ color: #90ee90;
+ border: 1px solid #90ee90;
+}
+.flash.error {
+ background-color: rgba(255, 31, 143, 0.2);
+ color: var(--primary);
+ border: 1px solid var(--primary);
+}
+
+@keyframes zoomIn {
+ from { opacity: 0; transform: scale(0.8); }
+ to { opacity: 1; transform: scale(1); }
+}
+@media (max-width: 900px) {
+ .sidebar {
+ transform: translateX(-100%);
+ width: 250px;
+ }
+ .sidebar.active {
+ transform: translateX(0);
+ box-shadow: 5px 0 15px rgba(0, 0, 0, 0.7);
+ }
+ .menu-btn {
+ display: block;
+ }
+ .container {
+ margin: 70px 15px 15px 15px;
+ max-width: calc(100% - 30px);
+ padding: 15px;
+ }
+ .theme-toggle {
+ top: 70px;
+ }
+}
+@media (max-width: 480px) {
+ .nav-brand { font-size: 1.4em; }
+ .nav-link { font-size: 1em; padding: 10px 15px; }
+ .btn { padding: 10px 20px; font-size: 0.9em; }
+ input, textarea, select { padding: 10px 12px; font-size: 0.95em;}
+}
+'''
+
+NAV_HTML = '''
+
+'''
+
+@app.route('/register', methods=['GET', 'POST'])
+def register():
+ if request.method == 'POST':
+ username = request.form.get('username')
+ password = request.form.get('password')
+ if not username or not password:
+ flash('Username and password are required.', 'error')
+ return redirect(url_for('register'))
+ if len(username) < 3:
+ flash('Username must be at least 3 characters long.', 'error')
+ return redirect(url_for('register'))
+ if len(password) < 6:
+ flash('Password must be at least 6 characters long.', 'error')
+ return redirect(url_for('register'))
+
+ data = load_data()
+ if username in data['users']:
+ flash('Username already exists! Choose another.', 'error')
+ return redirect(url_for('register'))
+
+ data['users'][username] = {
+ 'password': password,
+ 'bio': '',
+ 'link': '',
+ 'avatar': None,
+ 'last_seen': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+ }
+ try:
+ save_data(data)
+ flash('Registration successful! Please login.', 'success')
+ return redirect(url_for('login'))
+ except Exception as e:
+ flash('Registration failed. Please try again.', 'error')
+ logging.error(f"Failed to save data during registration: {e}")
+ return redirect(url_for('register'))
+
+ is_authenticated = 'username' in session
+ username = session.get('username')
+ data = load_data()
+ user_count = len(data.get('users', {}))
+ is_online = is_user_online(data, username) if username else False
+ html = '''
+
+
+
+
+
+ Register - Adusis - BBC QoS hub
+
+
+
+
+
+ ''' + NAV_HTML + '''
+
+
+
Join the Fun
+ {% with messages = get_flashed_messages(with_categories=true) %}
+ {% if messages %}
+ {% for category, message in messages %}
+
{{ message }}
+ {% endfor %}
+ {% endif %}
+ {% endwith %}
+
+
Already joined? Login here
+
+
+
+
+'''
+ return render_template_string(html, is_authenticated=is_authenticated, username=username, user_count=user_count, is_online=is_online)
+
+@app.route('/login', methods=['GET', 'POST'])
+def login():
+ if request.method == 'POST':
+ username = request.form.get('username')
+ password = request.form.get('password')
+ data = load_data()
+ if username in data.get('users', {}) and data['users'][username].get('password') == password:
+ session['username'] = username
+ session.permanent = True
+ update_last_seen(username)
+ flash('Login successful!', 'success')
+ return redirect(url_for('feed'))
+ else:
+ flash('Invalid username or password.', 'error')
+ return redirect(url_for('login'))
+
+ is_authenticated = 'username' in session
+ username = session.get('username')
+ data = load_data()
+ user_count = len(data.get('users', {}))
+ is_online = is_user_online(data, username) if username else False
+ html = '''
+
+
+
+
+
+ Login - Adusis - BBC QoS hub
+
+
+
+
+
+ ''' + NAV_HTML + '''
+
+
+
Welcome Back
+ {% with messages = get_flashed_messages(with_categories=true) %}
+ {% if messages %}
+ {% for category, message in messages %}
+
{{ message }}
+ {% endfor %}
+ {% endif %}
+ {% endwith %}
+
+
New here? Register an account
+
+
+
+
+'''
+ return render_template_string(html, is_authenticated=is_authenticated, username=username, user_count=user_count, is_online=is_online)
+
+
+@app.route('/logout')
+def logout():
+ username = session.get('username')
+ if username:
+ update_last_seen(username)
+ session.pop('username', None)
+ flash('You have been logged out.', 'success')
+ return redirect(url_for('login'))
+
+@app.route('/', methods=['GET'])
+def feed():
+ data = load_data()
+ username = session.get('username')
+ posts_list = data.get('posts', [])
+ if not isinstance(posts_list, list):
+ logging.error("Posts data is not a list, resetting.")
+ posts_list = []
+
+ stories = sorted(posts_list, key=lambda x: datetime.strptime(x.get('upload_date', '1970-01-01 00:00:00'), '%Y-%m-%d %H:%M:%S'), reverse=True)
+ is_authenticated = 'username' in session
+ user_count = len(data.get('users', {}))
+ is_online = is_user_online(data, username) if username else False
+
+ html = '''
+
+
+
+
+
+ Feed - Adusis - BBC QoS hub
+
+
+
+
+
+ ''' + NAV_HTML + '''
+
+
+
+ {% if not stories %}
+
+
No stories yet!
+
Be the first one to upload something hot.
+
+ {% else %}
+ {% for story in stories %}
+
+ {% set media_url = "https://huggingface.co/datasets/" + repo_id + "/resolve/main/" + story['type'] + "s/" + story['filename'] %}
+ {% if story['type'] == 'video' %}
+
+
+ {% else %}
+
![{{ story['title'] }}]({{ media_url }})
+ {% endif %}
+
+
+
+ {% if is_authenticated %}
+
+ {% else %}
+
+ β€οΈ
+ {{ story.get('likes', []) | length }}
+
+ {% endif %}
+
+
+ ποΈ
+ {{ story.get('views', 0) }}
+
+
+
+
+
{{ story['title'] }}
+
{{ story['description'] | truncate(100) }}
+
By: {{ story['uploader'] }}
+
+
{{ story['upload_date'] }}
+
+
+
+
+ {% endfor %}
+ {% endif %}
+
+
+
+
![]()
+
+
+
+
+
+'''
+ return render_template_string(html,
+ stories=stories,
+ is_authenticated=is_authenticated,
+ username=username,
+ repo_id=REPO_ID,
+ user_count=user_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!', 'error')
+ return redirect(url_for('login'))
+
+ data = load_data()
+ username = session['username']
+
+ if request.method == 'POST':
+ if 'delete_story' in request.form:
+ story_id = request.form.get('story_id')
+ if story_id:
+ original_length = len(data['posts'])
+ data['posts'] = [p for p in data['posts'] if not (p.get('id') == story_id and p.get('uploader') == username)]
+ if len(data['posts']) < original_length:
+ try:
+ save_data(data)
+ flash('Story deleted.', 'success')
+ except Exception as e:
+ flash('Failed to delete story.', 'error')
+ logging.error(f"Error saving data after deleting story {story_id}: {e}")
+ else:
+ flash('Story not found or you do not have permission.', 'error')
+ return redirect(url_for('profile'))
+
+ elif 'update_profile' in request.form:
+ bio = request.form.get('bio', '').strip()[:500]
+ link = request.form.get('link', '').strip()[:200]
+ avatar_file = request.files.get('avatar')
+
+ if avatar_file and avatar_file.filename:
+ if not allowed_file(avatar_file.filename, {'png', 'jpg', 'jpeg', 'gif'}):
+ flash('Invalid avatar file type. Use png, jpg, jpeg, gif.', 'error')
+ return redirect(url_for('profile'))
+
+ filename = secure_filename(f"{username}_avatar_{random.randint(100,999)}{os.path.splitext(avatar_file.filename)[1]}")
+ temp_path = os.path.join(UPLOAD_FOLDER, filename)
+
+ try:
+ avatar_file.save(temp_path)
+ api = HfApi()
+ avatar_path = f"avatars/{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"Avatar update for {username}"
+ )
+ data['users'][username]['avatar'] = avatar_path
+ if os.path.exists(temp_path):
+ os.remove(temp_path)
+ except Exception as e:
+ flash('Failed to upload avatar.', 'error')
+ logging.error(f"Error uploading avatar for {username}: {e}")
+ if os.path.exists(temp_path):
+ os.remove(temp_path)
+
+ data['users'][username]['bio'] = bio
+ data['users'][username]['link'] = link
+ try:
+ save_data(data)
+ flash('Profile updated!', 'success')
+ except Exception as e:
+ flash('Failed to update profile.', 'error')
+ logging.error(f"Error saving profile data for {username}: {e}")
+ return redirect(url_for('profile'))
+
+ data = load_data()
+ user_data = data.get('users', {}).get(username, {})
+ user_stories = sorted([p for p in data.get('posts', []) if p.get('uploader') == username], key=lambda x: datetime.strptime(x.get('upload_date', '1970-01-01 00:00:00'), '%Y-%m-%d %H:%M:%S'), reverse=True)
+ is_authenticated = True
+ user_count = len(data.get('users', {}))
+ is_online = is_user_online(data, 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')
+
+ html = '''
+
+
+
+
+
+ Profile - {{ username }} - Adusis - BBC QoS hub
+
+
+
+
+
+ ''' + NAV_HTML + '''
+
+
+
+
+
![]()
+
+
+
+
+
+'''
+ return render_template_string(html,
+ username=username,
+ user_stories=user_stories,
+ bio=bio,
+ link=link,
+ avatar=avatar,
+ last_seen=last_seen,
+ is_authenticated=is_authenticated,
+ repo_id=REPO_ID,
+ user_count=user_count,
+ is_online=is_online,
+ random=random
+ )
+
+def allowed_file(filename, allowed_extensions):
+ return '.' in filename and \
+ filename.rsplit('.', 1)[1].lower() in allowed_extensions
+
+@app.route('/story/', methods=['GET', 'POST'])
+def story_page(story_id):
+ data = load_data()
+ username = session.get('username')
+
+ story_index = next((index for (index, d) in enumerate(data.get('posts',[])) if d.get('id') == story_id), None)
+
+ if story_index is None:
+ flash('Story not found.', 'error')
+ return redirect(url_for('feed'))
+
+ story = data['posts'][story_index]
+
+ if request.method == 'POST':
+ if 'add_comment' in request.form:
+ if not username:
+ flash('You must be logged in to comment.', 'error')
+ return redirect(url_for('story_page', story_id=story_id))
+
+ comment_text = request.form.get('comment_text', '').strip()
+ if not comment_text:
+ flash('Comment cannot be empty.', 'error')
+ return redirect(url_for('story_page', story_id=story_id))
+ if len(comment_text) > 1000:
+ flash('Comment too long (max 1000 characters).', 'error')
+ return redirect(url_for('story_page', story_id=story_id))
+
+ new_comment = {
+ 'username': username,
+ 'text': comment_text,
+ 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+ }
+
+ if not isinstance(story.get('comments'), list):
+ story['comments'] = []
+
+ story['comments'].append(new_comment)
+ data['posts'][story_index] = story
+
+ try:
+ save_data(data)
+ flash('Comment added!', 'success')
+ return redirect(url_for('story_page', story_id=story_id) + '#comments')
+ except Exception as e:
+ flash('Failed to add comment.', 'error')
+ logging.error(f"Error saving comment for story {story_id}: {e}")
+ if story['comments'] and story['comments'][-1] == new_comment:
+ story['comments'].pop()
+ return redirect(url_for('story_page', story_id=story_id))
+
+ data = load_data()
+ story_index = next((index for (index, d) in enumerate(data.get('posts',[])) if d.get('id') == story_id), None)
+ if story_index is None:
+ flash('Story disappeared.', 'error')
+ return redirect(url_for('feed'))
+ story = data['posts'][story_index]
+
+ is_authenticated = 'username' in session
+ user_count = len(data.get('users', {}))
+ is_online = is_user_online(data, username) if username else False
+ uploader_online = is_user_online(data, story.get('uploader'))
+
+ html = '''
+
+
+
+
+
+ {{ story['title'] }} - Adusis - BBC QoS hub
+
+
+
+
+
+ ''' + NAV_HTML + '''
+
+
+ {% with messages = get_flashed_messages(with_categories=true) %}
+ {% if messages %}
+ {% for category, message in messages %}
+
{{ message }}
+ {% endfor %}
+ {% endif %}
+ {% endwith %}
+
+
{{ story['title'] }}
+
+
+
+
+
Description: {{ story['description'] if story['description'] else 'No description.'}}
+
Uploaded by: {{ story['uploader'] }}
+
Uploaded on: {{ story['upload_date'] }}
+
+
+
+ {% if is_authenticated %}
+
+ {% else %}
+
+ β€οΈ Likes {{ story.get('likes', []) | length }}
+
+ {% endif %}
+
+
+ ποΈ Views {{ story.get('views', 0) }}
+
+
+
+
+
+
+
Back to Feed
+
+
+
+
![]()
+
+
+
+
+
+'''
+ return render_template_string(html,
+ story=story,
+ repo_id=REPO_ID,
+ is_authenticated=is_authenticated,
+ username=username,
+ user_count=user_count,
+ is_online=is_online,
+ uploader_online=uploader_online)
+
+
+@app.route('/upload', methods=['GET', 'POST'])
+def upload():
+ if 'username' not in session:
+ flash('Login to upload a story!', 'error')
+ return redirect(url_for('login'))
+
+ data = load_data()
+ username = session['username']
+
+ if request.method == 'POST':
+ title = request.form.get('title', '').strip()
+ description = request.form.get('description', '').strip()[:2000]
+ file = request.files.get('file')
+
+ if not file or not title:
+ flash('Title and file are required!', 'error')
+ return redirect(url_for('upload'))
+
+ if not allowed_file(file.filename, {'png', 'jpg', 'jpeg', 'gif', 'mp4', 'mov', 'avi', 'webm'}):
+ flash('Invalid file type. Allowed: Images (png, jpg, gif), Videos (mp4, mov, avi, webm)', 'error')
+ return redirect(url_for('upload'))
+
+ filename = secure_filename(f"{username}_{int(time.time())}_{file.filename}")
+ temp_path = os.path.join(UPLOAD_FOLDER, filename)
+
+ try:
+ file.save(temp_path)
+
+ file_type = 'video' if filename.lower().endswith(('.mp4', '.mov', 'avi', '.webm')) else 'photo'
+
+ story_id = str(random.randint(100000, 999999))
+ while any(p.get('id') == story_id for p in data.get('posts', [])):
+ story_id = str(random.randint(100000, 999999))
+
+ api = HfApi()
+ hf_filename = f"{story_id}_{filename}"
+ file_path = f"{file_type}s/{hf_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"Upload {file_type} by {username} (ID: {story_id})"
+ )
+
+ data.setdefault('posts', []).append({
+ 'id': story_id,
+ 'title': title,
+ 'description': description,
+ 'filename': hf_filename,
+ 'type': file_type,
+ 'uploader': username,
+ 'upload_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
+ 'likes': [],
+ 'views': 0,
+ 'comments': [],
+ 'jerked_off_count': 0
+ })
+
+ save_data(data)
+ flash('Story uploaded successfully!', 'success')
+
+ if os.path.exists(temp_path):
+ os.remove(temp_path)
+ return redirect(url_for('profile'))
+
+ except Exception as e:
+ flash(f'Upload failed: {e}', 'error')
+ logging.error(f"Error during upload for {username}: {e}")
+ if os.path.exists(temp_path):
+ os.remove(temp_path)
+ return redirect(url_for('upload'))
+
+ is_authenticated = True
+ user_count = len(data.get('users', {}))
+ is_online = is_user_online(data, username)
+
+ html = '''
+
+
+
+
+
+ Upload Story - Adusis - BBC QoS hub
+
+
+
+
+
+ ''' + NAV_HTML + '''
+
+
+
Upload Your Story
+ {% with messages = get_flashed_messages(with_categories=true) %}
+ {% if messages %}
+ {% for category, message in messages %}
+
{{ message }}
+ {% endfor %}
+ {% endif %}
+ {% endwith %}
+
+
+
+
+
+
+'''
+ return render_template_string(html,
+ username=username,
+ is_authenticated=is_authenticated,
+ repo_id=REPO_ID,
+ user_count=user_count,
+ is_online=is_online)
+
+@app.route('/users', methods=['GET', 'POST'])
+def users():
+ data = load_data()
+ username = session.get('username')
+
+ is_authenticated = 'username' in session
+ user_count = len(data.get('users', {}))
+ is_online = is_user_online(data, username) if username else False
+
+ search_query = request.form.get('search', '').strip().lower() if request.method == 'POST' else ''
+
+ user_list = []
+ for user, user_data in data.get('users', {}).items():
+ if user == username:
+ continue
+ if not search_query or search_query in user.lower():
+ user_list.append({
+ 'name': user,
+ 'avatar': user_data.get('avatar'),
+ 'online': is_user_online(data, user)
+ })
+
+ user_list.sort(key=lambda u: (-u['online'], u['name'].lower()))
+
+ html = '''
+
+
+
+
+
+ Users - Adusis - BBC QoS hub
+
+
+
+
+
+ ''' + NAV_HTML + '''
+
+
+
Explore Users ({{ user_count }})
+
+
+
+
+ {% for user_info in user_list %}
+
+ {% if user_info['avatar'] %}
+
![{{ user_info['name'] }} Avatar](https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ user_info['avatar'] }}?rand={{ random.randint(1,1000) }})
+ {% else %}
+
{{ user_info['name'][0]|upper }}
+ {% endif %}
+
+
+ {% endfor %}
+ {% if not user_list and search_query %}
+
No users found matching "{{ search_query }}".
+ {% elif not user_list %}
+
Looks like it's just you (and maybe the admin)!
+ {% endif %}
+
+
+
+
+
+'''
+ return render_template_string(html,
+ user_list=user_list,
+ username=username,
+ is_authenticated=is_authenticated,
+ repo_id=REPO_ID,
+ user_count=user_count,
+ is_online=is_online,
+ search_query=search_query,
+ random=random)
+
+@app.route('/user/', methods=['GET'])
+def user_profile(username):
+ data = load_data()
+ current_user = session.get('username')
+
+ if username not in data.get('users', {}):
+ flash(f'User "{username}" not found.', 'error')
+ return redirect(url_for('users'))
+
+ is_own_profile = (username == current_user)
+ if is_own_profile:
+ return redirect(url_for('profile'))
+
+ user_data = data['users'][username]
+ user_stories = sorted([p for p in data.get('posts', []) if p.get('uploader') == username], key=lambda x: datetime.strptime(x.get('upload_date','1970-01-01 00:00:00'), '%Y-%m-%d %H:%M:%S'), reverse=True)
+
+ is_authenticated = 'username' in session
+ user_count = len(data.get('users', {}))
+ current_user_online = is_user_online(data, current_user) if current_user else False
+ profile_user_online = is_user_online(data, 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')
+
+ html = '''
+
+
+
+
+
+ Profile - {{ username }} - Adusis - BBC QoS hub
+
+
+
+
+
+ ''' + NAV_HTML + '''
+
+
+
+
+
![]()
+
+
+
+
+
+'''
+ return render_template_string(html,
+ username=username,
+ user_stories=user_stories,
+ bio=bio,
+ link=link,
+ avatar=avatar,
+ last_seen=last_seen,
+ is_authenticated=is_authenticated,
+ current_user=current_user,
+ repo_id=REPO_ID,
+ user_count=user_count,
+ is_online=current_user_online,
+ profile_user_online=profile_user_online,
+ random=random)
+
+
+@app.route('/like/', methods=['POST'])
+def like_story(story_id):
+ if 'username' not in session:
+ return jsonify({'message': 'Login required'}), 401
+
+ username = session['username']
+ data = load_data()
+
+ story_index = next((index for (index, d) in enumerate(data.get('posts',[])) if d.get('id') == story_id), None)
+ if story_index is None:
+ return jsonify({'message': 'Story not found'}), 404
+
+ story = data['posts'][story_index]
+
+ if not isinstance(story.get('likes'), list):
+ story['likes'] = []
+
+ liked = False
+ if username in story['likes']:
+ story['likes'].remove(username)
+ liked = False
+ else:
+ story['likes'].append(username)
+ liked = True
+
+ data['posts'][story_index] = story
+
+ try:
+ save_data(data)
+ return jsonify({'message': 'Success', 'likes': len(story['likes']), 'liked': liked}), 200
+ except Exception as e:
+ logging.error(f"Error saving like for story {story_id} by {username}: {e}")
+ if liked:
+ if username in story['likes']: story['likes'].remove(username)
+ else:
+ if username not in story['likes']: story['likes'].append(username)
+ return jsonify({'message': 'Failed to save like'}), 500
+
+
+@app.route('/jerk_off/', methods=['POST'])
+def jerk_off_story(story_id):
+ data = load_data()
+
+ story_index = next((index for (index, d) in enumerate(data.get('posts',[])) if d.get('id') == story_id), None)
+ if story_index is None:
+ return jsonify({'message': 'Story not found'}), 404
+
+ story = data['posts'][story_index]
+
+ if not isinstance(story.get('jerked_off_count'), int):
+ story['jerked_off_count'] = 0
+
+ story['jerked_off_count'] += 1
+ data['posts'][story_index] = story
+
+ try:
+ save_data(data)
+ return jsonify({'message': 'Success', 'jerked_off_count': story['jerked_off_count']}), 200
+ except Exception as e:
+ logging.error(f"Error saving jerk_off count for story {story_id}: {e}")
+ story['jerked_off_count'] -= 1
+ return jsonify({'message': 'Failed to save count'}), 500
+
+@app.route('/view/', methods=['POST'])
+def increment_view(story_id):
+
+ data = load_data()
+ story_index = next((index for (index, d) in enumerate(data.get('posts',[])) if d.get('id') == story_id), None)
+ if story_index is None:
+ return jsonify({'message': 'Story not found'}), 404
+
+ story = data['posts'][story_index]
+
+ if not isinstance(story.get('views'), int):
+ story['views'] = 0
+
+ story['views'] += 1
+ data['posts'][story_index] = story
+
+ try:
+ save_data(data)
+ return jsonify({'message': 'Success', 'views': story['views']}), 200
+ except Exception as e:
+ logging.error(f"Error saving view count for story {story_id}: {e}")
+ story['views'] -= 1
+ return jsonify({'message': 'Failed to save view count'}), 500
+
+if __name__ == '__main__':
+ backup_thread = threading.Thread(target=periodic_backup, daemon=True)
+ backup_thread.start()
+ app.run(debug=False, host='0.0.0.0', port=7860)
+
Comments ({{ story.get('comments', []) | length }})
+ {% if is_authenticated %} ++ {% else %} +
Login to post a comment.
+ {% endif %} + +Be the first to comment!
+ {% endif %} +