Загрузить файлы в «/»

This commit is contained in:
shaman_lesnoy 2025-03-18 01:14:50 +03:00
parent 30059aaa52
commit d80dbd5d01

700
main.py
View file

@ -1,409 +1,291 @@
import os import os
import time import time
import asyncio import asyncio
import aiosqlite import aiosqlite
from quart import Quart, render_template, request, send_from_directory, Response from quart import Quart, render_template, request, send_from_directory, Response
from datetime import datetime from datetime import datetime
from babel.dates import format_datetime from babel.dates import format_datetime
<<<<<<< HEAD
from urllib.parse import quote
=======
>>>>>>> 9cc0929a09596abeb93ee7f2711527fb5da25f7a app = Quart(__name__, template_folder='frontend', static_folder='frontend')
DB_PATH = 'maps.db'
DATA = "/data"
app = Quart(__name__, template_folder='frontend', static_folder='frontend')
GAME_MODES = {
DB_PATH = 'maps.db' "Classic": "Классический",
DATA = "/data" "Deathmatch": "Бой насмерть",
"Demolition": "Уничтожение объекта",
GAME_MODES = { "Armsrace": "Гонка вооружений",
"Classic": "Классический", "Custom": "Пользовательский",
"Deathmatch": "Бой насмерть", "Training": "Обучение",
"Demolition": "Уничтожение объекта", "Co-op Strike": "Совместный налёт",
"Armsrace": "Гонка вооружений", "Wingman": "Напарники",
"Custom": "Пользовательский", "Flying Scoutsman": "Перелётные снайперы"
"Training": "Обучение", }
"Co-op Strike": "Совместный налёт",
"Wingman": "Напарники", last_download_times = {}
"Flying Scoutsman": "Перелётные снайперы" DOWNLOAD_COOLDOWN = 10
}
async def get_maps(page=1, per_page=30):
last_download_times = {} async with aiosqlite.connect(DB_PATH) as conn:
DOWNLOAD_COOLDOWN = 10 cursor = await conn.cursor()
async def get_maps(page=1, per_page=30): offset = (page - 1) * per_page
print(f"Запрос карт на странице {page}, с {per_page} картами на странице.")
async with aiosqlite.connect(DB_PATH) as conn: await cursor.execute('''
cursor = await conn.cursor() SELECT FilePath, Title, COALESCE(Stars, 0) as Stars, Description
FROM maps
offset = (page - 1) * per_page ORDER BY DateTime DESC
LIMIT ? OFFSET ?
await cursor.execute(''' ''', (per_page, offset))
SELECT FilePath, Title, COALESCE(Stars, 0) as Stars, Description
FROM maps maps = await cursor.fetchall()
ORDER BY DateTime DESC
LIMIT ? OFFSET ? return maps
''', (per_page, offset))
def get_image_path(filepath):
maps = await cursor.fetchall() image_path = os.path.join(DATA, filepath)
if not os.path.exists(image_path):
print(f"Получено {len(maps)} карт на странице {page}.") return "/images/image.jpg"
return maps return f"/images/{filepath.split('/')[0]}/{filepath.split('/')[1]}/{filepath.split('/')[1]}.jpg"
def get_image_path(filepath): def get_star_image(stars):
<<<<<<< HEAD if stars is None or stars == 0:
print(f"Получение пути изображения для файла: {filepath}") return "/stars/0-star.png"
image_path = os.path.join(DATA, filepath) return f"/stars/{stars}-star.png"
if not os.path.exists(image_path):
print("Изображение не найдено, возвращаем дефолтное.") @app.route('/images/<path:filename>')
======= async def serve_image(filename):
image_path = os.path.join(DATA, filepath) image_path = os.path.join(DATA, filename)
if not os.path.exists(image_path): if os.path.exists(image_path):
>>>>>>> 9cc0929a09596abeb93ee7f2711527fb5da25f7a return await send_from_directory(DATA, filename)
return "/images/image.jpg" else:
return f"/images/{filepath.split('/')[0]}/{filepath.split('/')[1]}/{filepath.split('/')[1]}.jpg" default_image_path = os.path.join(DATA, 'image.jpg')
if os.path.exists(default_image_path):
def get_star_image(stars): return await send_from_directory(DATA, 'image.jpg')
print(f"Получение изображения для {stars} звезд.") return "Default image not found", 404
if stars is None or stars == 0:
return "/stars/0-star.png" @app.route('/stars/<filename>')
return f"/stars/{stars}-star.png" async def serve_star_image(filename):
stars = os.path.join(DATA, 'stars')
@app.route('/images/<path:filename>') star_path = os.path.join(stars, filename)
async def serve_image(filename): if os.path.exists(star_path):
<<<<<<< HEAD return await send_from_directory(stars, filename)
print(f"Запрос изображения с именем: {filename}") else:
image_path = os.path.join(DATA, filename) return "Star image not found", 404
if os.path.exists(image_path):
print(f"Изображение {filename} найдено и отправляется.") @app.route('/download_bsp')
======= async def download_bsp():
image_path = os.path.join(DATA, filename) user_ip = request.remote_addr
if os.path.exists(image_path):
>>>>>>> 9cc0929a09596abeb93ee7f2711527fb5da25f7a current_time = time.time()
return await send_from_directory(DATA, filename) last_time = last_download_times.get(user_ip, 0)
else:
default_image_path = os.path.join(DATA, 'image.jpg') if current_time - last_time < DOWNLOAD_COOLDOWN:
if os.path.exists(default_image_path): wait_time = DOWNLOAD_COOLDOWN - (current_time - last_time)
<<<<<<< HEAD return f"Please wait {int(wait_time)} seconds before downloading again.", 429
print("Изображение не найдено, отправляем дефолтное.")
return await send_from_directory(DATA, 'image.jpg') last_download_times[user_ip] = current_time
print("Не найдено ни одного изображения.")
======= image_path = request.args.get('image_path')
return await send_from_directory(DATA, 'image.jpg') if not image_path:
>>>>>>> 9cc0929a09596abeb93ee7f2711527fb5da25f7a return "No image path provided", 400
return "Default image not found", 404
image_folder = os.path.dirname(image_path.replace("/images/", ""))
@app.route('/stars/<filename>') bsp_filename = None
async def serve_star_image(filename):
<<<<<<< HEAD for file in os.listdir(os.path.join(DATA, image_folder)):
print(f"Запрос изображения звезды: {filename}") if file.endswith('.bsp'):
stars = os.path.join(DATA, 'stars') bsp_filename = file
star_path = os.path.join(stars, filename) break
if os.path.exists(star_path):
print(f"Звезда {filename} найдена и отправляется.") if not bsp_filename:
======= return "No .bsp file found in the same directory", 404
stars = os.path.join(DATA, 'stars')
star_path = os.path.join(stars, filename) file_path = os.path.join(DATA, image_folder, bsp_filename)
if os.path.exists(star_path):
>>>>>>> 9cc0929a09596abeb93ee7f2711527fb5da25f7a SPEED_LIMIT = 40 * 1024 * 1024 // 8
return await send_from_directory(stars, filename)
else: async def file_stream():
print("Изображение звезды не найдено.") with open(file_path, 'rb') as f:
return "Star image not found", 404 while chunk := f.read(SPEED_LIMIT):
yield chunk
@app.route('/download_bsp') await asyncio.sleep(1)
async def download_bsp():
user_ip = request.remote_addr headers = {
print(f"Запрос на скачивание от IP: {user_ip}") "Content-Disposition": f"attachment; filename={bsp_filename}"
}
current_time = time.time() return Response(file_stream(), headers=headers, content_type='application/octet-stream')
last_time = last_download_times.get(user_ip, 0)
@app.route('/main')
if current_time - last_time < DOWNLOAD_COOLDOWN: async def main_page():
wait_time = DOWNLOAD_COOLDOWN - (current_time - last_time) image_url = request.args.get('image_url', 'default_image.jpg')
print(f"Пользователь должен подождать {int(wait_time)} секунд до следующего скачивания.") map_title = request.args.get('map_title', 'Default Map Title')
return f"Please wait {int(wait_time)} seconds before downloading again.", 429
async with aiosqlite.connect(DB_PATH) as conn:
last_download_times[user_ip] = current_time cursor = await conn.cursor()
print("Тайм-аут скачивания обновлен.") await cursor.execute('''
SELECT GameMode, Tags, FilePath, DateTime, YoutubeLink, Description
image_path = request.args.get('image_path') FROM maps
if not image_path: WHERE Title = ?
print("Не указан путь к изображению.") ''', (map_title,))
return "No image path provided", 400 row = await cursor.fetchone()
image_folder = os.path.dirname(image_path.replace("/images/", "")) game_mode = row[0] if row else None
bsp_filename = None tags = row[1] if row else None
file_path = row[2] if row else None
for file in os.listdir(os.path.join(DATA, image_folder)): date_time = row[3] if row else None
if file.endswith('.bsp'): youtube_link = row[4] if row else None
bsp_filename = file description = row[5] if row else "Нет описания"
break
if date_time:
if not bsp_filename: dt = datetime.fromisoformat(date_time)
print("Не найден .bsp файл в той же директории.") added_time = format_datetime(dt, format='d MMM yг., HH:mm', locale='ru_RU')
return "No .bsp file found in the same directory", 404 else:
added_time = 'Не найдено'
file_path = os.path.join(DATA, image_folder, bsp_filename)
<<<<<<< HEAD if game_mode:
print(f"Найден файл для скачивания: {file_path}") game_modes = game_mode.split(', ')
======= game_modes = [GAME_MODES.get(mode, mode) for mode in game_modes]
>>>>>>> 9cc0929a09596abeb93ee7f2711527fb5da25f7a game_mode = ', '.join(game_modes)
else:
SPEED_LIMIT = 60 * 1024 * 1024 // 8 game_mode = 'Не найден'
async def file_stream(): if tags:
print("Начало передачи файла по частям.") tags_list = tags.split(', ')
with open(file_path, 'rb') as f: tags_list = [GAME_MODES.get(tag, tag) for tag in tags_list]
while chunk := f.read(SPEED_LIMIT): tags = ', '.join(tags_list)
yield chunk else:
await asyncio.sleep(1) tags = 'Не найдено'
headers = { if file_path:
"Content-Disposition": f"attachment; filename={bsp_filename}" file_size = os.path.getsize(os.path.join(DATA, file_path))
} file_size_mb = file_size / (1024 * 1024)
return Response(file_stream(), headers=headers, content_type='application/octet-stream') file_size_display = f"{file_size_mb:.2f} MB"
else:
@app.route('/main') file_size_display = 'Не найден'
async def main_page():
print("Запрос страницы карты с параметрами:", request.args) return await render_template(
image_url = request.args.get('image_url', 'default_image.jpg') 'main.html',
map_title = request.args.get('map_title', 'Default Map Title') image_url=image_url,
map_title=map_title,
async with aiosqlite.connect(DB_PATH) as conn: game_mode=game_mode,
cursor = await conn.cursor() tags=tags,
await cursor.execute(''' file_size=file_size_display,
SELECT GameMode, Tags, FilePath, DateTime, YoutubeLink, Description added_time=added_time,
FROM maps youtube_link=youtube_link,
WHERE Title = ? description=description
''', (map_title,)) )
row = await cursor.fetchone()
game_mode = row[0] if row else None @app.route('/')
tags = row[1] if row else None async def index():
file_path = row[2] if row else None page = int(request.args.get('page', 1))
date_time = row[3] if row else None selected_game_modes = request.args.getlist('game_modes')
youtube_link = row[4] if row else None start_date = request.args.get('start_date')
description = row[5] if row else "Нет описания" end_date = request.args.get('end_date')
search_title = request.args.get('search_title')
if date_time: selected_stars = request.args.get('stars')
dt = datetime.fromisoformat(date_time)
added_time = format_datetime(dt, format='d MMM yг., HH:mm', locale='ru_RU') maps_data = await get_maps_filtered(page, selected_game_modes, start_date, end_date, search_title, selected_stars)
else:
added_time = 'Не найдено' async with aiosqlite.connect(DB_PATH) as conn:
cursor = await conn.cursor()
if game_mode:
game_modes = game_mode.split(', ') query = 'SELECT COUNT(*) FROM maps WHERE 1=1'
game_modes = [GAME_MODES.get(mode, mode) for mode in game_modes] params = []
game_mode = ', '.join(game_modes)
else: if selected_game_modes:
game_mode = 'Не найден' placeholders = ', '.join('?' for _ in selected_game_modes)
query += f' AND GameMode IN ({placeholders})'
if tags: params.extend(selected_game_modes)
tags_list = tags.split(', ')
tags_list = [GAME_MODES.get(tag, tag) for tag in tags_list] if start_date:
tags = ', '.join(tags_list) query += ' AND DateTime >= ?'
else: params.append(start_date)
tags = 'Не найдено' if end_date:
query += ' AND DateTime <= ?'
if file_path: params.append(end_date)
file_size = os.path.getsize(os.path.join(DATA, file_path))
file_size_mb = file_size / (1024 * 1024) if search_title:
file_size_display = f"{file_size_mb:.2f} MB" query += ' AND Title LIKE ?'
else: params.append(f'%{search_title}%')
file_size_display = 'Не найден'
if selected_stars:
print(f"Отправка страницы карты {map_title} с данными:") query += ' AND Stars = ?'
print(f"Игра: {game_mode}, Теги: {tags}, Размер файла: {file_size_display}, Время добавления: {added_time}") params.append(selected_stars)
return await render_template( await cursor.execute(query, params)
'main.html', total_maps = await cursor.fetchone()
image_url=image_url,
map_title=map_title, total_maps = total_maps[0]
game_mode=game_mode, per_page = 30
tags=tags, total_pages = (total_maps + per_page - 1) // per_page
file_size=file_size_display,
added_time=added_time, filters = '&'.join(
youtube_link=youtube_link, [f'game_modes={mode}' for mode in selected_game_modes] +
description=description ([f'start_date={start_date}'] if start_date else []) +
) ([f'end_date={end_date}'] if end_date else []) +
([f'search_title={search_title}'] if search_title else []) +
([f'stars={selected_stars}'] if selected_stars else [])
@app.route('/') )
async def index():
print("Запрос главной страницы с параметрами фильтрации:", request.args) return await render_template(
page = int(request.args.get('page', 1)) 'workshop.html',
selected_game_modes = request.args.getlist('game_modes') maps_data=maps_data,
start_date = request.args.get('start_date') page=page,
end_date = request.args.get('end_date') total_pages=total_pages,
search_title = request.args.get('search_title') get_image_path=get_image_path,
selected_stars = request.args.get('stars') get_star_image=get_star_image,
selected_game_modes=selected_game_modes,
maps_data = await get_maps_filtered(page, selected_game_modes, start_date, end_date, search_title, selected_stars) filters=filters,
selected_stars=selected_stars
async with aiosqlite.connect(DB_PATH) as conn: )
cursor = await conn.cursor()
async def get_maps_filtered(page=1, selected_game_modes=None, start_date=None, end_date=None, search_title=None, selected_stars=None):
query = 'SELECT COUNT(*) FROM maps WHERE 1=1' async with aiosqlite.connect(DB_PATH) as conn:
params = [] cursor = await conn.cursor()
if selected_game_modes: offset = (page - 1) * 30
placeholders = ', '.join('?' for _ in selected_game_modes) query = '''
query += f' AND GameMode IN ({placeholders})' SELECT FilePath, Title, COALESCE(Stars, 0) as Stars, Description
params.extend(selected_game_modes) FROM maps
WHERE 1=1
if start_date: '''
query += ' AND DateTime >= ?' params = []
params.append(start_date)
if end_date: if selected_stars:
query += ' AND DateTime <= ?' query += ' AND Stars = ?'
params.append(end_date) params.append(selected_stars)
if search_title: if search_title:
query += ' AND Title LIKE ?' query += ' AND Title LIKE ?'
params.append(f'%{search_title}%') params.append(f'%{search_title}%')
if selected_stars: if selected_game_modes:
query += ' AND Stars = ?' query += ' AND GameMode IN ({})'.format(','.join('?' for _ in selected_game_modes))
params.append(selected_stars) params.extend(selected_game_modes)
await cursor.execute(query, params) if start_date:
total_maps = await cursor.fetchone() query += ' AND DateTime >= ?'
params.append(start_date)
total_maps = total_maps[0] if end_date:
per_page = 30 query += ' AND DateTime <= ?'
total_pages = (total_maps + per_page - 1) // per_page params.append(end_date)
filters = '&'.join( query += ' ORDER BY DateTime DESC LIMIT ? OFFSET ?'
[f'game_modes={mode}' for mode in selected_game_modes] + params.extend([30, offset])
([f'start_date={start_date}'] if start_date else []) +
([f'end_date={end_date}'] if end_date else []) + await cursor.execute(query, params)
([f'search_title={search_title}'] if search_title else []) + maps = await cursor.fetchall()
([f'stars={selected_stars}'] if selected_stars else [])
) return maps
print(f"Общее количество карт: {total_maps}, Страниц: {total_pages}") if __name__ == '__main__':
return await render_template( import hypercorn.asyncio
'workshop.html', from hypercorn.config import Config
maps_data=maps_data, config = Config()
page=page, config.bind = ["0.0.0.0:5000"]
total_pages=total_pages, hypercorn.asyncio.run(app, config)
get_image_path=get_image_path,
get_star_image=get_star_image,
selected_game_modes=selected_game_modes,
filters=filters,
selected_stars=selected_stars
)
async def get_maps_filtered(page=1, selected_game_modes=None, start_date=None, end_date=None, search_title=None, selected_stars=None):
print(f"Фильтрация карт с параметрами: {locals()}")
async with aiosqlite.connect(DB_PATH) as conn:
cursor = await conn.cursor()
offset = (page - 1) * 30
query = '''
SELECT FilePath, Title, COALESCE(Stars, 0) as Stars, Description
FROM maps
WHERE 1=1
'''
params = []
if selected_stars:
query += ' AND Stars = ?'
params.append(selected_stars)
if search_title:
query += ' AND Title LIKE ?'
params.append(f'%{search_title}%')
if selected_game_modes:
query += ' AND GameMode IN ({})'.format(','.join('?' for _ in selected_game_modes))
params.extend(selected_game_modes)
if start_date:
query += ' AND DateTime >= ?'
params.append(start_date)
if end_date:
query += ' AND DateTime <= ?'
params.append(end_date)
query += ' ORDER BY DateTime DESC LIMIT ? OFFSET ?'
params.extend([30, offset])
await cursor.execute(query, params)
maps = await cursor.fetchall()
print(f"Найдено {len(maps)} карт после фильтрации.")
return maps
@app.route('/favicon.ico')
async def favicon():
return await send_from_directory(os.getcwd(), 'favicon.ico')
@app.route('/sitemap.xml')
async def sitemap():
domain = "https://csgoworkshop.ru"
async with aiosqlite.connect(DB_PATH) as conn:
cursor = await conn.cursor()
# Получаем список всех карт
await cursor.execute('SELECT Title FROM maps')
maps = await cursor.fetchall()
# Создаем базовые URL для sitemap
urls = [
f"{domain}/",
f"{domain}/main",
]
# Добавляем URL-адреса для всех карт
for map_title, in maps:
encoded_title = quote(map_title)
urls.append(f"{domain}/main?map_title={encoded_title}")
# Генерируем XML
sitemap_xml = '<?xml version="1.0" encoding="UTF-8"?>\n'
sitemap_xml += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n'
for url in urls:
sitemap_xml += " <url>\n"
sitemap_xml += f" <loc>{url}</loc>\n"
sitemap_xml += f" <lastmod>{datetime.utcnow().strftime('%Y-%m-%d')}</lastmod>\n"
sitemap_xml += " <changefreq>weekly</changefreq>\n"
sitemap_xml += " <priority>0.8</priority>\n"
sitemap_xml += " </url>\n"
sitemap_xml += '</urlset>'
return Response(sitemap_xml, content_type='application/xml')
@app.route('/robots.txt')
async def robots_txt():
content = """
User-agent: *
Disallow:
"""
return Response(content, content_type='text/plain')
if __name__ == '__main__':
<<<<<<< HEAD
print("Запуск приложения...")
import hypercorn.asyncio
from hypercorn.config import Config
config = Config()
config.bind = ["0.0.0.0:5000"]
hypercorn.asyncio.run(app, config)
=======
import hypercorn.asyncio
from hypercorn.config import Config
config = Config()
config.bind = ["0.0.0.0:5000"]
hypercorn.asyncio.run(app, config)
>>>>>>> 9cc0929a09596abeb93ee7f2711527fb5da25f7a