diff --git "a/app.py" "b/app.py" --- "a/app.py" +++ "b/app.py" @@ -13,7 +13,7 @@ import random app = Flask(__name__) app.secret_key = os.getenv("FLASK_SECRET_KEY", "supersecretkey") DATA_FILE = 'data_adusis.json' -REPO_ID = "Eluza133/w1f9" +REPO_ID = "Eluza133/A12d12s12" HF_TOKEN_WRITE = os.getenv("HF_TOKEN") HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") or HF_TOKEN_WRITE @@ -119,196 +119,183 @@ def update_last_seen(data, username): data['users'][username]['last_seen'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') save_data(data) -# Новый стиль 2025 года с изменениями для TikTok-подобной ленты BASE_STYLE = ''' :root { - --primary: #ff3b8e; - --secondary: #00f4ff; - --accent: #7b00ff; - --background: #0a0a23; - --surface: #1a1a3d; - --text: #e0e0ff; - --shadow: 0 4px 15px rgba(0, 0, 0, 0.3); - --glass: rgba(255, 255, 255, 0.05); - --gradient: linear-gradient(135deg, var(--primary) 0%, var(--accent) 100%); - --online: #00ff85; - --offline: #ff3366; - --transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); + --primary: #ff4d6d; + --secondary: #00ddeb; + --accent: #8b5cf6; + --background-light: #fef9f1; + --background-dark: #1f1a44; + --card-bg: rgba(255, 255, 255, 0.9); + --card-bg-dark: rgba(50, 45, 80, 0.9); + --text-light: #2a1e5a; + --text-dark: #e8e1ff; + --shadow: 0 8px 24px rgba(0, 0, 0, 0.15); + --glass-bg: rgba(255, 255, 255, 0.1); + --transition: all 0.3s ease; + --online: #34c759; + --offline: #ff3b30; } - -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - +* { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Inter', sans-serif; - background: var(--background); - color: var(--text); + background: var(--background-light); + color: var(--text-light); line-height: 1.6; + transition: var(--transition); overflow-x: hidden; } - +body.dark { + background: var(--background-dark); + color: var(--text-dark); +} .sidebar { position: fixed; top: 0; left: 0; - width: 300px; + width: 280px; height: 100%; - background: var(--surface); - padding: 2rem; + background: var(--glass-bg); + backdrop-filter: blur(15px); + padding: 20px; box-shadow: var(--shadow); - transform: translateX(0); - transition: var(--transition); z-index: 1000; + transition: transform var(--transition); } - .sidebar.hidden { transform: translateX(-100%); } - .sidebar-header { display: flex; align-items: center; - gap: 1rem; - margin-bottom: 2rem; + gap: 12px; + margin-bottom: 30px; } - .nav-brand { - font-size: 1.8rem; + font-size: 1.6em; font-weight: 800; - background: var(--gradient); + background: linear-gradient(135deg, var(--primary), var(--accent)); -webkit-background-clip: text; color: transparent; } - .logo { - width: 40px; - height: 40px; - border-radius: 50%; + width: 36px; + height: 36px; + border-radius: 12px; box-shadow: var(--shadow); - transition: var(--transition); -} - -.logo:hover { - transform: scale(1.1); } - .nav-links { display: flex; flex-direction: column; - gap: 0.8rem; + gap: 12px; } - .nav-link { display: flex; align-items: center; - gap: 0.8rem; - padding: 1rem; - background: var(--glass); - color: var(--text); + gap: 12px; + padding: 12px 20px; + background: var(--card-bg); + color: var(--text-light); text-decoration: none; border-radius: 12px; - font-size: 1rem; + font-size: 1em; font-weight: 500; transition: var(--transition); position: relative; - backdrop-filter: blur(10px); } - +body.dark .nav-link { + background: var(--card-bg-dark); + color: var(--text-dark); +} .nav-link:hover { + transform: scale(1.02); background: var(--primary); - transform: translateX(5px); - box-shadow: var(--shadow); + color: white; + box-shadow: 0 4px 16px rgba(255, 77, 109, 0.3); } - .nav-link .badge { position: absolute; - right: 1rem; + right: 12px; background: var(--secondary); - color: var(--background); - padding: 0.3rem 0.6rem; - border-radius: 20px; - font-size: 0.75rem; + color: white; + padding: 3px 8px; + border-radius: 10px; + font-size: 0.75em; + font-weight: 700; } - .logout-btn { background: var(--secondary); - color: var(--background); + color: white; } - .logout-btn:hover { - background: var(--accent); + background: #00b8c5; } - .menu-btn { display: none; - position: fixed; - top: 1rem; - left: 1rem; - z-index: 1001; - background: var(--glass); + font-size: 24px; + background: var(--glass-bg); border: none; - padding: 0.8rem; - border-radius: 50%; 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(--text); + color: white; } - .container { - margin: 2rem 2rem 2rem 340px; - padding: 0; - background: var(--surface); - border-radius: 20px; - box-shadow: var(--shadow); + margin: 20px auto 20px 300px; + max-width: 1200px; + padding: 20px; transition: var(--transition); - height: calc(100vh - 4rem); - overflow: hidden; } - .btn { - padding: 0.8rem 1.5rem; - background: var(--gradient); - color: var(--text); + padding: 12px 24px; + background: var(--primary); + color: white; border: none; border-radius: 12px; cursor: pointer; + font-size: 1em; font-weight: 600; transition: var(--transition); + display: inline-flex; + align-items: center; + gap: 8px; box-shadow: var(--shadow); } - .btn:hover { - transform: scale(1.05); - box-shadow: 0 6px 20px rgba(255, 59, 142, 0.3); + transform: scale(1.03); + background: #e6415f; + box-shadow: 0 6px 20px rgba(255, 77, 109, 0.4); } - input, textarea, select { width: 100%; - padding: 1rem; - margin: 0.5rem 0; + padding: 12px; + margin: 10px 0; border: none; border-radius: 12px; - background: var(--glass); - color: var(--text); - font-size: 1rem; + background: var(--glass-bg); + color: var(--text-light); + font-size: 1em; transition: var(--transition); - backdrop-filter: blur(10px); + box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.05); +} +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.15); box-shadow: 0 0 0 3px var(--primary); - background: rgba(255, 255, 255, 0.1); } - .modal { display: none; position: fixed; @@ -316,59 +303,55 @@ input:focus, textarea:focus, select:focus { left: 0; width: 100%; height: 100%; - background: rgba(0, 0, 0, 0.9); + background: rgba(0, 0, 0, 0.8); z-index: 2000; - display: flex; justify-content: center; align-items: center; + transition: opacity var(--transition); } - .modal img, .modal video { max-width: 90%; max-height: 90%; - border-radius: 20px; + object-fit: contain; + border-radius: 16px; box-shadow: var(--shadow); - animation: fadeIn 0.3s ease; + animation: zoomIn 0.3s ease; } - .theme-toggle { position: fixed; - top: 1rem; - right: 1rem; - background: var(--glass); + top: 15px; + right: 15px; + background: var(--glass-bg); border: none; - padding: 0.8rem; + padding: 10px; border-radius: 50%; - color: var(--primary); cursor: pointer; + font-size: 20px; + box-shadow: var(--shadow); transition: var(--transition); } - .theme-toggle:hover { + transform: rotate(90deg); background: var(--accent); - transform: rotate(180deg); + color: white; } - .status-dot { - width: 10px; - height: 10px; + width: 8px; + height: 8px; border-radius: 50%; display: inline-block; - margin-left: 0.5rem; + margin-left: 5px; } - .online { background: var(--online); } .offline { background: var(--offline); } - -@keyframes fadeIn { - from { opacity: 0; transform: scale(0.9); } +@keyframes zoomIn { + from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } } - @media (max-width: 900px) { .sidebar { width: 100%; - max-width: 300px; + max-width: 280px; transform: translateX(-100%); } .sidebar.active { @@ -378,140 +361,17 @@ input:focus, textarea:focus, select:focus { display: block; } .container { - margin: 2rem 1rem; + margin: 70px 15px 20px 15px; + max-width: calc(100% - 30px); + } + .theme-toggle { + top: 70px; } } - -/* Стили для TikTok-подобной ленты */ -.feed-container { - height: 100%; - overflow-y: scroll; - scroll-snap-type: y mandatory; - -webkit-overflow-scrolling: touch; -} - -.post-slide { - height: 100vh; - scroll-snap-align: start; - position: relative; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - background: var(--glass); -} - -.post-media { - width: 100%; - height: 80%; - object-fit: contain; - border-radius: 20px; - box-shadow: var(--shadow); -} - -.post-overlay { - position: absolute; - bottom: 0; - left: 0; - width: 100%; - padding: 1rem; - background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent); -} - -.post-info { - padding: 1rem; - width: 100%; -} - -.post-info h2 { - font-size: 1.5rem; - margin-bottom: 0.5rem; -} - -.post-info p { - font-size: 1rem; - opacity: 0.8; -} - -.actions { - position: absolute; - right: 1rem; - top: 50%; - transform: translateY(-50%); - display: flex; - flex-direction: column; - gap: 1rem; - align-items: center; -} - -.action-btn { - background: var(--glass); - border: none; - border-radius: 50%; - width: 50px; - height: 50px; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - transition: var(--transition); - backdrop-filter: blur(10px); -} - -.action-btn:hover { - background: var(--primary); - transform: scale(1.1); -} - -.action-btn.liked { - background: var(--secondary); -} - -.action-count { - font-size: 0.9rem; - margin-top: 0.3rem; -} - -.comments-container { - position: absolute; - right: 0; - top: 0; - height: 100%; - width: 350px; - background: rgba(0, 0, 0, 0.9); - padding: 1rem; - overflow-y: auto; - transform: translateX(100%); - transition: var(--transition); - z-index: 10; -} - -.comments-container.active { - transform: translateX(0); -} - -.comment { - background: var(--glass); - padding: 0.8rem; - border-radius: 10px; - margin-bottom: 0.8rem; -} - -.comment-form { - position: sticky; - bottom: 0; - background: rgba(0, 0, 0, 0.9); - padding: 1rem; -} - -.username-link { - color: var(--primary); - text-decoration: none; - font-weight: 600; -} - -.username-link:hover { - color: var(--accent); +@media (max-width: 480px) { + .nav-brand { font-size: 1.4em; } + .nav-link { font-size: 0.9em; padding: 10px 16px; } + .btn { padding: 10px 18px; font-size: 0.9em; } } ''' @@ -548,7 +408,7 @@ def register(): data = load_data() if username in data['users']: - flash('User already exists!') + flash('Пользователь уже существует!') return redirect(url_for('register')) data['users'][username] = { @@ -561,7 +421,7 @@ def register(): 'last_seen': datetime.now().strftime('%Y-%m-%d %H:%M:%S') } save_data(data) - flash('Registration successful! Please login.') + flash('Регистрация успешна! Пожалуйста, войдите.') return redirect(url_for('login')) is_authenticated = 'username' in session @@ -579,53 +439,54 @@ def register(): - Register - ADUSIS Hub + + Register - Content Hub @@ -677,7 +538,7 @@ def login(): session.permanent = True update_last_seen(data, username) return redirect(url_for('feed')) - flash('Invalid username or password!') + flash('Неверное имя пользователя или пароль!') return redirect(url_for('login')) is_authenticated = 'username' in session @@ -696,43 +557,52 @@ def login(): - Login - ADUSIS Hub + Login - Content Hub @@ -790,7 +660,7 @@ def increment_view(post_id): post['views'] = post.get('views', 0) + 1 save_data(data) return '', 204 - return 'Post not found', 404 + return 'Пост не найден', 404 @app.route('/', methods=['GET', 'POST']) def feed(): @@ -807,19 +677,42 @@ def feed(): if request.method == 'POST' and is_authenticated: post_id = request.form.get('post_id') - action = request.form.get('action') post = next((p for p in data['posts'] if p['id'] == post_id), None) - if post: - if action == 'like': - if username not in post.get('likes', []): - post['likes'] = post.get('likes', []) + [username] - else: - post['likes'] = [user for user in post.get('likes', []) if user != username] - elif action == 'comment': - 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) + if not post: + return "Пост не найден", 404 + + if 'like' in request.form: + if username not in post.get('likes', []): + post['likes'] = post.get('likes', []) + [username] + else: + post['likes'] = [u for u in post.get('likes', []) if u != username] + elif '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 = ''' @@ -828,111 +721,294 @@ def feed(): - ADUSIS Feed + + Adusis - QoS, BBC, BNWO HUB ''' + NAV_HTML + ''' -
-
- {% for post in posts %} -
- {% if post['type'] == 'video' %} - - {% else %} - {{ post['title'] }} - {% 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)) + 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): @@ -942,7 +1018,7 @@ def post_page(post_id): 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 + return "Пост не найден", 404 is_authenticated = 'username' in session unread_count = get_unread_count(data, username) if is_authenticated else 0 @@ -971,54 +1047,57 @@ def post_page(post_id): - {{ post['title'] }} - ADUSIS Hub + + {{ post['title'] }} - Content Hub @@ -1037,11 +1124,11 @@ def post_page(post_id):

{{ post['title'] }}

{% if post['type'] == 'video' %} -
@@ -2497,62 +2915,77 @@ def admin_panel(): - Admin Panel - ADUSIS Hub + + Admin Panel - Content Hub @@ -2568,37 +3012,37 @@ def admin_panel(): ''' + NAV_HTML + '''
-

Admin Panel: All Posts

+

Admin Panel

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

{{ post['title'] }}

-
-

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

-

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

-
- - -
-
- {% endfor %} - {% else %} -

No posts found.

+ {% 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 %}
@@ -2606,23 +3050,30 @@ def admin_panel():