diff --git "a/Codetxt.txt" "b/Codetxt.txt" new file mode 100644--- /dev/null +++ "b/Codetxt.txt" @@ -0,0 +1,3216 @@ +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/w1f9" +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("Данные не в формате dict, инициализация пустой базы") + 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("Данные успешно загружены") + return data + except Exception as e: + logging.error(f"Ошибка при загрузке данных: {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("Данные сохранены и загружены на HF") + except Exception as e: + logging.error(f"Ошибка при сохранении данных: {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"Бэкап {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" + ) + logging.info("База данных загружена на Hugging Face") + except Exception as e: + logging.error(f"Ошибка при загрузке базы данных: {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("База данных скачана с Hugging Face") + except Exception as e: + logging.error(f"Ошибка при скачивании базы данных: {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('Пользователь уже существует!') + 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('Регистрация успешна! Пожалуйста, войдите.') + 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 %} +
+ + + +
+ +
+ + + +''' + 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('Неверное имя пользователя или пароль!') + 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 %} +
+ + + +
+ +
+ + + +''' + 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 + + 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 "Пост не найден", 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') + }] + elif 'share' in request.form: + share_to = request.form.get('share_to') + if share_to == 'chat': + data['general_chat'].append({ + 'sender': username, + 'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + 'post_id': post_id + }) + elif share_to in data['users']: + chat_key = f"{min(username, share_to)}_{max(username, share_to)}" + if chat_key not in data['private_chats']: + data['private_chats'][chat_key] = [] + data['private_chats'][chat_key].append({ + 'sender': username, + 'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + 'post_id': post_id + }) + save_data(data) + return redirect(url_for('feed')) + + html = ''' + + + + + + + + Adusis - QoS, BBC, BNWO HUB + + + + + + ''' + NAV_HTML + ''' + +
+ {% for post in posts %} +
+ {% if post['type'] == 'video' %} + + {% else %} + {{ post['title'] }} + {% endif %} +
+

{{ post['title'] }}

+

{{ post['description']|truncate(150) }}

+

By: {{ post['uploader'] }}

+
+
+ + {{ post['likes']|length }} + + {{ post['comments']|length }} + + {{ post['views'] }} +
+
+ {% for comment in post.get('comments', []) %} +
+ {{ comment['user'] }}: {{ comment['text'] }} +
+ {% endfor %} + {% if is_authenticated %} +
+ + + +
+ {% endif %} +
+ +
+ {% 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 "Пост не найден", 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 %} + {{ post['title'] }} + {% endif %} +

{{ post['description'] }}

+

By: {{ post['uploader'] }} | {{ post['upload_date'] }}

+

Views: {{ post['views'] }} | Likes: {{ post['likes']|length }}

+ {% if is_authenticated %} + +
+ + +
+ {% endif %} +

Comments

+ {% for comment in post.get('comments', []) %} +
+

{{ comment['user'] }} ({{ comment['date'] }}): {{ comment['text'] }}

+
+ {% 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('Войдите, чтобы просмотреть свой профиль!') + 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"Загружен аватар для {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 + ''' + +
+
+ {% if avatar %} + Avatar + {% endif %} +
+

{{ username }}

+ {% if not is_online %} +

Last seen: {{ last_seen }}

+ {% endif %} +

{{ bio }}

+ {% if link %} +

{{ link }}

+ {% endif %} +

Views: {{ total_views }} | Likes: {{ total_likes }}

+ +
+
+

Edit Profile

+
+ + + + +
+ Add Post +

Your Posts

+
+ {% if user_posts %} + {% for post in user_posts %} +
+ + {% if post['type'] == 'video' %} + + {% else %} + {{ post['title'] }} + {% endif %} +

{{ post['title'] }}

+
+

{{ post['description']|truncate(100) }}

+

{{ post['upload_date'] }}

+
+ + +
+
+ {% endfor %} + {% else %} +

You haven't uploaded any posts yet.

+ {% endif %} +
+
+ + + + +''' + return render_template_string(html, username=username, user_posts=user_posts, total_views=total_views, total_likes=total_likes, bio=bio, link=link, avatar=avatar, repo_id=REPO_ID, is_authenticated=is_authenticated, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online, last_seen=last_seen) + +@app.route('/profile/') +def user_profile(username): + data = load_data() + session_username = session.get('username', None) + if session_username: + update_last_seen(data, session_username) + if username not in data['users']: + return "Пользователь не найден", 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, session_username) if is_authenticated else 0 + private_unread_count = get_private_unread_count(data, session_username) if is_authenticated else 0 + user_count = len(data['users']) + is_online = is_user_online(data, session_username) 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) + profile_is_online = is_user_online(data, username) + last_seen = user_data.get('last_seen', 'Never') + + html = ''' + + + + + + + Profile - {{ username }} - Content Hub + + + + + + ''' + NAV_HTML + ''' + +
+
+ {% if avatar %} + Avatar + {% endif %} +
+

{{ username }}

+ {% if not profile_is_online %} +

Last seen: {{ last_seen }}

+ {% endif %} +

{{ bio }}

+ {% if link %} +

{{ link }}

+ {% endif %} +

Views: {{ total_views }} | Likes: {{ total_likes }}

+
+
+

Posts

+
+ {% if user_posts %} + {% for post in user_posts %} +
+ + {% if post['type'] == 'video' %} + + {% else %} + {{ post['title'] }} + {% endif %} +

{{ post['title'] }}

+
+

{{ post['description']|truncate(100) }}

+

{{ post['upload_date'] }}

+
+ {% endfor %} + {% else %} +

No posts yet.

+ {% endif %} +
+
+ + + + +''' + return render_template_string(html, username=username, user_posts=user_posts, total_views=total_views, total_likes=total_likes, bio=bio, link=link, avatar=avatar, repo_id=REPO_ID, is_authenticated=is_authenticated, unread_count=unread_count, user_count=user_count, private_unread_count=private_unread_count, is_online=is_online, profile_is_online=profile_is_online, last_seen=last_seen) + +@app.route('/upload', methods=['GET', 'POST']) +def upload(): + if 'username' not in session: + flash('Войдите, чтобы загрузить контент!') + 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('Необходимо указать заголовок и выбрать файл!') + 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}_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{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}_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{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 Hub + + + + + + ''' + NAV_HTML + ''' + +
+

Upload Content

+ {% with messages = get_flashed_messages() %} + {% if messages %} + {% for message in messages %} +
{{ message }}
+ {% endfor %} + {% endif %} + {% endwith %} +
+ + + + +
+
+
+
+
+ + + +''' + return render_template_string(html, username=username, is_authenticated=is_authenticated, 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 %} + Your Avatar + {% else %} +
+ {% endif %} + {% else %} + {% if data['users'][message['sender']].get('avatar') %} + {{ message['sender'] }} Avatar + {% else %} +
+ {% endif %} + {% endif %} +
+ {{ message['sender'] }} + + {% if 'text' in message %} +

{{ message['text'] }}

+ {% endif %} + {% if 'file' in message %} + {% if message['file_type'] == 'video' %} + + {% else %} + Chat file + {% 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 %} +
+ + + + +
+ {% 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 + 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 %} + {{ user }} 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('Войдите, чтобы просмотреть сообщения!') + 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 %} + {{ other_user }} 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('Войдите, чтобы начать чат!') + return redirect(url_for('login')) + + data = load_data() + username = session['username'] + update_last_seen(data, username) + if recipient not in data['users']: + return "Пользователь не найден", 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 %} + {{ recipient }} Avatar + {% else %} +
+ {% endif %} + Chat with {{ recipient }} +

+
+ {% for message in private_messages %} +
+ {% if message['sender'] == username %} + {% if avatar %} + Your Avatar + {% else %} +
+ {% endif %} + {% else %} + {% if recipient_avatar %} + {{ recipient }} Avatar + {% else %} +
+ {% endif %} + {% endif %} +
+ {{ message['sender'] }} + + {% if 'text' in message %} +

{{ message['text'] }}

+ {% endif %} + {% if 'file' in message %} + {% if message['file_type'] == 'video' %} + + {% else %} + Chat file + {% 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 %} +
+
+ + + + +
+ Back to Messages +
+ + + + +''' + 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), + avatar=avatar, + recipient_avatar=recipient_avatar, + recipient_online=recipient_online) + +@app.route('/admhosto', methods=['GET', 'POST']) +def admin_panel(): + 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.form.get('search', '').strip().lower() if request.method == 'POST' and 'search' in request.form else request.args.get('search', '').strip().lower() + 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 'delete' 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] + save_data(data) + return redirect(url_for('admin_panel')) + + html = ''' + + + + + + + Admin Panel - Content Hub + + + + + + ''' + NAV_HTML + ''' + +
+

Admin Panel

+
+
+ + +
+
+
+ {% for post in posts %} +
+ + {% if post['type'] == 'video' %} + + {% else %} + {{ post['title'] }} + {% endif %} +

{{ post['title'] }}

+
+

{{ post['description']|truncate(100) }}

+

By: {{ post['uploader'] }}

+

{{ post['upload_date'] }} | Views: {{ post['views'] }} | Likes: {{ post['likes']|length }}

+
+ + +
+
+ {% endfor %} + {% if not posts %} +

No posts found.

+ {% endif %} +
+
+ + + + +''' + 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), + search_query=search_query) + +if __name__ == '__main__': + threading.Thread(target=periodic_backup, daemon=True).start() + app.run(debug=True, host='0.0.0.0', port=7860) \ No newline at end of file