Update app.py
Browse files
app.py
CHANGED
|
@@ -636,6 +636,10 @@ def profile():
|
|
| 636 |
username = session['username']
|
| 637 |
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)
|
| 638 |
is_authenticated = 'username' in session
|
|
|
|
|
|
|
|
|
|
|
|
|
| 639 |
|
| 640 |
if request.method == 'POST':
|
| 641 |
post_id = request.form.get('post_id')
|
|
@@ -694,6 +698,7 @@ def profile():
|
|
| 694 |
''' + NAV_HTML + '''
|
| 695 |
<div class="container">
|
| 696 |
<h1>Профиль: {{ username }}</h1>
|
|
|
|
| 697 |
<a href="{{ url_for('upload') }}" class="btn">Добавить публикацию</a>
|
| 698 |
<h2>Ваши публикации</h2>
|
| 699 |
<div class="post-grid">
|
|
@@ -803,7 +808,7 @@ def profile():
|
|
| 803 |
</body>
|
| 804 |
</html>
|
| 805 |
'''
|
| 806 |
-
return render_template_string(html, username=username, user_posts=user_posts, repo_id=REPO_ID, is_authenticated=is_authenticated)
|
| 807 |
|
| 808 |
# Страница профиля другого пользователя
|
| 809 |
@app.route('/profile/<username>')
|
|
@@ -815,6 +820,10 @@ def user_profile(username):
|
|
| 815 |
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)
|
| 816 |
is_authenticated = 'username' in session
|
| 817 |
current_user = session.get('username', None)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 818 |
|
| 819 |
html = '''
|
| 820 |
<!DOCTYPE html>
|
|
@@ -865,6 +874,7 @@ def user_profile(username):
|
|
| 865 |
''' + NAV_HTML + '''
|
| 866 |
<div class="container">
|
| 867 |
<h1>Профиль: {{ username }}</h1>
|
|
|
|
| 868 |
<h2>Публикации</h2>
|
| 869 |
<div class="post-grid">
|
| 870 |
{% if user_posts %}
|
|
@@ -969,7 +979,7 @@ def user_profile(username):
|
|
| 969 |
</body>
|
| 970 |
</html>
|
| 971 |
'''
|
| 972 |
-
return render_template_string(html, username=username, user_posts=user_posts, repo_id=REPO_ID, is_authenticated=is_authenticated, current_user=current_user)
|
| 973 |
|
| 974 |
# Страница загрузки контента
|
| 975 |
@app.route('/upload', methods=['GET', 'POST'])
|
|
@@ -1035,7 +1045,7 @@ def upload():
|
|
| 1035 |
username = session.get('username', None)
|
| 1036 |
html = '''
|
| 1037 |
<!DOCTYPE html>
|
| 1038 |
-
<html lang="
|
| 1039 |
<head>
|
| 1040 |
<meta charset="UTF-8">
|
| 1041 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
@@ -1124,25 +1134,25 @@ def upload():
|
|
| 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 |
data = load_data()
|
| 1131 |
-
|
| 1132 |
is_authenticated = 'username' in session
|
| 1133 |
username = session.get('username', None)
|
| 1134 |
|
| 1135 |
search_query = request.form.get('search', '').strip().lower() if request.method == 'POST' and 'search' in request.form else request.args.get('search', '').strip().lower()
|
| 1136 |
|
| 1137 |
if search_query:
|
| 1138 |
-
|
| 1139 |
|
| 1140 |
if request.method == 'POST' and 'delete' in request.form:
|
| 1141 |
post_id = request.form.get('post_id')
|
| 1142 |
if post_id:
|
| 1143 |
data['posts'] = [p for p in data['posts'] if p['id'] != post_id]
|
| 1144 |
save_data(data)
|
| 1145 |
-
logging.info(f"Удален
|
| 1146 |
return redirect(url_for('admin_panel'))
|
| 1147 |
|
| 1148 |
html = '''
|
|
@@ -1154,7 +1164,7 @@ def admin_panel():
|
|
| 1154 |
<title>Админ-панель</title>
|
| 1155 |
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
| 1156 |
<style>
|
| 1157 |
-
body { font-family: 'Poppins',
|
| 1158 |
.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; }
|
| 1159 |
.sidebar-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
|
| 1160 |
.nav-brand { font-size: 1.5em; font-weight: 600; color: #3b82f6; }
|
|
@@ -1165,10 +1175,10 @@ def admin_panel():
|
|
| 1165 |
.logout-btn:hover { background: rgba(239, 68, 68, 0.3); color: #dc2626; }
|
| 1166 |
.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); }
|
| 1167 |
.container { max-width: 1200px; margin: 20px auto 20px 270px; padding: 20px; transition: margin-left 0.3s ease; }
|
| 1168 |
-
.
|
| 1169 |
-
.
|
| 1170 |
-
.
|
| 1171 |
-
.
|
| 1172 |
.delete-btn { padding: 10px; background: #ef4444; color: white; border: none; border-radius: 8px; cursor: pointer; width: 100%; transition: background 0.3s ease; }
|
| 1173 |
.delete-btn:hover { background: #dc2626; }
|
| 1174 |
.search-container { margin-bottom: 20px; }
|
|
@@ -1183,9 +1193,9 @@ def admin_panel():
|
|
| 1183 |
.sidebar.active { transform: translateX(0); }
|
| 1184 |
.menu-btn { display: block; }
|
| 1185 |
.container { margin: 60px 10px 20px 10px; max-width: calc(100% - 20px); }
|
| 1186 |
-
.
|
| 1187 |
-
.
|
| 1188 |
-
.
|
| 1189 |
.delete-btn, .search-input { padding: 10px; font-size: 14px; }
|
| 1190 |
.search-btn { padding: 10px 15px; font-size: 14px; }
|
| 1191 |
h1 { font-size: 1.5em; }
|
|
@@ -1196,34 +1206,38 @@ def admin_panel():
|
|
| 1196 |
<button class="menu-btn" onclick="toggleSidebar()">☰</button>
|
| 1197 |
''' + NAV_HTML + '''
|
| 1198 |
<div class="container">
|
| 1199 |
-
<h1>Админ-панель: Все
|
| 1200 |
<div class="search-container">
|
| 1201 |
<form method="POST">
|
| 1202 |
<input type="text" name="search" class="search-input" placeholder="Поиск по названию или описанию" value="{{ search_query }}">
|
| 1203 |
<button type="submit" class="search-btn">Искать</button>
|
| 1204 |
</form>
|
| 1205 |
</div>
|
| 1206 |
-
<div class="
|
| 1207 |
-
{% if
|
| 1208 |
-
{% for
|
| 1209 |
-
<div class="
|
| 1210 |
-
<a href="{{ url_for('post_page', post_id=
|
| 1211 |
-
|
| 1212 |
-
<
|
| 1213 |
-
|
| 1214 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1215 |
</a>
|
| 1216 |
-
<p>{{
|
| 1217 |
-
<p>Загрузил: <a href="{{ url_for('user_profile', username=
|
| 1218 |
-
<p class="stats">Просмотров: {{
|
| 1219 |
<form method="POST">
|
| 1220 |
-
<input type="hidden" name="post_id" value="{{
|
| 1221 |
<button type="submit" name="delete" class="delete-btn">Удалить</button>
|
| 1222 |
</form>
|
| 1223 |
</div>
|
| 1224 |
{% endfor %}
|
| 1225 |
{% else %}
|
| 1226 |
-
<p>
|
| 1227 |
{% endif %}
|
| 1228 |
</div>
|
| 1229 |
</div>
|
|
@@ -1233,22 +1247,24 @@ def admin_panel():
|
|
| 1233 |
}
|
| 1234 |
|
| 1235 |
document.addEventListener('DOMContentLoaded', function() {
|
| 1236 |
-
const videos = document.querySelectorAll('.
|
| 1237 |
videos.forEach(video => {
|
| 1238 |
-
video.
|
| 1239 |
-
|
| 1240 |
-
|
| 1241 |
-
|
| 1242 |
-
|
|
|
|
|
|
|
| 1243 |
});
|
| 1244 |
});
|
| 1245 |
</script>
|
| 1246 |
</body>
|
| 1247 |
</html>
|
| 1248 |
'''
|
| 1249 |
-
return render_template_string(html,
|
| 1250 |
|
| 1251 |
if __name__ == '__main__':
|
| 1252 |
backup_thread = threading.Thread(target=periodic_backup, daemon=True)
|
| 1253 |
backup_thread.start()
|
| 1254 |
-
app.run(debug=True, host='0.0.0.0', port=7860)
|
|
|
|
| 636 |
username = session['username']
|
| 637 |
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)
|
| 638 |
is_authenticated = 'username' in session
|
| 639 |
+
|
| 640 |
+
# Подсчет общего количества просмотров и лайков
|
| 641 |
+
total_views = sum(post.get('views', 0) for post in user_posts)
|
| 642 |
+
total_likes = sum(len(post.get('likes', [])) for post in user_posts)
|
| 643 |
|
| 644 |
if request.method == 'POST':
|
| 645 |
post_id = request.form.get('post_id')
|
|
|
|
| 698 |
''' + NAV_HTML + '''
|
| 699 |
<div class="container">
|
| 700 |
<h1>Профиль: {{ username }}</h1>
|
| 701 |
+
<p>Всего просмотров: {{ total_views }} | Всего лайков: {{ total_likes }}</p>
|
| 702 |
<a href="{{ url_for('upload') }}" class="btn">Добавить публикацию</a>
|
| 703 |
<h2>Ваши публикации</h2>
|
| 704 |
<div class="post-grid">
|
|
|
|
| 808 |
</body>
|
| 809 |
</html>
|
| 810 |
'''
|
| 811 |
+
return render_template_string(html, username=username, user_posts=user_posts, total_views=total_views, total_likes=total_likes, repo_id=REPO_ID, is_authenticated=is_authenticated)
|
| 812 |
|
| 813 |
# Страница профиля другого пользователя
|
| 814 |
@app.route('/profile/<username>')
|
|
|
|
| 820 |
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)
|
| 821 |
is_authenticated = 'username' in session
|
| 822 |
current_user = session.get('username', None)
|
| 823 |
+
|
| 824 |
+
# Подсчет общего количества просмотров и лайков
|
| 825 |
+
total_views = sum(post.get('views', 0) for post in user_posts)
|
| 826 |
+
total_likes = sum(len(post.get('likes', [])) for post in user_posts)
|
| 827 |
|
| 828 |
html = '''
|
| 829 |
<!DOCTYPE html>
|
|
|
|
| 874 |
''' + NAV_HTML + '''
|
| 875 |
<div class="container">
|
| 876 |
<h1>Профиль: {{ username }}</h1>
|
| 877 |
+
<p>Всего просмотров: {{ total_views }} | Всего лайков: {{ total_likes }}</p>
|
| 878 |
<h2>Публикации</h2>
|
| 879 |
<div class="post-grid">
|
| 880 |
{% if user_posts %}
|
|
|
|
| 979 |
</body>
|
| 980 |
</html>
|
| 981 |
'''
|
| 982 |
+
return render_template_string(html, username=username, user_posts=user_posts, total_views=total_views, total_likes=total_likes, repo_id=REPO_ID, is_authenticated=is_authenticated, current_user=current_user)
|
| 983 |
|
| 984 |
# Страница загрузки контента
|
| 985 |
@app.route('/upload', methods=['GET', 'POST'])
|
|
|
|
| 1045 |
username = session.get('username', None)
|
| 1046 |
html = '''
|
| 1047 |
<!DOCTYPE html>
|
| 1048 |
+
<html lang="ру">
|
| 1049 |
<head>
|
| 1050 |
<meta charset="UTF-8">
|
| 1051 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
| 1134 |
'''
|
| 1135 |
return render_template_string(html, username=username, is_authenticated=is_authenticated)
|
| 1136 |
|
| 1137 |
+
# Админ-панель (все посты: видео и фото)
|
| 1138 |
@app.route('/admhosto', methods=['GET', 'POST'])
|
| 1139 |
def admin_panel():
|
| 1140 |
data = load_data()
|
| 1141 |
+
posts = sorted(data.get('posts', [] ), key=lambda x: datetime.strptime(x['upload_date'], '%Y-%m-%d %H:%M:%S'), reverse=True)
|
| 1142 |
is_authenticated = 'username' in session
|
| 1143 |
username = session.get('username', None)
|
| 1144 |
|
| 1145 |
search_query = request.form.get('search', '').strip().lower() if request.method == 'POST' and 'search' in request.form else request.args.get('search', '').strip().lower()
|
| 1146 |
|
| 1147 |
if search_query:
|
| 1148 |
+
posts = [post for post in posts if search_query in post['title'].lower() or search_query in post['description'].lower()]
|
| 1149 |
|
| 1150 |
if request.method == 'POST' and 'delete' in request.form:
|
| 1151 |
post_id = request.form.get('post_id')
|
| 1152 |
if post_id:
|
| 1153 |
data['posts'] = [p for p in data['posts'] if p['id'] != post_id]
|
| 1154 |
save_data(data)
|
| 1155 |
+
logging.info(f"Удален пост с ID {post_id} через админ-панель")
|
| 1156 |
return redirect(url_for('admin_panel'))
|
| 1157 |
|
| 1158 |
html = '''
|
|
|
|
| 1164 |
<title>Админ-панель</title>
|
| 1165 |
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
|
| 1166 |
<style>
|
| 1167 |
+
body { font-family: 'Poppins', sans-serif; background: linear-gradient(135deg, #e0e7ff, #f3f4f6); margin: 0; padding: 0; min-height: 100vh; }
|
| 1168 |
.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; }
|
| 1169 |
.sidebar-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
|
| 1170 |
.nav-brand { font-size: 1.5em; font-weight: 600; color: #3b82f6; }
|
|
|
|
| 1175 |
.logout-btn:hover { background: rgba(239, 68, 68, 0.3); color: #dc2626; }
|
| 1176 |
.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); }
|
| 1177 |
.container { max-width: 1200px; margin: 20px auto 20px 270px; padding: 20px; transition: margin-left 0.3s ease; }
|
| 1178 |
+
.post-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; }
|
| 1179 |
+
.post-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; }
|
| 1180 |
+
.post-item:hover { transform: scale(1.02); }
|
| 1181 |
+
.post-preview { width: 100%; border-radius: 8px; height: 200px; object-fit: cover; }
|
| 1182 |
.delete-btn { padding: 10px; background: #ef4444; color: white; border: none; border-radius: 8px; cursor: pointer; width: 100%; transition: background 0.3s ease; }
|
| 1183 |
.delete-btn:hover { background: #dc2626; }
|
| 1184 |
.search-container { margin-bottom: 20px; }
|
|
|
|
| 1193 |
.sidebar.active { transform: translateX(0); }
|
| 1194 |
.menu-btn { display: block; }
|
| 1195 |
.container { margin: 60px 10px 20px 10px; max-width: calc(100% - 20px); }
|
| 1196 |
+
.post-grid { grid-template-columns: 1fr; gap: 15px; }
|
| 1197 |
+
.post-item { padding: 10px; }
|
| 1198 |
+
.post-preview { height: 150px; }
|
| 1199 |
.delete-btn, .search-input { padding: 10px; font-size: 14px; }
|
| 1200 |
.search-btn { padding: 10px 15px; font-size: 14px; }
|
| 1201 |
h1 { font-size: 1.5em; }
|
|
|
|
| 1206 |
<button class="menu-btn" onclick="toggleSidebar()">☰</button>
|
| 1207 |
''' + NAV_HTML + '''
|
| 1208 |
<div class="container">
|
| 1209 |
+
<h1>Админ-панель: Все посты</h1>
|
| 1210 |
<div class="search-container">
|
| 1211 |
<form method="POST">
|
| 1212 |
<input type="text" name="search" class="search-input" placeholder="Поиск по названию или описанию" value="{{ search_query }}">
|
| 1213 |
<button type="submit" class="search-btn">Искать</button>
|
| 1214 |
</form>
|
| 1215 |
</div>
|
| 1216 |
+
<div class="post-grid">
|
| 1217 |
+
{% if posts %}
|
| 1218 |
+
{% for post in posts %}
|
| 1219 |
+
<div class="post-item">
|
| 1220 |
+
<a href="{{ url_for('post_page', post_id=post['id']) }}">
|
| 1221 |
+
{% if post['type'] == 'video' %}
|
| 1222 |
+
<video class="post-preview" preload="metadata" muted>
|
| 1223 |
+
<source src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ post['type'] }}s/{{ post['filename'] }}" type="video/mp4">
|
| 1224 |
+
</video>
|
| 1225 |
+
{% else %}
|
| 1226 |
+
<img class="post-preview" src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/{{ post['type'] }}s/{{ post['filename'] }}" alt="{{ post['title'] }}">
|
| 1227 |
+
{% endif %}
|
| 1228 |
+
<h2>{{ post['title'] }}</h2>
|
| 1229 |
</a>
|
| 1230 |
+
<p>{{ post['description'] }}</p>
|
| 1231 |
+
<p>Загрузил: <a href="{{ url_for('user_profile', username=post['uploader']) }}" class="username-link">{{ post['uploader'] }}</a> | {{ post['upload_date'] }}</p>
|
| 1232 |
+
<p class="stats">Просмотров: {{ post['views'] }} | Лайков: {{ post['likes']|length }}</p>
|
| 1233 |
<form method="POST">
|
| 1234 |
+
<input type="hidden" name="post_id" value="{{ post['id'] }}">
|
| 1235 |
<button type="submit" name="delete" class="delete-btn">Удалить</button>
|
| 1236 |
</form>
|
| 1237 |
</div>
|
| 1238 |
{% endfor %}
|
| 1239 |
{% else %}
|
| 1240 |
+
<p>Посты не найдены.</p>
|
| 1241 |
{% endif %}
|
| 1242 |
</div>
|
| 1243 |
</div>
|
|
|
|
| 1247 |
}
|
| 1248 |
|
| 1249 |
document.addEventListener('DOMContentLoaded', function() {
|
| 1250 |
+
const videos = document.querySelectorAll('.post-preview');
|
| 1251 |
videos.forEach(video => {
|
| 1252 |
+
if (video.tagName === 'VIDEO') {
|
| 1253 |
+
video.addEventListener('loadedmetadata', function() {
|
| 1254 |
+
const duration = video.duration;
|
| 1255 |
+
const randomTime = Math.random() * duration;
|
| 1256 |
+
video.currentTime = randomTime;
|
| 1257 |
+
});
|
| 1258 |
+
}
|
| 1259 |
});
|
| 1260 |
});
|
| 1261 |
</script>
|
| 1262 |
</body>
|
| 1263 |
</html>
|
| 1264 |
'''
|
| 1265 |
+
return render_template_string(html, posts=posts, is_authenticated=is_authenticated, username=username, repo_id=REPO_ID, search_query=search_query)
|
| 1266 |
|
| 1267 |
if __name__ == '__main__':
|
| 1268 |
backup_thread = threading.Thread(target=periodic_backup, daemon=True)
|
| 1269 |
backup_thread.start()
|
| 1270 |
+
app.run(debug=True, host='0.0.0.0', port=7860)
|