first commit

This commit is contained in:
shaman_lesnoy 2024-12-29 01:39:52 +03:00
commit 9e4041cd0b
33 changed files with 1631162 additions and 0 deletions

285
main.py Normal file
View file

@ -0,0 +1,285 @@
import os
import time
import asyncio
import aiosqlite
from quart import Quart, render_template, request, send_from_directory, Response
from datetime import datetime
from babel.dates import format_datetime
import locale
locale.setlocale(locale.LC_TIME, 'ru_RU.UTF-8')
app = Quart(__name__, template_folder='frontend', static_folder='frontend')
DB_PATH = 'maps.db'
GAME_MODES = {
"Classic": "Классический",
"Deathmatch": "Бой насмерть",
"Demolition": "Уничтожение объекта",
"Armsrace": "Гонка вооружений",
"Custom": "Пользовательский",
"Training": "Обучение",
"Co-op Strike": "Совместный налёт",
"Wingman": "Напарники",
"Flying Scoutsman": "Перелётные снайперы"
}
last_download_times = {}
DOWNLOAD_COOLDOWN = 10
async def get_maps(page=1, per_page=30):
async with aiosqlite.connect(DB_PATH) as conn:
cursor = await conn.cursor()
offset = (page - 1) * per_page
await cursor.execute('''
SELECT FilePath, Title, COALESCE(Stars, 0) as Stars, Description
FROM maps
ORDER BY DateTime DESC
LIMIT ? OFFSET ?
''', (per_page, offset))
maps = await cursor.fetchall()
return maps
def get_image_path(filepath):
image_path = os.path.join('J:/public/complete/workshop', filepath)
if not os.path.exists(image_path):
return "http://127.0.0.1:5000/images/image.jpg"
return f"http://127.0.0.1:5000/images/{filepath.split('/')[0]}/{filepath.split('/')[1]}/{filepath.split('/')[1]}.jpg"
def get_star_image(stars):
if stars is None or stars == 0:
return "http://127.0.0.1:5000/stars/0-star.png"
return f"http://127.0.0.1:5000/stars/{stars}-star.png"
@app.route('/images/<path:filename>')
async def serve_image(filename):
image_path = os.path.join('J:/public/complete/workshop', filename)
if os.path.exists(image_path):
return await send_from_directory('J:/public/complete/workshop', filename)
else:
default_image_path = os.path.join('J:/public/complete/workshop', 'image.jpg')
if os.path.exists(default_image_path):
return await send_from_directory('J:/public/complete/workshop', 'image.jpg')
return "Default image not found", 404
@app.route('/stars/<filename>')
async def serve_star_image(filename):
star_path = os.path.join('J:/public/complete/workshop/stars', filename)
if os.path.exists(star_path):
return await send_from_directory('J:/public/complete/workshop/stars', filename)
else:
return "Star image not found", 404
@app.route('/download_bsp')
async def download_bsp():
user_ip = request.remote_addr
current_time = time.time()
last_time = last_download_times.get(user_ip, 0)
if current_time - last_time < DOWNLOAD_COOLDOWN:
wait_time = DOWNLOAD_COOLDOWN - (current_time - last_time)
return f"Please wait {int(wait_time)} seconds before downloading again.", 429
last_download_times[user_ip] = current_time
image_path = request.args.get('image_path')
if not image_path:
return "No image path provided", 400
image_folder = os.path.dirname(image_path.replace("http://127.0.0.1:5000/images/", ""))
bsp_filename = None
for file in os.listdir(os.path.join('J:/public/complete/workshop', image_folder)):
if file.endswith('.bsp'):
bsp_filename = file
break
if not bsp_filename:
return "No .bsp file found in the same directory", 404
file_path = os.path.join('J:/public/complete/workshop', image_folder, bsp_filename)
SPEED_LIMIT = 40 * 1024 * 1024 // 8
async def file_stream():
with open(file_path, 'rb') as f:
while chunk := f.read(SPEED_LIMIT):
yield chunk
await asyncio.sleep(1)
headers = {
"Content-Disposition": f"attachment; filename={bsp_filename}"
}
return Response(file_stream(), headers=headers, content_type='application/octet-stream')
@app.route('/main')
async def main_page():
image_url = request.args.get('image_url', 'default_image.jpg')
map_title = request.args.get('map_title', 'Default Map Title')
async with aiosqlite.connect(DB_PATH) as conn:
cursor = await conn.cursor()
await cursor.execute('''
SELECT GameMode, Tags, FilePath, DateTime, YoutubeLink, Description
FROM maps
WHERE Title = ?
''', (map_title,))
row = await cursor.fetchone()
game_mode = row[0] if row else None
tags = row[1] if row else None
file_path = row[2] if row else None
date_time = row[3] if row else None
youtube_link = row[4] if row else None
description = row[5] if row else "Нет описания"
if date_time:
dt = datetime.fromisoformat(date_time)
added_time = format_datetime(dt, format='d MMM yг., HH:mm', locale='ru_RU')
else:
added_time = 'Не найдено'
if game_mode:
game_modes = game_mode.split(', ')
game_modes = [GAME_MODES.get(mode, mode) for mode in game_modes]
game_mode = ', '.join(game_modes)
else:
game_mode = 'Не найден'
if tags:
tags_list = tags.split(', ')
tags_list = [GAME_MODES.get(tag, tag) for tag in tags_list]
tags = ', '.join(tags_list)
else:
tags = 'Не найдено'
if file_path:
file_size = os.path.getsize(os.path.join('J:/public/complete/workshop', file_path))
file_size_mb = file_size / (1024 * 1024)
file_size_display = f"{file_size_mb:.2f} MB"
else:
file_size_display = 'Не найден'
return await render_template(
'main.html',
image_url=image_url,
map_title=map_title,
game_mode=game_mode,
tags=tags,
file_size=file_size_display,
added_time=added_time,
youtube_link=youtube_link,
description=description
)
@app.route('/')
async def index():
page = int(request.args.get('page', 1))
selected_game_modes = request.args.getlist('game_modes')
start_date = request.args.get('start_date')
end_date = request.args.get('end_date')
search_title = request.args.get('search_title')
selected_stars = request.args.get('stars')
maps_data = await get_maps_filtered(page, selected_game_modes, start_date, end_date, search_title, selected_stars)
async with aiosqlite.connect(DB_PATH) as conn:
cursor = await conn.cursor()
query = 'SELECT COUNT(*) FROM maps WHERE 1=1'
params = []
if selected_game_modes:
placeholders = ', '.join('?' for _ in selected_game_modes)
query += f' AND GameMode IN ({placeholders})'
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)
if search_title:
query += ' AND Title LIKE ?'
params.append(f'%{search_title}%')
if selected_stars:
query += ' AND Stars = ?'
params.append(selected_stars)
await cursor.execute(query, params)
total_maps = await cursor.fetchone()
total_maps = total_maps[0]
per_page = 30
total_pages = (total_maps + per_page - 1) // per_page
filters = '&'.join(
[f'game_modes={mode}' for mode in selected_game_modes] +
([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 [])
)
return await render_template(
'workshop.html',
maps_data=maps_data,
page=page,
total_pages=total_pages,
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):
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()
return maps
if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000)