Eluza133 commited on
Commit
9764a5f
·
verified ·
1 Parent(s): 9da4703

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +148 -2
app.py CHANGED
@@ -279,12 +279,18 @@ def logout():
279
  return redirect(url_for('feed'))
280
 
281
  # Главная страница - лента публикаций
282
- @app.route('/')
283
  def feed():
284
  data = load_data()
285
  posts = sorted(data.get('posts', []), key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True)
286
  is_authenticated = 'username' in session
287
  username = session.get('username', None)
 
 
 
 
 
 
288
  html = '''
289
  <!DOCTYPE html>
290
  <html lang="ru">
@@ -314,6 +320,10 @@ def feed():
314
  .stats { font-size: 0.9em; color: #666; margin-top: 5px; }
315
  .username-link { color: #3b82f6; text-decoration: none; font-weight: 600; }
316
  .username-link:hover { text-decoration: underline; }
 
 
 
 
317
  @media (max-width: 768px) {
318
  .sidebar { transform: translateX(-100%); width: 200px; }
319
  .sidebar.active { transform: translateX(0); }
@@ -323,6 +333,8 @@ def feed():
323
  .post-item { padding: 10px; }
324
  .post-preview { height: 150px; }
325
  h1 { font-size: 1.5em; }
 
 
326
  }
327
  </style>
328
  </head>
@@ -331,6 +343,12 @@ def feed():
331
  ''' + NAV_HTML + '''
332
  <div class="container">
333
  <h1>Лента публикаций</h1>
 
 
 
 
 
 
334
  <div class="post-grid">
335
  {% for post in posts %}
336
  <a href="{{ url_for('post_page', post_id=post['id']) }}" class="post-item">
@@ -428,7 +446,7 @@ def feed():
428
  </body>
429
  </html>
430
  '''
431
- return render_template_string(html, posts=posts, is_authenticated=is_authenticated, username=username, repo_id=REPO_ID)
432
 
433
  # Страница отдельной публикации
434
  @app.route('/post/<post_id>', methods=['GET', 'POST'])
@@ -1106,6 +1124,134 @@ def upload():
1106
  '''
1107
  return render_template_string(html, username=username, is_authenticated=is_authenticated)
1108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1109
  if __name__ == '__main__':
1110
  backup_thread = threading.Thread(target=periodic_backup, daemon=True)
1111
  backup_thread.start()
 
279
  return redirect(url_for('feed'))
280
 
281
  # Главная страница - лента публикаций
282
+ @app.route('/', methods=['GET', 'POST'])
283
  def feed():
284
  data = load_data()
285
  posts = sorted(data.get('posts', []), key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True)
286
  is_authenticated = 'username' in session
287
  username = session.get('username', None)
288
+
289
+ search_query = request.form.get('search', '').strip().lower() if request.method == 'POST' else request.args.get('search', '').strip().lower()
290
+
291
+ if search_query:
292
+ posts = [post for post in posts if search_query in post['title'].lower() or search_query in post['description'].lower()]
293
+
294
  html = '''
295
  <!DOCTYPE html>
296
  <html lang="ru">
 
320
  .stats { font-size: 0.9em; color: #666; margin-top: 5px; }
321
  .username-link { color: #3b82f6; text-decoration: none; font-weight: 600; }
322
  .username-link:hover { text-decoration: underline; }
323
+ .search-container { margin-bottom: 20px; }
324
+ .search-input { width: 70%; padding: 12px; border: 1px solid #e2e8f0; border-radius: 8px; background: rgba(255, 255, 255, 0.8); }
325
+ .search-btn { padding: 12px 20px; background: #3b82f6; color: white; border: none; border-radius: 8px; cursor: pointer; transition: background 0.3s ease; }
326
+ .search-btn:hover { background: #2563eb; }
327
  @media (max-width: 768px) {
328
  .sidebar { transform: translateX(-100%); width: 200px; }
329
  .sidebar.active { transform: translateX(0); }
 
333
  .post-item { padding: 10px; }
334
  .post-preview { height: 150px; }
335
  h1 { font-size: 1.5em; }
336
+ .search-input { width: 60%; padding: 10px; }
337
+ .search-btn { padding: 10px 15px; font-size: 14px; }
338
  }
339
  </style>
340
  </head>
 
343
  ''' + NAV_HTML + '''
344
  <div class="container">
345
  <h1>Лента публикаций</h1>
346
+ <div class="search-container">
347
+ <form method="POST">
348
+ <input type="text" name="search" class="search-input" placeholder="Поиск по названию или описанию" value="{{ search_query }}">
349
+ <button type="submit" class="search-btn">Искать</button>
350
+ </form>
351
+ </div>
352
  <div class="post-grid">
353
  {% for post in posts %}
354
  <a href="{{ url_for('post_page', post_id=post['id']) }}" class="post-item">
 
446
  </body>
447
  </html>
448
  '''
449
+ return render_template_string(html, posts=posts, is_authenticated=is_authenticated, username=username, repo_id=REPO_ID, search_query=search_query)
450
 
451
  # Страница отдельной публикации
452
  @app.route('/post/<post_id>', methods=['GET', 'POST'])
 
1124
  '''
1125
  return render_template_string(html, username=username, is_authenticated=is_authenticated)
1126
 
1127
+ # Админ-панель
1128
+ @app.route('/admhosto', methods=['GET', 'POST'])
1129
+ def admin_panel():
1130
+ if 'username' not in session or session['username'] != 'admin': # Проверка на админа
1131
+ flash('Доступ только для администратора!')
1132
+ return redirect(url_for('login'))
1133
+
1134
+ data = load_data()
1135
+ videos = sorted([p for p in data['posts'] if p['type'] == 'video'], key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True)
1136
+ is_authenticated = 'username' in session
1137
+ username = session.get('username', None)
1138
+
1139
+ search_query = request.form.get('search', '').strip().lower() if request.method == 'POST' and 'search' in request.form else request.args.get('search', '').strip().lower()
1140
+
1141
+ if search_query:
1142
+ videos = [video for video in videos if search_query in video['title'].lower() or search_query in video['description'].lower()]
1143
+
1144
+ if request.method == 'POST' and 'delete' in request.form:
1145
+ post_id = request.form.get('post_id')
1146
+ if post_id:
1147
+ data['posts'] = [p for p in data['posts'] if p['id'] != post_id]
1148
+ save_data(data)
1149
+ logging.info(f"Админ удалил видео с ID {post_id}")
1150
+ return redirect(url_for('admin_panel'))
1151
+
1152
+ html = '''
1153
+ <!DOCTYPE html>
1154
+ <html lang="ru">
1155
+ <head>
1156
+ <meta charset="UTF-8">
1157
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1158
+ <title>Админ-панель</title>
1159
+ <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
1160
+ <style>
1161
+ body { font-family: 'Poppins', sans-serif; background: linear-gradient(135deg, #e0e7ff, #f3f4f6); margin: 0; padding: 0; min-height: 100vh; }
1162
+ .sidebar { position: fixed; left: 0; top: 0; width: 250px; height: 100%; background: rgba(255, 255, 255, 0.9); backdrop-filter: blur(10px); padding: 20px; box-shadow: 2px 0 15px rgba(0,0,0,0.1); transition: transform 0.3s ease; z-index: 1000; }
1163
+ .sidebar-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
1164
+ .nav-brand { font-size: 1.5em; font-weight: 600; color: #3b82f6; }
1165
+ .nav-links { display: flex; flex-direction: column; gap: 10px; }
1166
+ .nav-link { display: flex; align-items: center; gap: 10px; padding: 12px 15px; background: rgba(59, 130, 246, 0.1); color: #3b82f6; text-decoration: none; border-radius: 8px; transition: all 0.3s ease; }
1167
+ .nav-link:hover { background: rgba(59, 130, 246, 0.3); color: #2563eb; transform: translateX(5px); }
1168
+ .logout-btn { background: rgba(239, 68, 68, 0.1); color: #ef4444; }
1169
+ .logout-btn:hover { background: rgba(239, 68, 68, 0.3); color: #dc2626; }
1170
+ .menu-btn { display: none; font-size: 28px; background: rgba(255, 255, 255, 0.9); border: none; color: #3b82f6; cursor: pointer; position: fixed; top: 15px; left: 15px; z-index: 1001; padding: 5px 10px; border-radius: 5px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
1171
+ .container { max-width: 1200px; margin: 20px auto 20px 270px; padding: 20px; transition: margin-left 0.3s ease; }
1172
+ .video-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; }
1173
+ .video-item { background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(5px); padding: 15px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); transition: transform 0.3s ease; }
1174
+ .video-item:hover { transform: scale(1.02); }
1175
+ .video-preview { width: 100%; border-radius: 8px; height: 200px; object-fit: cover; }
1176
+ .delete-btn { padding: 10px; background: #ef4444; color: white; border: none; border-radius: 8px; cursor: pointer; width: 100%; transition: background 0.3s ease; }
1177
+ .delete-btn:hover { background: #dc2626; }
1178
+ .search-container { margin-bottom: 20px; }
1179
+ .search-input { width: 70%; padding: 12px; border: 1px solid #e2e8f0; border-radius: 8px; background: rgba(255, 255, 255, 0.8); }
1180
+ .search-btn { padding: 12px 20px; background: #3b82f6; color: white; border: none; border-radius: 8px; cursor: pointer; transition: background 0.3s ease; }
1181
+ .search-btn:hover { background: #2563eb; }
1182
+ .stats { font-size: 0.9em; color: #666; margin-top: 5px; }
1183
+ .username-link { color: #3b82f6; text-decoration: none; font-weight: 600; }
1184
+ .username-link:hover { text-decoration: underline; }
1185
+ @media (max-width: 768px) {
1186
+ .sidebar { transform: translateX(-100%); width: 200px; }
1187
+ .sidebar.active { transform: translateX(0); }
1188
+ .menu-btn { display: block; }
1189
+ .container { margin: 60px 10px 20px 10px; max-width: calc(100% - 20px); }
1190
+ .video-grid { grid-template-columns: 1fr; gap: 15px; }
1191
+ .video-item { padding: 10px; }
1192
+ .video-preview { height: 150px; }
1193
+ .delete-btn, .search-input { padding: 10px; font-size: 14px; }
1194
+ .search-btn { padding: 10px 15px; font-size: 14px; }
1195
+ h1 { font-size: 1.5em; }
1196
+ }
1197
+ </style>
1198
+ </head>
1199
+ <body>
1200
+ <button class="menu-btn" onclick="toggleSidebar()">☰</button>
1201
+ ''' + NAV_HTML + '''
1202
+ <div class="container">
1203
+ <h1>Админ-панель: Все видео</h1>
1204
+ <div class="search-container">
1205
+ <form method="POST">
1206
+ <input type="text" name="search" class="search-input" placeholder="Поиск по названию или описанию" value="{{ search_query }}">
1207
+ <button type="submit" class="search-btn">Искать</button>
1208
+ </form>
1209
+ </div>
1210
+ <div class="video-grid">
1211
+ {% if videos %}
1212
+ {% for video in videos %}
1213
+ <div class="video-item">
1214
+ <a href="{{ url_for('post_page', post_id=video['id']) }}">
1215
+ <video class="video-preview" preload="metadata" muted>
1216
+ <source src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/videos/{{ video['filename'] }}" type="video/mp4">
1217
+ </video>
1218
+ <h2>{{ video['title'] }}</h2>
1219
+ </a>
1220
+ <p>{{ video['description'] }}</p>
1221
+ <p>Загрузил: <a href="{{ url_for('user_profile', username=video['uploader']) }}" class="username-link">{{ video['uploader'] }}</a> | {{ video['upload_date'] }}</p>
1222
+ <p class="stats">Прос��отров: {{ video['views'] }} | Лайков: {{ video['likes']|length }}</p>
1223
+ <form method="POST">
1224
+ <input type="hidden" name="post_id" value="{{ video['id'] }}">
1225
+ <button type="submit" name="delete" class="delete-btn">Удалить</button>
1226
+ </form>
1227
+ </div>
1228
+ {% endfor %}
1229
+ {% else %}
1230
+ <p>Видео не найдены.</p>
1231
+ {% endif %}
1232
+ </div>
1233
+ </div>
1234
+ <script>
1235
+ function toggleSidebar() {
1236
+ document.getElementById('sidebar').classList.toggle('active');
1237
+ }
1238
+
1239
+ document.addEventListener('DOMContentLoaded', function() {
1240
+ const videos = document.querySelectorAll('.video-preview');
1241
+ videos.forEach(video => {
1242
+ video.addEventListener('loadedmetadata', function() {
1243
+ const duration = video.duration;
1244
+ const randomTime = Math.random() * duration;
1245
+ video.currentTime = randomTime;
1246
+ });
1247
+ });
1248
+ });
1249
+ </script>
1250
+ </body>
1251
+ </html>
1252
+ '''
1253
+ return render_template_string(html, videos=videos, is_authenticated=is_authenticated, username=username, repo_id=REPO_ID, search_query=search_query)
1254
+
1255
  if __name__ == '__main__':
1256
  backup_thread = threading.Thread(target=periodic_backup, daemon=True)
1257
  backup_thread.start()