diff --git "a/app.py" "b/app.py"
--- "a/app.py"
+++ "b/app.py"
@@ -10,32 +10,32 @@ from werkzeug.utils import secure_filename
import random
app = Flask(__name__)
-app.secret_key = 'supersecretkey' # Замените на безопасный ключ в продакшене
-DATA_FILE = 'data_adusis.json'
+app.secret_key = 'supersecretkey' # Replace with a secure key in production
+DATA_FILE = 'datatest.json'
REPO_ID = "Eluza133/w1f9"
-HF_TOKEN_WRITE = os.getenv("HF_TOKEN") # Токен с правами записи
+HF_TOKEN_WRITE = os.getenv("HF_TOKEN") # Write token
HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") or HF_TOKEN_WRITE
-# Настройка логирования
+# Logging setup
logging.basicConfig(level=logging.DEBUG)
-# Функции работы с базой данных
+# Database functions
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, инициализация пустой базы")
+ logging.warning("Data is not in dict format, initializing empty database")
return {'posts': [], 'users': {}}
if 'posts' not in data:
data['posts'] = []
if 'users' not in data:
data['users'] = {}
- logging.info("Данные успешно загружены")
+ logging.info("Data loaded successfully")
return data
except Exception as e:
- logging.error(f"Ошибка загрузки данных: {e}")
+ logging.error(f"Error loading data: {e}")
return {'posts': [], 'users': {}}
def save_data(data):
@@ -43,9 +43,9 @@ def save_data(data):
with open(DATA_FILE, 'w', encoding='utf-8') as file:
json.dump(data, file, ensure_ascii=False, indent=4)
upload_db_to_hf()
- logging.info("Данные сохранены и загружены на HF")
+ logging.info("Data saved and uploaded to HF")
except Exception as e:
- logging.error(f"Ошибка сохранения данных: {e}")
+ logging.error(f"Error saving data: {e}")
raise
def upload_db_to_hf():
@@ -59,9 +59,9 @@ def upload_db_to_hf():
token=HF_TOKEN_WRITE,
commit_message=f"Backup {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
)
- logging.info("База данных загружена на Hugging Face")
+ logging.info("Database uploaded to Hugging Face")
except Exception as e:
- logging.error(f"Ошибка загрузки базы: {e}")
+ logging.error(f"Error uploading database: {e}")
def download_db_from_hf():
try:
@@ -73,9 +73,9 @@ def download_db_from_hf():
local_dir=".",
local_dir_use_symlinks=False
)
- logging.info("База данных скачана с Hugging Face")
+ logging.info("Database downloaded from Hugging Face")
except Exception as e:
- logging.error(f"Ошибка скачивания базы: {e}")
+ logging.error(f"Error downloading database: {e}")
if not os.path.exists(DATA_FILE):
with open(DATA_FILE, 'w', encoding='utf-8') as f:
json.dump({'posts': [], 'users': {}}, f)
@@ -85,27 +85,234 @@ def periodic_backup():
upload_db_to_hf()
time.sleep(15)
-# Базовый шаблон боковой навигации
+# Updated base style with new color palette
+BASE_STYLE = '''
+ :root {
+ --primary: #ff6b6b; /* Bright coral */
+ --secondary: #4ecdc4; /* Turquoise */
+ --background-light: #fff5e6; /* Light peach */
+ --background-dark: #2d1b3d; /* Deep purple */
+ --card-bg-light: rgba(255, 245, 230, 0.95);
+ --card-bg-dark: rgba(60, 40, 80, 0.95);
+ --text-light: #2d1b3d;
+ --text-dark: #fff5e6;
+ --shadow: 0 12px 48px rgba(0, 0, 0, 0.2);
+ --glass-bg: rgba(255, 245, 230, 0.15);
+ --transition: all 0.4s ease;
+ }
+ * { 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;
+ 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;
+ height: 100%;
+ background: var(--glass-bg);
+ backdrop-filter: blur(20px);
+ padding: 30px;
+ box-shadow: var(--shadow);
+ z-index: 1000;
+ transition: var(--transition);
+ }
+ .sidebar-header {
+ margin-bottom: 40px;
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ }
+ .nav-brand {
+ font-size: 1.8em;
+ font-weight: 700;
+ background: linear-gradient(45deg, var(--primary), var(--secondary));
+ -webkit-background-clip: text;
+ color: transparent;
+ }
+ .logo {
+ width: 40px;
+ height: 40px;
+ border-radius: 50%;
+ }
+ .nav-links {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+ }
+ .nav-link {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ padding: 18px;
+ background: var(--card-bg-light);
+ color: var(--text-light);
+ text-decoration: none;
+ border-radius: 15px;
+ font-size: 1.1em;
+ transition: var(--transition);
+ }
+ body.dark .nav-link {
+ background: var(--card-bg-dark);
+ color: var(--text-dark);
+ }
+ .nav-link:hover {
+ transform: translateX(15px);
+ background: var(--primary);
+ color: white;
+ }
+ .logout-btn {
+ background: var(--secondary);
+ color: white;
+ }
+ .logout-btn:hover {
+ background %s: #45b7b0;
+ }
+ .menu-btn {
+ display: none;
+ font-size: 32px;
+ 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);
+ }
+ .container {
+ margin: 30px auto 30px 330px;
+ max-width: 1200px;
+ padding: 30px;
+ transition: var(--transition);
+ }
+ .btn {
+ padding: 14px 28px;
+ background: var(--primary);
+ color: white;
+ border: none;
+ border-radius: 15px;
+ cursor: pointer;
+ font-size: 1.1em;
+ transition: var(--transition);
+ display: inline-block;
+ margin: 10px 0;
+ }
+ .btn:hover {
+ transform: scale(1.08);
+ background: #e65b5b;
+ }
+ input, textarea {
+ width: 100%;
+ padding: 14px;
+ margin: 15px 0;
+ border: 2px solid rgba(0, 0, 0, 0.1);
+ border-radius: 15px;
+ background: var(--glass-bg);
+ color: var(--text-light);
+ font-size: 1.1em;
+ transition: var(--transition);
+ }
+ body.dark input, body.dark textarea {
+ border: 2px solid rgba(255, 255, 255, 0.1);
+ color: var(--text-dark);
+ }
+ input:focus, textarea:focus {
+ outline: none;
+ border-color: var(--primary);
+ background: rgba(255, 245, 230, 0.2);
+ }
+ .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;
+ }
+ .modal img {
+ max-width: 95%;
+ max-height: 95%;
+ object-fit: contain;
+ border-radius: 15px;
+ box-shadow: var(--shadow);
+ }
+ .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);
+ }
+ @media (max-width: 768px) {
+ .sidebar {
+ transform: translateX(-100%);
+ width: 280px;
+ }
+ .sidebar.active {
+ transform: translateX(0);
+ }
+ .menu-btn {
+ display: block;
+ }
+ .container {
+ margin: 80px 15px 30px 15px;
+ max-width: calc(100% - 30px);
+ }
+ .theme-toggle {
+ top: 80px;
+ right: 15px;
+ }
+ .btn, input, textarea {
+ font-size: 1em;
+ padding: 12px;
+ }
+ }
+'''
+
NAV_HTML = '''
'''
-# Регистрация пользователя
+# Registration
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
@@ -114,57 +321,72 @@ def register():
data = load_data()
if username in data['users']:
- flash('Пользователь уже существует!')
+ flash('User already exists!')
return redirect(url_for('register'))
data['users'][username] = {'password': password, 'bio': '', 'link': '', 'avatar': None}
save_data(data)
- logging.info(f"Пользователь {username} зарегистрирован")
- flash('Регистрация успешна! Войдите в систему.')
+ flash('Registration successful! Please log in.')
return redirect(url_for('login'))
is_authenticated = 'username' in session
username = session.get('username', None)
html = '''
-
+
- Регистрация
-
+
+ Register - Content Hub
+
''' + NAV_HTML + '''
+ 🌙
-
Регистрация
+
Register
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
@@ -173,23 +395,30 @@ def register():
{% endif %}
{% endwith %}
-
Уже есть аккаунт? Войти
+
Already have an account? Login
'''
return render_template_string(html, is_authenticated=is_authenticated, username=username)
-# Авторизация пользователя
+# Login
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
@@ -200,53 +429,68 @@ def login():
if username in data['users'] and data['users'][username]['password'] == password:
session['username'] = username
session.permanent = True
- logging.info(f"Успешный вход: {username}")
return redirect(url_for('feed'))
- flash('Неверный логин или пароль!')
+ flash('Invalid username or password!')
return redirect(url_for('login'))
is_authenticated = 'username' in session
username = session.get('username', None)
html = '''
-
+
- Вход
-
+
+ Login - Content Hub
+
''' + NAV_HTML + '''
+ 🌙
'''
return render_template_string(html, is_authenticated=is_authenticated, username=username)
-# Выход из системы
+# Logout
@app.route('/logout')
def logout():
session.pop('username', None)
- logging.info("Пользователь вышел из системы")
return redirect(url_for('feed'))
-# Главная страница - лента публикаций
+# Feed
@app.route('/', methods=['GET', 'POST'])
def feed():
data = load_data()
@@ -287,66 +537,120 @@ def feed():
username = session.get('username', None)
search_query = request.form.get('search', '').strip().lower() if request.method == 'POST' 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()]
html = '''
-
+
- Лента
-
+
+ Feed - Content Hub
+
''' + NAV_HTML + '''
+ 🌙
-
Лента публикаций
+
Content Feed
@@ -361,8 +665,8 @@ def feed():
{% endif %}
{{ post['title'] }}
{{ post['description'] }}
-
Загрузил: {{ post['uploader'] }} | {{ post['upload_date'] }}
-
Просмотров: {{ post['views'] }} | Лайков: {{ post['likes']|length }}
+
Uploaded by: {{ post['uploader'] }} | {{ post['upload_date'] }}
+
Views: {{ post['views'] }} | Likes: {{ post['likes']|length }}
{% endfor %}
@@ -374,92 +678,49 @@ def feed():
function toggleSidebar() {
document.getElementById('sidebar').classList.toggle('active');
}
-
+ function toggleTheme() {
+ document.body.classList.toggle('dark');
+ localStorage.setItem('theme', document.body.classList.contains('dark') ? 'dark' : 'light');
+ }
function openModal(src, event) {
event.preventDefault();
const modal = document.getElementById('imageModal');
const modalImg = document.getElementById('modalImage');
modal.style.display = 'flex';
modalImg.src = src;
- modalImg.style.transform = 'scale(1)';
}
-
function closeModal(event) {
if (event.target.tagName !== 'IMG') {
- const modal = document.getElementById('imageModal');
- modal.style.display = 'none';
+ document.getElementById('imageModal').style.display = 'none';
}
}
-
- document.addEventListener('DOMContentLoaded', function() {
+ window.onload = () => {
+ if (localStorage.getItem('theme') === 'dark') document.body.classList.add('dark');
const videos = document.querySelectorAll('.post-preview');
videos.forEach(video => {
if (video.tagName === 'VIDEO') {
- video.addEventListener('loadedmetadata', function() {
- const duration = video.duration;
- const randomTime = Math.random() * duration;
- video.currentTime = randomTime;
+ video.addEventListener('loadedmetadata', () => {
+ video.currentTime = Math.random() * video.duration;
});
}
});
-
- const modalImg = document.getElementById('modalImage');
- let scale = 1;
- let startDistance = 0;
- let lastTap = 0;
-
- modalImg.addEventListener('dblclick', function() {
- scale = scale === 1 ? 2 : 1;
- modalImg.style.transform = `scale(${scale})`;
- });
-
- modalImg.addEventListener('touchstart', function(e) {
- if (e.touches.length === 2) {
- startDistance = getDistance(e.touches[0], e.touches[1]);
- } else if (e.touches.length === 1) {
- const now = new Date().getTime();
- if (now - lastTap < 300) {
- scale = scale === 1 ? 2 : 1;
- modalImg.style.transform = `scale(${scale})`;
- }
- lastTap = now;
- }
- });
-
- modalImg.addEventListener('touchmove', function(e) {
- if (e.touches.length === 2) {
- e.preventDefault();
- const currentDistance = getDistance(e.touches[0], e.touches[1]);
- scale = Math.max(1, Math.min(3, scale * (currentDistance / startDistance)));
- modalImg.style.transform = `scale(${scale})`;
- startDistance = currentDistance;
- }
- });
-
- function getDistance(touch1, touch2) {
- const dx = touch1.pageX - touch2.pageX;
- const dy = touch1.pageY - touch2.pageY;
- return Math.sqrt(dx * dx + dy * dy);
- }
- });
+ };
'''
return render_template_string(html, posts=posts, is_authenticated=is_authenticated, username=username, repo_id=REPO_ID, search_query=search_query)
-# Страница отдельной публикации
+# Post page
@app.route('/post/
', methods=['GET', 'POST'])
def post_page(post_id):
data = load_data()
post = next((p for p in data['posts'] if p['id'] == post_id), None)
if not post:
- return "Публикация не найдена", 404
+ return "Post not found", 404
is_authenticated = 'username' in session
username = session.get('username', None)
-
- # Увеличение количества просмотров
post['views'] = post.get('views', 0) + 1
save_data(data)
@@ -478,50 +739,76 @@ def post_page(post_id):
html = '''
-
+
- {{ post['title'] }}
-
+
+ {{ post['title'] }} - Content Hub
+
''' + NAV_HTML + '''
+ 🌙
{{ post['title'] }}
{% if post['type'] == 'video' %}
@@ -532,28 +819,28 @@ def post_page(post_id):
{% endif %}
{{ post['description'] }}
-
Загрузил: {{ post['uploader'] }} | {{ post['upload_date'] }}
-
Просмотров: {{ post['views'] }} | Лайков: {{ post['likes']|length }}
+
Uploaded by: {{ post['uploader'] }} | {{ post['upload_date'] }}
+
Views: {{ post['views'] }} | Likes: {{ post['likes']|length }}
{% if is_authenticated %}
-
{% endif %}
-
Комментарии
+
Comments
{% for comment in post.get('comments', []) %}
{% endfor %}
-
Назад к ленте
+
Back to Feed
{% if not is_authenticated %}
-
Войдите , чтобы ставить лайки и комментировать.
+
Login to like and comment.
{% endif %}
@@ -563,73 +850,35 @@ def post_page(post_id):
function toggleSidebar() {
document.getElementById('sidebar').classList.toggle('active');
}
-
+ function toggleTheme() {
+ document.body.classList.toggle('dark');
+ localStorage.setItem('theme', document.body.classList.contains('dark') ? 'dark' : 'light');
+ }
function openModal(src) {
const modal = document.getElementById('imageModal');
const modalImg = document.getElementById('modalImage');
modal.style.display = 'flex';
modalImg.src = src;
- modalImg.style.transform = 'scale(1)';
}
-
function closeModal(event) {
if (event.target.tagName !== 'IMG') {
- const modal = document.getElementById('imageModal');
- modal.style.display = 'none';
+ document.getElementById('imageModal').style.display = 'none';
}
}
-
- document.addEventListener('DOMContentLoaded', function() {
- const modalImg = document.getElementById('modalImage');
- let scale = 1;
- let startDistance = 0;
- let lastTap = 0;
-
- modalImg.addEventListener('dblclick', function() {
- scale = scale === 1 ? 2 : 1;
- modalImg.style.transform = `scale(${scale})`;
- });
-
- modalImg.addEventListener('touchstart', function(e) {
- if (e.touches.length === 2) {
- startDistance = getDistance(e.touches[0], e.touches[1]);
- } else if (e.touches.length === 1) {
- const now = new Date().getTime();
- if (now - lastTap < 300) {
- scale = scale === 1 ? 2 : 1;
- modalImg.style.transform = `scale(${scale})`;
- }
- lastTap = now;
- }
- });
-
- modalImg.addEventListener('touchmove', function(e) {
- if (e.touches.length === 2) {
- e.preventDefault();
- const currentDistance = getDistance(e.touches[0], e.touches[1]);
- scale = Math.max(1, Math.min(3, scale * (currentDistance / startDistance)));
- modalImg.style.transform = `scale(${scale})`;
- startDistance = currentDistance;
- }
- });
-
- function getDistance(touch1, touch2) {
- const dx = touch1.pageX - touch2.pageX;
- const dy = touch1.pageY - touch2.pageY;
- return Math.sqrt(dx * dx + dy * dy);
- }
- });
+ window.onload = () => {
+ if (localStorage.getItem('theme') === 'dark') document.body.classList.add('dark');
+ };
'''
return render_template_string(html, post=post, repo_id=REPO_ID, is_authenticated=is_authenticated, username=username)
-# Страница профиля текущего пользователя
+# User profile
@app.route('/profile', methods=['GET', 'POST'])
def profile():
if 'username' not in session:
- flash('Войдите, чтобы просмотреть профиль!')
+ flash('Login to view your profile!')
return redirect(url_for('login'))
data = load_data()
@@ -637,11 +886,8 @@ def profile():
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
- # Подсчет общего количества просмотров и лайков
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', '')
@@ -653,7 +899,6 @@ def profile():
if post_id:
data['posts'] = [p for p in data['posts'] if p['id'] != post_id or p['uploader'] != username]
save_data(data)
- logging.info(f"Публикация {post_id} удалена пользователем {username}")
return redirect(url_for('profile'))
elif 'update_profile' in request.form:
bio = request.form.get('bio', '')
@@ -672,7 +917,7 @@ def profile():
repo_id=REPO_ID,
repo_type="dataset",
token=HF_TOKEN_WRITE,
- commit_message=f"Добавлен аватар для {username}"
+ commit_message=f"Uploaded avatar for {username}"
)
data['users'][username]['avatar'] = avatar_path
if os.path.exists(temp_path):
@@ -680,85 +925,134 @@ def profile():
data['users'][username]['bio'] = bio
data['users'][username]['link'] = link
save_data(data)
- logging.info(f"Профиль {username} обновлен")
return redirect(url_for('profile'))
html = '''
-
+
-
Профиль - {{ username }}
-
+
+
Profile - {{ username }} - Content Hub
+
''' + NAV_HTML + '''
+
🌙
-
Профиль: {{ username }}
-
+
-
Редактировать профиль
+
Edit Profile
- {{ bio }}
-
+ {{ bio }}
+
- Сохранить
+ Save
-
Добавить публикацию
-
Ваши публикации
+
Add Post
+
Your Posts
{% if user_posts %}
{% for post in user_posts %}
@@ -775,15 +1069,14 @@ def profile():
{{ post['description'] }}
{{ post['upload_date'] }}
-
Просмотров: {{ post['views'] }} | Лайков: {{ post['likes']|length }}
- Удалить
+ Delete
{% endfor %}
{% else %}
-
Вы пока не загрузили ни одной публикации.
+
You haven't uploaded any posts yet.
{% endif %}
@@ -794,103 +1087,55 @@ def profile():
function toggleSidebar() {
document.getElementById('sidebar').classList.toggle('active');
}
-
+ function toggleTheme() {
+ document.body.classList.toggle('dark');
+ localStorage.setItem('theme', document.body.classList.contains('dark') ? 'dark' : 'light');
+ }
function openModal(src, event) {
event.preventDefault();
const modal = document.getElementById('imageModal');
const modalImg = document.getElementById('modalImage');
modal.style.display = 'flex';
modalImg.src = src;
- modalImg.style.transform = 'scale(1)';
}
-
function closeModal(event) {
if (event.target.tagName !== 'IMG') {
- const modal = document.getElementById('imageModal');
- modal.style.display = 'none';
+ document.getElementById('imageModal').style.display = 'none';
}
}
-
function copyProfileLink() {
- const url = window.location.href;
- navigator.clipboard.writeText(url).then(() => {
- alert('Ссылка на профиль скопирована в буфер обмена!');
+ navigator.clipboard.writeText(window.location.href).then(() => {
+ alert('Link copied!');
});
}
-
- document.addEventListener('DOMContentLoaded', function() {
+ window.onload = () => {
+ if (localStorage.getItem('theme') === 'dark') document.body.classList.add('dark');
const videos = document.querySelectorAll('.post-preview');
videos.forEach(video => {
if (video.tagName === 'VIDEO') {
- video.addEventListener('loadedmetadata', function() {
- const duration = video.duration;
- const randomTime = Math.random() * duration;
- video.currentTime = randomTime;
+ video.addEventListener('loadedmetadata', () => {
+ video.currentTime = Math.random() * video.duration;
});
}
});
-
- const modalImg = document.getElementById('modalImage');
- let scale = 1;
- let startDistance = 0;
- let lastTap = 0;
-
- modalImg.addEventListener('dblclick', function() {
- scale = scale === 1 ? 2 : 1;
- modalImg.style.transform = `scale(${scale})`;
- });
-
- modalImg.addEventListener('touchstart', function(e) {
- if (e.touches.length === 2) {
- startDistance = getDistance(e.touches[0], e.touches[1]);
- } else if (e.touches.length === 1) {
- const now = new Date().getTime();
- if (now - lastTap < 300) {
- scale = scale === 1 ? 2 : 1;
- modalImg.style.transform = `scale(${scale})`;
- }
- lastTap = now;
- }
- });
-
- modalImg.addEventListener('touchmove', function(e) {
- if (e.touches.length === 2) {
- e.preventDefault();
- const currentDistance = getDistance(e.touches[0], e.touches[1]);
- scale = Math.max(1, Math.min(3, scale * (currentDistance / startDistance)));
- modalImg.style.transform = `scale(${scale})`;
- startDistance = currentDistance;
- }
- });
-
- function getDistance(touch1, touch2) {
- const dx = touch1.pageX - touch2.pageX;
- const dy = touch1.pageY - touch2.pageY;
- return Math.sqrt(dx * dx + dy * dy);
- }
- });
+ };
'''
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)
-# Страница профиля другого пользователя
+# Other user's profile
@app.route('/profile/
')
def user_profile(username):
data = load_data()
if username not in data['users']:
- return "Пользователь не найден", 404
+ return "User not found", 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
- current_user = session.get('username', None)
-
- # Подсчет общего количества просмотров и лайков
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', '')
@@ -898,70 +1143,116 @@ def user_profile(username):
html = '''
-
+
- Профиль - {{ username }}
-
+
+ Profile - {{ username }} - Content Hub
+
''' + NAV_HTML + '''
+ 🌙
-
Профиль: {{ username }}
-
+
-
Публикации
+
Posts
{% if user_posts %}
{% for post in user_posts %}
@@ -978,11 +1269,10 @@ def user_profile(username):
{{ post['description'] }}
{{ post['upload_date'] }}
-
Просмотров: {{ post['views'] }} | Лайков: {{ post['likes']|length }}
{% endfor %}
{% else %}
-
Этот пользователь пока не загрузил ни одной публикации.
+
This user hasn't uploaded any posts yet.
{% endif %}
@@ -993,92 +1283,49 @@ def user_profile(username):
function toggleSidebar() {
document.getElementById('sidebar').classList.toggle('active');
}
-
+ function toggleTheme() {
+ document.body.classList.toggle('dark');
+ localStorage.setItem('theme', document.body.classList.contains('dark') ? 'dark' : 'light');
+ }
function openModal(src, event) {
event.preventDefault();
const modal = document.getElementById('imageModal');
const modalImg = document.getElementById('modalImage');
modal.style.display = 'flex';
modalImg.src = src;
- modalImg.style.transform = 'scale(1)';
}
-
function closeModal(event) {
if (event.target.tagName !== 'IMG') {
- const modal = document.getElementById('imageModal');
- modal.style.display = 'none';
+ document.getElementById('imageModal').style.display = 'none';
}
}
-
function copyProfileLink() {
- const url = window.location.href;
- navigator.clipboard.writeText(url).then(() => {
- alert('Ссылка на профиль скопирована в буфер обмена!');
+ navigator.clipboard.writeText(window.location.href).then(() => {
+ alert('Link copied!');
});
}
-
- document.addEventListener('DOMContentLoaded', function() {
+ window.onload = () => {
+ if (localStorage.getItem('theme') === 'dark') document.body.classList.add('dark');
const videos = document.querySelectorAll('.post-preview');
videos.forEach(video => {
if (video.tagName === 'VIDEO') {
- video.addEventListener('loadedmetadata', function() {
- const duration = video.duration;
- const randomTime = Math.random() * duration;
- video.currentTime = randomTime;
+ video.addEventListener('loadedmetadata', () => {
+ video.currentTime = Math.random() * video.duration;
});
}
});
-
- const modalImg = document.getElementById('modalImage');
- let scale = 1;
- let startDistance = 0;
- let lastTap = 0;
-
- modalImg.addEventListener('dblclick', function() {
- scale = scale === 1 ? 2 : 1;
- modalImg.style.transform = `scale(${scale})`;
- });
-
- modalImg.addEventListener('touchstart', function(e) {
- if (e.touches.length === 2) {
- startDistance = getDistance(e.touches[0], e.touches[1]);
- } else if (e.touches.length === 1) {
- const now = new Date().getTime();
- if (now - lastTap < 300) {
- scale = scale === 1 ? 2 : 1;
- modalImg.style.transform = `scale(${scale})`;
- }
- lastTap = now;
- }
- });
-
- modalImg.addEventListener('touchmove', function(e) {
- if (e.touches.length === 2) {
- e.preventDefault();
- const currentDistance = getDistance(e.touches[0], e.touches[1]);
- scale = Math.max(1, Math.min(3, scale * (currentDistance / startDistance)));
- modalImg.style.transform = `scale(${scale})`;
- startDistance = currentDistance;
- }
- });
-
- function getDistance(touch1, touch2) {
- const dx = touch1.pageX - touch2.pageX;
- const dy = touch1.pageY - touch2.pageY;
- return Math.sqrt(dx * dx + dy * dy);
- }
- });
+ };
'''
- 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, current_user=current_user)
+ 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)
-# Страница загрузки контента
+# Upload content
@app.route('/upload', methods=['GET', 'POST'])
def upload():
if 'username' not in session:
- flash('Войдите, чтобы загрузить контент!')
+ flash('Login to upload content!')
return redirect(url_for('login'))
if request.method == 'POST':
@@ -1088,17 +1335,14 @@ def upload():
uploader = session['username']
if not title or not file:
- return "Укажите название и выберите файл", 400
+ return "Please provide a title and select a file", 400
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'
-
- # Загрузка на Hugging Face
api = HfApi()
api.upload_file(
path_or_fileobj=temp_path,
@@ -1106,10 +1350,9 @@ def upload():
repo_id=REPO_ID,
repo_type="dataset",
token=HF_TOKEN_WRITE,
- commit_message=f"Добавлена публикация: {title} пользователем {uploader}"
+ commit_message=f"Uploaded post: {title} by {uploader}"
)
- # Обновление базы данных
data = load_data()
post_id = f"post_{len(data['posts']) + 1}"
while any(p['id'] == post_id for p in data['posts']):
@@ -1127,107 +1370,117 @@ def upload():
'comments': []
})
save_data(data)
- logging.info(f"Публикация {title} ({file_type}) загружена пользователем {uploader} с ID {post_id}")
if os.path.exists(temp_path):
os.remove(temp_path)
-
return redirect(url_for('profile'))
is_authenticated = 'username' in session
username = session.get('username', None)
html = '''
-
+
- Загрузка контента
-
+
+ Upload Content - Content Hub
+
''' + NAV_HTML + '''
+ 🌙
'''
return render_template_string(html, username=username, is_authenticated=is_authenticated)
-# Админ-панель (все посты: видео и фото)
+# Admin panel
@app.route('/admhosto', methods=['GET', 'POST'])
def admin_panel():
data = load_data()
@@ -1236,7 +1489,6 @@ def admin_panel():
username = session.get('username', None)
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()]
@@ -1245,65 +1497,115 @@ def admin_panel():
if post_id:
data['posts'] = [p for p in data['posts'] if p['id'] != post_id]
save_data(data)
- logging.info(f"Удален пост с ID {post_id} через админ-панель")
return redirect(url_for('admin_panel'))
html = '''
-
+
- Админ-панель
-
+
+ Admin Panel - Content Hub
+
''' + NAV_HTML + '''
+ 🌙
-
Админ-панель: Все посты
+
Admin Panel: All Posts
-
- Искать
+
+ Search
@@ -1321,16 +1623,15 @@ def admin_panel():
{{ post['title'] }}
{{ post['description'] }}
-
Загрузил: {{ post['uploader'] }} | {{ post['upload_date'] }}
-
Просмотров: {{ post['views'] }} | Лайков: {{ post['likes']|length }}
+
Uploaded by: {{ post['uploader'] }} | {{ post['upload_date'] }}
- Удалить
+ Delete
{% endfor %}
{% else %}
-
Посты не найдены.
+
No posts found.
{% endif %}
@@ -1338,19 +1639,21 @@ def admin_panel():
function toggleSidebar() {
document.getElementById('sidebar').classList.toggle('active');
}
-
- document.addEventListener('DOMContentLoaded', function() {
+ function toggleTheme() {
+ document.body.classList.toggle('dark');
+ localStorage.setItem('theme', document.body.classList.contains('dark') ? 'dark' : 'light');
+ }
+ window.onload = () => {
+ if (localStorage.getItem('theme') === 'dark') document.body.classList.add('dark');
const videos = document.querySelectorAll('.post-preview');
videos.forEach(video => {
if (video.tagName === 'VIDEO') {
- video.addEventListener('loadedmetadata', function() {
- const duration = video.duration;
- const randomTime = Math.random() * duration;
- video.currentTime = randomTime;
+ video.addEventListener('loadedmetadata', () => {
+ video.currentTime = Math.random() * video.duration;
});
}
});
- });
+ };
{{ comment['user'] }} ({{ comment['date'] }}): {{ comment['text'] }}