Eluza133 commited on
Commit
5f8fab5
·
verified ·
1 Parent(s): 8b8c639

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +154 -100
app.py CHANGED
@@ -1,4 +1,6 @@
1
  from flask import Flask, render_template_string, request, redirect, url_for, session, flash
 
 
2
  import json
3
  import os
4
  import logging
@@ -7,26 +9,33 @@ import time
7
  from datetime import datetime, timedelta
8
  from huggingface_hub import HfApi, hf_hub_download
9
  from werkzeug.utils import secure_filename
 
 
10
  import random
11
 
12
  app = Flask(__name__)
13
- app.secret_key = 'supersecretkey' # Replace with a secure key in production
14
  DATA_FILE = 'data_adusis.json'
15
- REPO_ID = "Eluza133/w1f9"
16
- HF_TOKEN_WRITE = os.getenv("HF_TOKEN") # Write token
17
  HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") or HF_TOKEN_WRITE
18
 
19
- # Logging setup
20
  logging.basicConfig(level=logging.DEBUG)
21
 
22
- # Database functions
 
 
 
 
 
23
  def load_data():
24
  try:
25
  download_db_from_hf()
26
  with open(DATA_FILE, 'r', encoding='utf-8') as file:
27
  data = json.load(file)
28
  if not isinstance(data, dict):
29
- logging.warning("Data is not in dict format, initializing empty database")
30
  return {'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}}
31
  if 'posts' not in data:
32
  data['posts'] = []
@@ -42,11 +51,11 @@ def load_data():
42
  if 'last_private_visit' not in data['users'][user]:
43
  data['users'][user]['last_private_visit'] = '1970-01-01 00:00:00'
44
  if 'last_seen' not in data['users'][user]:
45
- data['users'][user]['last_seen'] = '1970-01-01 00:00:00' # Track last activity
46
- logging.info("Data loaded successfully")
47
  return data
48
  except Exception as e:
49
- logging.error(f"Error loading data: {e}")
50
  return {'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}}
51
 
52
  def save_data(data):
@@ -54,9 +63,10 @@ def save_data(data):
54
  with open(DATA_FILE, 'w', encoding='utf-8') as file:
55
  json.dump(data, file, ensure_ascii=False, indent=4)
56
  upload_db_to_hf()
57
- logging.info("Data saved and uploaded to HF")
 
58
  except Exception as e:
59
- logging.error(f"Error saving data: {e}")
60
  raise
61
 
62
  def upload_db_to_hf():
@@ -68,11 +78,11 @@ def upload_db_to_hf():
68
  repo_id=REPO_ID,
69
  repo_type="dataset",
70
  token=HF_TOKEN_WRITE,
71
- commit_message=f"Backup {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
72
  )
73
- logging.info("Database uploaded to Hugging Face")
74
  except Exception as e:
75
- logging.error(f"Error uploading database: {e}")
76
 
77
  def download_db_from_hf():
78
  try:
@@ -84,9 +94,9 @@ def download_db_from_hf():
84
  local_dir=".",
85
  local_dir_use_symlinks=False
86
  )
87
- logging.info("Database downloaded from Hugging Face")
88
  except Exception as e:
89
- logging.error(f"Error downloading database: {e}")
90
  if not os.path.exists(DATA_FILE):
91
  with open(DATA_FILE, 'w', encoding='utf-8') as f:
92
  json.dump({'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}}, f)
@@ -117,14 +127,14 @@ def is_user_online(data, username):
117
  if username not in data['users']:
118
  return False
119
  last_seen = datetime.strptime(data['users'][username]['last_seen'], '%Y-%m-%d %H:%M:%S')
120
- return (datetime.now() - last_seen).total_seconds() < 300 # Online if active within 5 minutes
121
 
122
  def update_last_seen(data, username):
123
  if username in data['users']:
124
  data['users'][username]['last_seen'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
125
  save_data(data)
126
 
127
- # Updated base style
128
  BASE_STYLE = '''
129
  :root {
130
  --primary: #ff4d6d; /* Vivid pink-red */
@@ -423,7 +433,7 @@ NAV_HTML = '''
423
  </aside>
424
  '''
425
 
426
- # Registration
427
  @app.route('/register', methods=['GET', 'POST'])
428
  def register():
429
  if request.method == 'POST':
@@ -432,7 +442,7 @@ def register():
432
  data = load_data()
433
 
434
  if username in data['users']:
435
- flash('User already exists!')
436
  return redirect(url_for('register'))
437
 
438
  data['users'][username] = {
@@ -445,7 +455,7 @@ def register():
445
  'last_seen': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
446
  }
447
  save_data(data)
448
- flash('Registration successful! Please log in.')
449
  return redirect(url_for('login'))
450
 
451
  is_authenticated = 'username' in session
@@ -546,7 +556,7 @@ def register():
546
  '''
547
  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)
548
 
549
- # Login
550
  @app.route('/login', methods=['GET', 'POST'])
551
  def login():
552
  data = load_data()
@@ -559,7 +569,7 @@ def login():
559
  session.permanent = True
560
  update_last_seen(data, username)
561
  return redirect(url_for('feed'))
562
- flash('Invalid username or password!')
563
  return redirect(url_for('login'))
564
 
565
  is_authenticated = 'username' in session
@@ -660,7 +670,7 @@ def login():
660
  '''
661
  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)
662
 
663
- # Logout
664
  @app.route('/logout')
665
  def logout():
666
  data = load_data()
@@ -670,7 +680,7 @@ def logout():
670
  session.pop('username', None)
671
  return redirect(url_for('feed'))
672
 
673
- # Increment view count
674
  @app.route('/increment_view/<post_id>', methods=['POST'])
675
  def increment_view(post_id):
676
  data = load_data()
@@ -679,16 +689,16 @@ def increment_view(post_id):
679
  post['views'] = post.get('views', 0) + 1
680
  save_data(data)
681
  return '', 204
682
- return 'Post not found', 404
683
 
684
- # Feed
685
  @app.route('/', methods=['GET', 'POST'])
686
  def feed():
687
  data = load_data()
688
  username = session.get('username', None)
689
  if username:
690
  update_last_seen(data, username)
691
- posts = sorted(data.get('posts', []), key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True)
692
  is_authenticated = 'username' in session
693
  unread_count = get_unread_count(data, username) if is_authenticated else 0
694
  private_unread_count = get_private_unread_count(data, username) if is_authenticated else 0
@@ -874,7 +884,7 @@ def feed():
874
  '''
875
  return render_template_string(html, posts=posts, is_authenticated=is_authenticated, username=username, repo_id=REPO_ID, search_query=search_query, 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))
876
 
877
- # Post page
878
  @app.route('/post/<post_id>', methods=['GET', 'POST'])
879
  def post_page(post_id):
880
  data = load_data()
@@ -883,7 +893,7 @@ def post_page(post_id):
883
  update_last_seen(data, username)
884
  post = next((p for p in data['posts'] if p['id'] == post_id), None)
885
  if not post:
886
- return "Post not found", 404
887
 
888
  is_authenticated = 'username' in session
889
  unread_count = get_unread_count(data, username) if is_authenticated else 0
@@ -1043,11 +1053,11 @@ def post_page(post_id):
1043
  '''
1044
  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))
1045
 
1046
- # User profile
1047
  @app.route('/profile', methods=['GET', 'POST'])
1048
  def profile():
1049
  if 'username' not in session:
1050
- flash('Login to view your profile!')
1051
  return redirect(url_for('login'))
1052
 
1053
  data = load_data()
@@ -1092,7 +1102,7 @@ def profile():
1092
  repo_id=REPO_ID,
1093
  repo_type="dataset",
1094
  token=HF_TOKEN_WRITE,
1095
- commit_message=f"Uploaded avatar for {username}"
1096
  )
1097
  data['users'][username]['avatar'] = avatar_path
1098
  if os.path.exists(temp_path):
@@ -1310,7 +1320,7 @@ def profile():
1310
  '''
1311
  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)
1312
 
1313
- # Other user's profile
1314
  @app.route('/profile/<username>')
1315
  def user_profile(username):
1316
  data = load_data()
@@ -1318,7 +1328,7 @@ def user_profile(username):
1318
  if session_username:
1319
  update_last_seen(data, session_username)
1320
  if username not in data['users']:
1321
- return "User not found", 404
1322
 
1323
  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)
1324
  is_authenticated = 'username' in session
@@ -1417,19 +1427,13 @@ def user_profile(username):
1417
  font-weight: 600;
1418
  margin-bottom: 10px;
1419
  }
1420
- .share-btn {
1421
- background: var(--accent);
1422
- }
1423
- .share-btn:hover {
1424
- background: #7a4de0;
1425
  }
1426
- h2 {
1427
- font-size: 1.8em;
1428
- font-weight: 800;
1429
- margin: 35px 0 20px;
1430
- background: linear-gradient(135deg, var(--primary), var(--accent));
1431
- -webkit-background-clip: text;
1432
- color: transparent;
1433
  }
1434
  </style>
1435
  </head>
@@ -1452,31 +1456,28 @@ def user_profile(username):
1452
  <p><a href="{{ link }}" target="_blank" style="color: var(--primary);">{{ link }}</a></p>
1453
  {% endif %}
1454
  <p>Views: {{ total_views }} | Likes: {{ total_likes }}</p>
1455
- <button class="btn share-btn" onclick="copyProfileLink()">Share</button>
 
 
1456
  </div>
1457
  </div>
1458
- <h2>Posts</h2>
1459
  <div class="post-grid">
1460
- {% if user_posts %}
1461
- {% for post in user_posts %}
1462
- <div class="post-item">
1463
- <a href="{{ url_for('post_page', post_id=post['id']) }}">
1464
- {% if post['type'] == 'video' %}
1465
- <video class="post-preview" preload="metadata" muted>
1466
- <source src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ post['type'] }}s/{{ post['filename'] }}" type="video/mp4">
1467
- </video>
1468
- {% else %}
1469
- <img class="post-preview" src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ post['type'] }}s/{{ post['filename'] }}" alt="{{ post['title'] }}" onclick="openModal(this.src, event, '{{ post['id'] }}')">
1470
- {% endif %}
1471
- <h3>{{ post['title'] }}</h3>
1472
- </a>
1473
- <p>{{ post['description'] }}</p>
1474
- <p>{{ post['upload_date'] }}</p>
1475
- </div>
1476
- {% endfor %}
1477
- {% else %}
1478
- <p style="font-size: 1.1em;">This user hasn't uploaded any posts yet.</p>
1479
- {% endif %}
1480
  </div>
1481
  </div>
1482
  <div class="modal" id="imageModal" onclick="closeModal(event)">
@@ -1503,11 +1504,6 @@ def user_profile(username):
1503
  document.getElementById('imageModal').style.display = 'none';
1504
  }
1505
  }
1506
- function copyProfileLink() {
1507
- navigator.clipboard.writeText(window.location.href).then(() => {
1508
- alert('Link copied!');
1509
- });
1510
- }
1511
  window.onload = () => {
1512
  if (localStorage.getItem('theme') === 'dark') document.body.classList.add('dark');
1513
  const videos = document.querySelectorAll('.post-preview');
@@ -1523,38 +1519,59 @@ def user_profile(username):
1523
  </body>
1524
  </html>
1525
  '''
1526
- 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)
1527
 
1528
- # Upload content
1529
  @app.route('/upload', methods=['GET', 'POST'])
1530
  def upload():
1531
  if 'username' not in session:
1532
- flash('Login to upload content!')
1533
  return redirect(url_for('login'))
1534
 
1535
  data = load_data()
1536
- username = session.get('username', None)
1537
  update_last_seen(data, username)
 
1538
  unread_count = get_unread_count(data, username)
1539
  private_unread_count = get_private_unread_count(data, username)
1540
  user_count = len(data['users'])
1541
  is_online = is_user_online(data, username)
1542
-
1543
  if request.method == 'POST':
1544
  title = request.form.get('title')
1545
- description = request.form.get('description')
1546
  file = request.files.get('file')
1547
- uploader = session['username']
1548
-
1549
- if not title or not file:
1550
- return "Please provide a title and select a file", 400
1551
 
1552
  filename = secure_filename(file.filename)
1553
  temp_path = os.path.join('uploads', filename)
1554
  os.makedirs('uploads', exist_ok=True)
1555
  file.save(temp_path)
1556
 
 
1557
  file_type = 'video' if filename.lower().endswith(('.mp4', '.mov', '.avi')) else 'photo'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1558
  api = HfApi()
1559
  api.upload_file(
1560
  path_or_fileobj=temp_path,
@@ -1562,7 +1579,7 @@ def upload():
1562
  repo_id=REPO_ID,
1563
  repo_type="dataset",
1564
  token=HF_TOKEN_WRITE,
1565
- commit_message=f"Uploaded post: {title} by {uploader}"
1566
  )
1567
 
1568
  data = load_data()
@@ -1575,7 +1592,7 @@ def upload():
1575
  'description': description,
1576
  'filename': filename,
1577
  'type': file_type,
1578
- 'uploader': uploader,
1579
  'upload_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
1580
  'views': 0,
1581
  'likes': [],
@@ -1587,7 +1604,6 @@ def upload():
1587
  os.remove(temp_path)
1588
  return redirect(url_for('profile'))
1589
 
1590
- is_authenticated = 'username' in session
1591
  html = '''
1592
  <!DOCTYPE html>
1593
  <html lang="en">
@@ -1646,7 +1662,7 @@ def upload():
1646
  <form id="upload-form" enctype="multipart/form-data">
1647
  <input type="text" name="title" placeholder="Title" required>
1648
  <textarea name="description" placeholder="Description" rows="4"></textarea>
1649
- <input type="file" name="file" accept="video/*,image/*" required>
1650
  <button type="submit" class="btn">Upload</button>
1651
  </form>
1652
  <div id="progress-container">
@@ -1691,7 +1707,7 @@ def upload():
1691
  '''
1692
  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)
1693
 
1694
- # General Chat
1695
  @app.route('/chat', methods=['GET', 'POST'])
1696
  def chat():
1697
  data = load_data()
@@ -1730,6 +1746,24 @@ def chat():
1730
  file.save(temp_path)
1731
 
1732
  file_type = 'video' if filename.lower().endswith(('.mp4', '.mov', '.avi')) else 'photo'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1733
  api = HfApi()
1734
  file_path = f"chat_files/general/{filename}"
1735
  api.upload_file(
@@ -1793,7 +1827,7 @@ def chat():
1793
  background: var(--glass-bg);
1794
  border-radius: 15px;
1795
  margin-bottom: 20px;
1796
- scroll-behavior: smooth;
1797
  }
1798
  .message {
1799
  padding: 12px 18px;
@@ -1916,7 +1950,7 @@ def chat():
1916
  {% if is_authenticated %}
1917
  <form method="POST" enctype="multipart/form-data" class="message-form">
1918
  <textarea name="message" placeholder="Type a message" rows="2"></textarea>
1919
- <input type="file" name="file" accept="video/*,image/*">
1920
  <select name="post_id" class="post-select">
1921
  <option value="">Share a post (optional)</option>
1922
  {% for post in user_posts %}
@@ -1974,7 +2008,7 @@ def chat():
1974
  is_online=is_online,
1975
  is_user_online=lambda u: is_user_online(data, u))
1976
 
1977
- # Users page
1978
  @app.route('/users', methods=['GET', 'POST'])
1979
  def users():
1980
  data = load_data()
@@ -2149,11 +2183,11 @@ def users():
2149
  '''
2150
  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)
2151
 
2152
- # Messages page (list of private dialogs)
2153
  @app.route('/messages', methods=['GET'])
2154
  def messages():
2155
  if 'username' not in session:
2156
- flash('Login to view messages!')
2157
  return redirect(url_for('login'))
2158
 
2159
  data = load_data()
@@ -2168,6 +2202,8 @@ def messages():
2168
  dialogs = {}
2169
  for chat_key, messages in data['private_chats'].items():
2170
  user1, user2 = chat_key.split('_')
 
 
2171
  other_user = user1 if user2 == username else user2
2172
  last_message = messages[-1] if messages else None
2173
  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)
@@ -2307,18 +2343,18 @@ def messages():
2307
  '''
2308
  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)
2309
 
2310
- # Private Chat
2311
  @app.route('/chat/<recipient>', methods=['GET', 'POST'])
2312
  def private_chat(recipient):
2313
  if 'username' not in session:
2314
- flash('Login to chat!')
2315
  return redirect(url_for('login'))
2316
 
2317
  data = load_data()
2318
  username = session['username']
2319
  update_last_seen(data, username)
2320
  if recipient not in data['users']:
2321
- return "User not found", 404
2322
 
2323
  is_authenticated = 'username' in session
2324
  unread_count = get_unread_count(data, username)
@@ -2326,7 +2362,7 @@ def private_chat(recipient):
2326
  user_count = len(data['users'])
2327
  is_online = is_user_online(data, username)
2328
 
2329
- # Update last_private_visit
2330
  data['users'][username]['last_private_visit'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
2331
  save_data(data)
2332
 
@@ -2355,6 +2391,24 @@ def private_chat(recipient):
2355
  file.save(temp_path)
2356
 
2357
  file_type = 'video' if filename.lower().endswith(('.mp4', '.mov', '.avi')) else 'photo'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2358
  api = HfApi()
2359
  file_path = f"chat_files/private/{chat_key}/{filename}"
2360
  api.upload_file(
@@ -2622,7 +2676,7 @@ def private_chat(recipient):
2622
  </div>
2623
  <form method="POST" enctype="multipart/form-data" class="message-form">
2624
  <textarea name="message" placeholder="Type a message" rows="2"></textarea>
2625
- <input type="file" name="file" accept="video/*,image/*">
2626
  <select name="post_id" class="post-select">
2627
  <option value="">Share a post (optional)</option>
2628
  {% for post in user_posts %}
@@ -2682,7 +2736,7 @@ def private_chat(recipient):
2682
  recipient_avatar=recipient_avatar,
2683
  recipient_online=recipient_online)
2684
 
2685
- # Admin panel
2686
  @app.route('/admhosto', methods=['GET', 'POST'])
2687
  def admin_panel():
2688
  data = load_data()
 
1
  from flask import Flask, render_template_string, request, redirect, url_for, session, flash
2
+ from flask_caching import Cache
3
+ from flask_compress import Compress
4
  import json
5
  import os
6
  import logging
 
9
  from datetime import datetime, timedelta
10
  from huggingface_hub import HfApi, hf_hub_download
11
  from werkzeug.utils import secure_filename
12
+ from PIL import Image
13
+ import pyheif
14
  import random
15
 
16
  app = Flask(__name__)
17
+ app.secret_key = 'supersecretkey' # Замените на безопасный ключ в продакшене
18
  DATA_FILE = 'data_adusis.json'
19
+ REPO_ID = "Eluza133/A12d12s12"
20
+ HF_TOKEN_WRITE = os.getenv("HF_TOKEN") # Токен для записи
21
  HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") or HF_TOKEN_WRITE
22
 
23
+ # Настройка логирования
24
  logging.basicConfig(level=logging.DEBUG)
25
 
26
+ # Инициализация кэширования и сжатия
27
+ cache = Cache(app, config={'CACHE_TYPE': 'simple'})
28
+ Compress(app)
29
+
30
+ # Функции работы с базой данных
31
+ @cache.memoize(timeout=60) # Кэш на 60 секунд
32
  def load_data():
33
  try:
34
  download_db_from_hf()
35
  with open(DATA_FILE, 'r', encoding='utf-8') as file:
36
  data = json.load(file)
37
  if not isinstance(data, dict):
38
+ logging.warning("Данные не в формате dict, инициализация пустой базы")
39
  return {'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}}
40
  if 'posts' not in data:
41
  data['posts'] = []
 
51
  if 'last_private_visit' not in data['users'][user]:
52
  data['users'][user]['last_private_visit'] = '1970-01-01 00:00:00'
53
  if 'last_seen' not in data['users'][user]:
54
+ data['users'][user]['last_seen'] = '1970-01-01 00:00:00'
55
+ logging.info("Данные успешно загружены")
56
  return data
57
  except Exception as e:
58
+ logging.error(f"Ошибка при загрузке данных: {e}")
59
  return {'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}}
60
 
61
  def save_data(data):
 
63
  with open(DATA_FILE, 'w', encoding='utf-8') as file:
64
  json.dump(data, file, ensure_ascii=False, indent=4)
65
  upload_db_to_hf()
66
+ logging.info("Данные сохранены и загружены на HF")
67
+ cache.delete_memoized('load_data') # Очистка кэша после сохранения
68
  except Exception as e:
69
+ logging.error(f"Ошибка при сохранении данных: {e}")
70
  raise
71
 
72
  def upload_db_to_hf():
 
78
  repo_id=REPO_ID,
79
  repo_type="dataset",
80
  token=HF_TOKEN_WRITE,
81
+ commit_message=f"Бэкап {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
82
  )
83
+ logging.info("База данных загружена на Hugging Face")
84
  except Exception as e:
85
+ logging.error(f"Ошибка при загрузке базы данных: {e}")
86
 
87
  def download_db_from_hf():
88
  try:
 
94
  local_dir=".",
95
  local_dir_use_symlinks=False
96
  )
97
+ logging.info("База данных скачана с Hugging Face")
98
  except Exception as e:
99
+ logging.error(f"Ошибка при скачивании базы данных: {e}")
100
  if not os.path.exists(DATA_FILE):
101
  with open(DATA_FILE, 'w', encoding='utf-8') as f:
102
  json.dump({'posts': [], 'users': {}, 'general_chat': [], 'private_chats': {}}, f)
 
127
  if username not in data['users']:
128
  return False
129
  last_seen = datetime.strptime(data['users'][username]['last_seen'], '%Y-%m-%d %H:%M:%S')
130
+ return (datetime.now() - last_seen).total_seconds() < 300 # Онлайн, если активен в последние 5 минут
131
 
132
  def update_last_seen(data, username):
133
  if username in data['users']:
134
  data['users'][username]['last_seen'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
135
  save_data(data)
136
 
137
+ # Исходный стиль
138
  BASE_STYLE = '''
139
  :root {
140
  --primary: #ff4d6d; /* Vivid pink-red */
 
433
  </aside>
434
  '''
435
 
436
+ # Регистрация
437
  @app.route('/register', methods=['GET', 'POST'])
438
  def register():
439
  if request.method == 'POST':
 
442
  data = load_data()
443
 
444
  if username in data['users']:
445
+ flash('Пользователь уже существует!')
446
  return redirect(url_for('register'))
447
 
448
  data['users'][username] = {
 
455
  'last_seen': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
456
  }
457
  save_data(data)
458
+ flash('Регистрация успешна! Пожалуйста, войдите.')
459
  return redirect(url_for('login'))
460
 
461
  is_authenticated = 'username' in session
 
556
  '''
557
  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)
558
 
559
+ # Вход
560
  @app.route('/login', methods=['GET', 'POST'])
561
  def login():
562
  data = load_data()
 
569
  session.permanent = True
570
  update_last_seen(data, username)
571
  return redirect(url_for('feed'))
572
+ flash('Неверное имя пользователя или пароль!')
573
  return redirect(url_for('login'))
574
 
575
  is_authenticated = 'username' in session
 
670
  '''
671
  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)
672
 
673
+ # Выход
674
  @app.route('/logout')
675
  def logout():
676
  data = load_data()
 
680
  session.pop('username', None)
681
  return redirect(url_for('feed'))
682
 
683
+ # Увеличение счетчика просмотров
684
  @app.route('/increment_view/<post_id>', methods=['POST'])
685
  def increment_view(post_id):
686
  data = load_data()
 
689
  post['views'] = post.get('views', 0) + 1
690
  save_data(data)
691
  return '', 204
692
+ return 'Пост не найден', 404
693
 
694
+ # Лента
695
  @app.route('/', methods=['GET', 'POST'])
696
  def feed():
697
  data = load_data()
698
  username = session.get('username', None)
699
  if username:
700
  update_last_seen(data, username)
701
+ posts = sorted(data.get('posts', []), key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True)[:20] # Только первые 20 постов
702
  is_authenticated = 'username' in session
703
  unread_count = get_unread_count(data, username) if is_authenticated else 0
704
  private_unread_count = get_private_unread_count(data, username) if is_authenticated else 0
 
884
  '''
885
  return render_template_string(html, posts=posts, is_authenticated=is_authenticated, username=username, repo_id=REPO_ID, search_query=search_query, 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))
886
 
887
+ # Страница поста
888
  @app.route('/post/<post_id>', methods=['GET', 'POST'])
889
  def post_page(post_id):
890
  data = load_data()
 
893
  update_last_seen(data, username)
894
  post = next((p for p in data['posts'] if p['id'] == post_id), None)
895
  if not post:
896
+ return "Пост не найден", 404
897
 
898
  is_authenticated = 'username' in session
899
  unread_count = get_unread_count(data, username) if is_authenticated else 0
 
1053
  '''
1054
  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))
1055
 
1056
+ # Профиль пользователя
1057
  @app.route('/profile', methods=['GET', 'POST'])
1058
  def profile():
1059
  if 'username' not in session:
1060
+ flash('Войдите, чтобы просмотреть свой профиль!')
1061
  return redirect(url_for('login'))
1062
 
1063
  data = load_data()
 
1102
  repo_id=REPO_ID,
1103
  repo_type="dataset",
1104
  token=HF_TOKEN_WRITE,
1105
+ commit_message=f"Загружен аватар для {username}"
1106
  )
1107
  data['users'][username]['avatar'] = avatar_path
1108
  if os.path.exists(temp_path):
 
1320
  '''
1321
  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)
1322
 
1323
+ # Профиль другого пользователя
1324
  @app.route('/profile/<username>')
1325
  def user_profile(username):
1326
  data = load_data()
 
1328
  if session_username:
1329
  update_last_seen(data, session_username)
1330
  if username not in data['users']:
1331
+ return "Пользователь не найден", 404
1332
 
1333
  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)
1334
  is_authenticated = 'username' in session
 
1427
  font-weight: 600;
1428
  margin-bottom: 10px;
1429
  }
1430
+ .message-btn {
1431
+ background: var(--secondary);
1432
+ padding: 8px 16px;
1433
+ font-size: 0.9em;
 
1434
  }
1435
+ .message-btn:hover {
1436
+ background: #00b8c5;
 
 
 
 
 
1437
  }
1438
  </style>
1439
  </head>
 
1456
  <p><a href="{{ link }}" target="_blank" style="color: var(--primary);">{{ link }}</a></p>
1457
  {% endif %}
1458
  <p>Views: {{ total_views }} | Likes: {{ total_likes }}</p>
1459
+ {% if is_authenticated and username != session_username %}
1460
+ <a href="{{ url_for('private_chat', recipient=username) }}" class="btn message-btn">Send Message</a>
1461
+ {% endif %}
1462
  </div>
1463
  </div>
 
1464
  <div class="post-grid">
1465
+ {% for post in user_posts %}
1466
+ <div class="post-item">
1467
+ <a href="{{ url_for('post_page', post_id=post['id']) }}">
1468
+ {% if post['type'] == 'video' %}
1469
+ <video class="post-preview" preload="metadata" muted>
1470
+ <source src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ post['type'] }}s/{{ post['filename'] }}" type="video/mp4">
1471
+ </video>
1472
+ {% else %}
1473
+ <img class="post-preview" src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ post['type'] }}s/{{ post['filename'] }}" alt="{{ post['title'] }}" onclick="openModal(this.src, event, '{{ post['id'] }}')">
1474
+ {% endif %}
1475
+ <h3>{{ post['title'] }}</h3>
1476
+ </a>
1477
+ <p>{{ post['description'] }}</p>
1478
+ <p>{{ post['upload_date'] }}</p>
1479
+ </div>
1480
+ {% endfor %}
 
 
 
 
1481
  </div>
1482
  </div>
1483
  <div class="modal" id="imageModal" onclick="closeModal(event)">
 
1504
  document.getElementById('imageModal').style.display = 'none';
1505
  }
1506
  }
 
 
 
 
 
1507
  window.onload = () => {
1508
  if (localStorage.getItem('theme') === 'dark') document.body.classList.add('dark');
1509
  const videos = document.querySelectorAll('.post-preview');
 
1519
  </body>
1520
  </html>
1521
  '''
1522
+ return render_template_string(html, username=username, session_username=session_username, user_posts=user_posts, total_views=total_views, total_likes=total_likes, bio=bio, link=link, avatar=avatar, profile_is_online=profile_is_online, last_seen=last_seen, 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)
1523
 
1524
+ # Загрузка контента
1525
  @app.route('/upload', methods=['GET', 'POST'])
1526
  def upload():
1527
  if 'username' not in session:
1528
+ flash('Войдите, чтобы загрузить контент!')
1529
  return redirect(url_for('login'))
1530
 
1531
  data = load_data()
1532
+ username = session['username']
1533
  update_last_seen(data, username)
1534
+ is_authenticated = 'username' in session
1535
  unread_count = get_unread_count(data, username)
1536
  private_unread_count = get_private_unread_count(data, username)
1537
  user_count = len(data['users'])
1538
  is_online = is_user_online(data, username)
1539
+
1540
  if request.method == 'POST':
1541
  title = request.form.get('title')
1542
+ description = request.form.get('description', '')
1543
  file = request.files.get('file')
1544
+
1545
+ if not file or not title:
1546
+ flash('Заполните все поля!')
1547
+ return redirect(url_for('upload'))
1548
 
1549
  filename = secure_filename(file.filename)
1550
  temp_path = os.path.join('uploads', filename)
1551
  os.makedirs('uploads', exist_ok=True)
1552
  file.save(temp_path)
1553
 
1554
+ # Определяем тип файла
1555
  file_type = 'video' if filename.lower().endswith(('.mp4', '.mov', '.avi')) else 'photo'
1556
+ if filename.lower().endswith(('.heic', '.heif')):
1557
+ # Конверсия HEIC в JPEG
1558
+ heif_file = pyheif.read(temp_path)
1559
+ image = Image.frombytes(
1560
+ heif_file.mode,
1561
+ heif_file.size,
1562
+ heif_file.data,
1563
+ "raw",
1564
+ heif_file.mode,
1565
+ heif_file.stride,
1566
+ )
1567
+ new_filename = filename.rsplit('.', 1)[0] + '.jpg'
1568
+ temp_new_path = os.path.join('uploads', new_filename)
1569
+ image.save(temp_new_path, "JPEG")
1570
+ os.remove(temp_path)
1571
+ temp_path = temp_new_path
1572
+ filename = new_filename
1573
+ file_type = 'photo'
1574
+
1575
  api = HfApi()
1576
  api.upload_file(
1577
  path_or_fileobj=temp_path,
 
1579
  repo_id=REPO_ID,
1580
  repo_type="dataset",
1581
  token=HF_TOKEN_WRITE,
1582
+ commit_message=f"Uploaded post: {title} by {username}"
1583
  )
1584
 
1585
  data = load_data()
 
1592
  'description': description,
1593
  'filename': filename,
1594
  'type': file_type,
1595
+ 'uploader': username,
1596
  'upload_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
1597
  'views': 0,
1598
  'likes': [],
 
1604
  os.remove(temp_path)
1605
  return redirect(url_for('profile'))
1606
 
 
1607
  html = '''
1608
  <!DOCTYPE html>
1609
  <html lang="en">
 
1662
  <form id="upload-form" enctype="multipart/form-data">
1663
  <input type="text" name="title" placeholder="Title" required>
1664
  <textarea name="description" placeholder="Description" rows="4"></textarea>
1665
+ <input type="file" name="file" accept="image/*,video/*,image/gif,image/heic,image/heif" required>
1666
  <button type="submit" class="btn">Upload</button>
1667
  </form>
1668
  <div id="progress-container">
 
1707
  '''
1708
  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)
1709
 
1710
+ # Общий чат
1711
  @app.route('/chat', methods=['GET', 'POST'])
1712
  def chat():
1713
  data = load_data()
 
1746
  file.save(temp_path)
1747
 
1748
  file_type = 'video' if filename.lower().endswith(('.mp4', '.mov', '.avi')) else 'photo'
1749
+ if filename.lower().endswith(('.heic', '.heif')):
1750
+ heif_file = pyheif.read(temp_path)
1751
+ image = Image.frombytes(
1752
+ heif_file.mode,
1753
+ heif_file.size,
1754
+ heif_file.data,
1755
+ "raw",
1756
+ heif_file.mode,
1757
+ heif_file.stride,
1758
+ )
1759
+ new_filename = filename.rsplit('.', 1)[0] + '.jpg'
1760
+ temp_new_path = os.path.join('uploads', new_filename)
1761
+ image.save(temp_new_path, "JPEG")
1762
+ os.remove(temp_path)
1763
+ temp_path = temp_new_path
1764
+ filename = new_filename
1765
+ file_type = 'photo'
1766
+
1767
  api = HfApi()
1768
  file_path = f"chat_files/general/{filename}"
1769
  api.upload_file(
 
1827
  background: var(--glass-bg);
1828
  border-radius: 15px;
1829
  margin-bottom: 20px;
1830
+ scroll-behavior: smooth;
1831
  }
1832
  .message {
1833
  padding: 12px 18px;
 
1950
  {% if is_authenticated %}
1951
  <form method="POST" enctype="multipart/form-data" class="message-form">
1952
  <textarea name="message" placeholder="Type a message" rows="2"></textarea>
1953
+ <input type="file" name="file" accept="image/*,video/*,image/gif,image/heic,image/heif">
1954
  <select name="post_id" class="post-select">
1955
  <option value="">Share a post (optional)</option>
1956
  {% for post in user_posts %}
 
2008
  is_online=is_online,
2009
  is_user_online=lambda u: is_user_online(data, u))
2010
 
2011
+ # Страница пользователей
2012
  @app.route('/users', methods=['GET', 'POST'])
2013
  def users():
2014
  data = load_data()
 
2183
  '''
2184
  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)
2185
 
2186
+ # Страница сообщений (список приватных диалогов)
2187
  @app.route('/messages', methods=['GET'])
2188
  def messages():
2189
  if 'username' not in session:
2190
+ flash('Войдите, чтобы просмотреть сообщения!')
2191
  return redirect(url_for('login'))
2192
 
2193
  data = load_data()
 
2202
  dialogs = {}
2203
  for chat_key, messages in data['private_chats'].items():
2204
  user1, user2 = chat_key.split('_')
2205
+ if username not in (user1, user2): # Пропускаем чаты, где пользователь не участвует
2206
+ continue
2207
  other_user = user1 if user2 == username else user2
2208
  last_message = messages[-1] if messages else None
2209
  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)
 
2343
  '''
2344
  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)
2345
 
2346
+ # Приватный чат
2347
  @app.route('/chat/<recipient>', methods=['GET', 'POST'])
2348
  def private_chat(recipient):
2349
  if 'username' not in session:
2350
+ flash('Войдите, чтобы начать чат!')
2351
  return redirect(url_for('login'))
2352
 
2353
  data = load_data()
2354
  username = session['username']
2355
  update_last_seen(data, username)
2356
  if recipient not in data['users']:
2357
+ return "Пользователь не найден", 404
2358
 
2359
  is_authenticated = 'username' in session
2360
  unread_count = get_unread_count(data, username)
 
2362
  user_count = len(data['users'])
2363
  is_online = is_user_online(data, username)
2364
 
2365
+ # Обновляем время последнего посещения приватных чатов
2366
  data['users'][username]['last_private_visit'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
2367
  save_data(data)
2368
 
 
2391
  file.save(temp_path)
2392
 
2393
  file_type = 'video' if filename.lower().endswith(('.mp4', '.mov', '.avi')) else 'photo'
2394
+ if filename.lower().endswith(('.heic', '.heif')):
2395
+ heif_file = pyheif.read(temp_path)
2396
+ image = Image.frombytes(
2397
+ heif_file.mode,
2398
+ heif_file.size,
2399
+ heif_file.data,
2400
+ "raw",
2401
+ heif_file.mode,
2402
+ heif_file.stride,
2403
+ )
2404
+ new_filename = filename.rsplit('.', 1)[0] + '.jpg'
2405
+ temp_new_path = os.path.join('uploads', new_filename)
2406
+ image.save(temp_new_path, "JPEG")
2407
+ os.remove(temp_path)
2408
+ temp_path = temp_new_path
2409
+ filename = new_filename
2410
+ file_type = 'photo'
2411
+
2412
  api = HfApi()
2413
  file_path = f"chat_files/private/{chat_key}/{filename}"
2414
  api.upload_file(
 
2676
  </div>
2677
  <form method="POST" enctype="multipart/form-data" class="message-form">
2678
  <textarea name="message" placeholder="Type a message" rows="2"></textarea>
2679
+ <input type="file" name="file" accept="image/*,video/*,image/gif,image/heic,image/heif">
2680
  <select name="post_id" class="post-select">
2681
  <option value="">Share a post (optional)</option>
2682
  {% for post in user_posts %}
 
2736
  recipient_avatar=recipient_avatar,
2737
  recipient_online=recipient_online)
2738
 
2739
+ # Админ-панель
2740
  @app.route('/admhosto', methods=['GET', 'POST'])
2741
  def admin_panel():
2742
  data = load_data()