привет . есть веб приложение телеграм. и при входе через телеграм не сохраняется мои данные в сессии. нужно отправить видео по ссылке. но у меня не отправляется говорит что не авторизован и тп


# main.py

import os
import logging
from contextlib import asynccontextmanager
# main.py

import os
import hashlib
import hmac
from urllib.parse import parse_qsl, unquote_plus
from fastapi import FastAPI, Request, Depends, HTTPException, Form, status
from fastapi.responses import RedirectResponse, HTMLResponse
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
from sqlalchemy import create_engine, func, Column, Integer, String, ForeignKey, Text
from sqlalchemy.orm import sessionmaker, Session, relationship
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import declarative_base
from starlette.middleware.sessions import SessionMiddleware
from starlette.middleware import Middleware
from starlette.responses import Response
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import secrets
import logging
from fastapi import UploadFile, File
from telegram.ext import (
    ApplicationBuilder,
    CommandHandler,
    ContextTypes,
)
import telegram
from dotenv import load_dotenv
from models import Base, TelegramUser, UserPoints, Video, Series, Season, Episode


from models import Base, TelegramUser, UserPoints, Video, Series, Season, Episode, CinematicUniverse


from telegram.ext import (
    ApplicationBuilder,
    CommandHandler,
    ContextTypes,
)
import telegram

from dotenv import load_dotenv

from fastapi.security import HTTPBasic, HTTPBasicCredentials
import secrets

# Загрузка переменных окружения
load_dotenv("makefile.env")
TOKEN = os.getenv('TELEGRAM_BOT_TOKEN')
if not TOKEN:
    raise ValueError("Токен бота не задан. Проверьте переменные окружения.")

SECRET_KEY = os.urandom(24).hex()

# Настройка логирования
logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO,
)
logger = logging.getLogger(__name__)

# Настройка базы данных
DATABASE_URL = "sqlite:///./database.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)

# Создание таблиц в базе данных
Base.metadata.create_all(bind=engine)

# Зависимость для получения сессии базы данных
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

# Настройка шаблонов Jinja2
templates = Jinja2Templates(directory="templates")

# Настройка Telegram-бота
bot_app = ApplicationBuilder().token(TOKEN).build()

# Определение обработчиков бота
# main.py (проверьте функцию start)

async def start(update: telegram.Update, context: ContextTypes.DEFAULT_TYPE):
    try:
        telegram_id = update.effective_user.id
        db = SessionLocal()

        user = db.query(TelegramUser).filter_by(telegram_id=telegram_id).first()

        if not user:
            user = TelegramUser(telegram_id=telegram_id)
            db.add(user)
            db.commit()

            user_points = UserPoints(telegram_user_id=user.id, points=100)
            db.add(user_points)
            db.commit()

        db.close()

        web_app_url = 'https://5ca8-85-117-101-195.ngrok-free.app/webapp'  # Замените на ваш публичный URL
        admin_url = 'https://5ca8-85-117-101-195.ngrok-free.app/admin'    # Замените на ваш публичный URL

        keyboard = [
            [
                telegram.InlineKeyboardButton(
                    text='Открыть веб-приложение',
                    web_app=telegram.WebAppInfo(url=web_app_url),
                )
            ],
            [
                telegram.InlineKeyboardButton(
                    text='Открыть admin',
                    web_app=telegram.WebAppInfo(url=admin_url),
                )
            ]
        ]
        reply_markup = telegram.InlineKeyboardMarkup(keyboard)

        await update.message.reply_text(
            'Привет! Нажмите кнопку ниже, чтобы открыть веб-приложение.',
            reply_markup=reply_markup,
        )
    except Exception as e:
        logger.error(f"Ошибка в обработчике /start: {e}")
        await update.message.reply_text("Произошла ошибка при обработке вашей команды.")


bot_app.add_handler(CommandHandler('start', start))

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Инициализируем бота
    await bot_app.initialize()
    await bot_app.start()

    # Устанавливаем вебхук
    webhook_url = 'https://5ca8-85-117-101-195.ngrok-free.app/webhook'  # Замените на ваш публичный URL
    await bot_app.bot.set_webhook(url=webhook_url)

    yield

    # Останавливаем бота при завершении приложения
    await bot_app.stop()
    await bot_app.shutdown()

# Настройка аутентификации
security = HTTPBasic()

ADMIN_USERNAME = os.getenv("ADMIN_USERNAME")
ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD")

def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
    ADMIN_USERNAME = os.getenv("ADMIN_USERNAME", "admin")
    ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD", "password")
    correct_username = secrets.compare_digest(credentials.username, ADMIN_USERNAME)
    correct_password = secrets.compare_digest(credentials.password, ADMIN_PASSWORD)
    if not (correct_username and correct_password):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Неверные учетные данные",
            headers={"WWW-Authenticate": "Basic"},
        )
    return credentials.username

middleware = [
    Middleware(SessionMiddleware, secret_key=SECRET_KEY)
]

# Создание приложения FastAPI с использованием lifespan
app = FastAPI(lifespan=lifespan, middleware=middleware)

# Подключение статики
app.mount("/static", StaticFiles(directory="static"), name="static")

def verify_telegram_auth(init_data: str, bot_token: str) -> bool:
    try:
        parsed_data = dict(parse_qsl(init_data, strict_parsing=True))
        auth_hash = parsed_data.pop('hash', None)

        if not auth_hash:
            logger.warning('auth_hash not found in init_data')
            return False

        logger.info(f"Parsed data for hash calculation: {parsed_data}")

        data_check_arr = [f"{k}={v}" for k, v in sorted(parsed_data.items())]
        data_check_string = '\n'.join(data_check_arr)
        logger.info(f"Data check string: {data_check_string}")

        secret_key = hashlib.sha256(bot_token.encode('utf-8')).digest()
        calculated_hash = hmac.new(secret_key, data_check_string.encode('utf-8'), hashlib.sha256).hexdigest()

        logger.info(f"Calculated hash: {calculated_hash}")
        logger.info(f"Auth hash from init_data: {auth_hash}")

        if calculated_hash != auth_hash:
            logger.warning('Hashes do not match')
            return False

        logger.info('Verification successful')
        return True
    except Exception as e:
        logger.error(f"Exception during verification: {e}")
        return False

# main.py
@app.get('/', response_class=HTMLResponse)
async def home(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})
@app.get("/admin", response_class=HTMLResponse)
async def admin_home(request: Request, username: str = Depends(get_current_username)):
    return templates.TemplateResponse("admin/home.html", {"request": request})
@app.get('/series', response_class=HTMLResponse)
async def list_series_front(request: Request, db: Session = Depends(get_db)):
    try:
        series_list = db.query(Series).all()
        return templates.TemplateResponse("series_list_front.html", {"request": request, "series_list": series_list})
    except Exception as e:
        logger.error(f"Error fetching series for front-end: {e}")
        raise HTTPException(status_code=500, detail="Ошибка при загрузке сериалов.")

# Правильно: возвращает рендеринг шаблона с контекстом
@app.get('/movies', response_class=HTMLResponse)
async def list_movies_front(request: Request, db: Session = Depends(get_db)):
    try:
        movies_list = db.query(Video).all()
        return templates.TemplateResponse("admin/movies_list_front.html", {"request": request, "movies_list": movies_list})
    except Exception as e:
        logger.error(f"Error fetching movies for front-end: {e}")
        raise HTTPException(status_code=500, detail="Ошибка при загрузке списка фильмов.")


# Маршрут для отправки видео
@app.post('/send_video')
async def send_video_endpoint(request: Request, db: Session = Depends(get_db)):
    data = await request.json()
    video_url = data.get('video_url')
    telegram_id = data.get('telegram_id')
    init_data = data.get('initData')
    logger.info(f"Received init_data: {init_data}")

    if not video_url or not telegram_id or not init_data:
        return {'success': False, 'error': 'Недостаточно данных'}

    init_data = unquote_plus(init_data)
    logger.info(f"Decoded init_data: {init_data}")

    is_valid = verify_telegram_auth(init_data, TOKEN)
    if not is_valid:
        return {'success': False, 'error': 'Неверные данные авторизации'}

    try:
        video = db.query(Video).filter(Video.url == video_url).first()
        if not video:
            return {'success': False, 'error': 'Видео не найдено или недоступно'}

        user = db.query(TelegramUser).filter_by(telegram_id=telegram_id).first()
        if not user:
            return {'success': False, 'error': 'Пользователь не найден'}

        user_points = db.query(UserPoints).filter_by(telegram_user_id=user.id).first()
        if not user_points:
            return {'success': False, 'error': 'Информация о балансе пользователя не найдена'}

        if user_points.points < video.price_in_points:
            return {'success': False, 'error': 'У вас недостаточно очков для просмотра этого видео'}

        user_points.points -= video.price_in_points
        db.commit()

        await bot_app.bot.send_video(
            chat_id=telegram_id,
            video=video.url,
            caption=f'Вы запросили видео: "{video.title}".'
        )

        logger.info(f"Видео отправлено пользователю {telegram_id}. Баланс уменьшен на {video.price_in_points} очков.")
        return {'success': True}

    except Exception as e:
        logger.error(f"Ошибка при обработке запроса: {e}")
        return {'success': False, 'error': 'Ошибка при обработке запроса'}

# Маршрут главной страницы веб-приложения
@app.get('/webapp', response_class=HTMLResponse)
async def index(request: Request, db: Session = Depends(get_db)):
    videos = db.query(Video).all()
    return templates.TemplateResponse("index.html", {"request": request, "videos": videos, "active_page": "home"})

# Маршрут страницы видео
@app.get('/video/{video_id}', response_class=HTMLResponse)
async def show_video(request: Request, video_id: int, db: Session = Depends(get_db)):
    video = db.query(Video).filter(Video.id == video_id).first()
    if not video:
        raise HTTPException(status_code=404, detail="Видео не найдено")

    logger.info(f"Rendering video page for video_id={video_id}: {video}")

    return templates.TemplateResponse("video.html", {"request": request, "video": video, "active_page": "video"})

# Маршрут вебхука для Telegram бота
@app.post('/webhook')
async def webhook(request: Request):
    data = await request.json()
    logger.info(f"Received update: {data}")
    update = telegram.Update.de_json(data, bot_app.bot)
    await bot_app.process_update(update)
    return 'ok'


# Маршрут для отображения списка сериалов
@app.get("/admin/series", response_class=HTMLResponse)
async def list_series(request: Request, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    try:
        series_list = db.query(Series).all()
        cinematic_universes = db.query(CinematicUniverse).all()
        logger.info(f"Found {len(series_list)} series.")
        return templates.TemplateResponse("admin/series_list.html", {
            "request": request,
            "series_list": series_list,
            "cinematic_universes": cinematic_universes
        })
    except Exception as e:
        logger.error(f"Error in list_series: {e}")
        raise HTTPException(status_code=500, detail="Ошибка при загрузке списка сериалов.")



# Маршрут для отображения формы создания сериала
# app.py

@app.get("/admin/series/create", response_class=HTMLResponse)
async def create_series_form(request: Request, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    cinematic_universes = db.query(CinematicUniverse).all()
    return templates.TemplateResponse("admin/series_create.html", {"request": request, "cinematic_universes": cinematic_universes})


@app.post("/admin/series/create", response_class=RedirectResponse)
async def create_series_submit(request: Request, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    form = await request.form()
    name = form.get("name")
    poster_url = form.get("poster_url")
    genre = form.get("genre")
    release_year = form.get("release_year")
    description = form.get("description")
    price_in_points = form.get("price_in_points", 0)
    cinematic_universe_id = form.get("cinematic_universe_id")

    if not name:
        raise HTTPException(status_code=400, detail="Название сериала обязательно.")

    existing_series = db.query(Series).filter_by(name=name).first()
    if existing_series:
        raise HTTPException(status_code=400, detail="Сериал с таким названием уже существует.")

    try:
        release_year = int(release_year) if release_year else None
    except ValueError:
        raise HTTPException(status_code=400, detail="Год выпуска должен быть числом.")

    try:
        price_in_points = int(price_in_points) if price_in_points else 0
    except ValueError:
        raise HTTPException(status_code=400, detail="Цена в очках должна быть числом.")

    try:
        cinematic_universe_id = int(cinematic_universe_id) if cinematic_universe_id else None
    except ValueError:
        raise HTTPException(status_code=400, detail="Некорректный ID киновселенной.")

    new_series = Series(
        name=name,
        poster_url=poster_url,
        genre=genre,
        release_year=release_year,
        description=description,
        price_in_points=price_in_points,
        cinematic_universe_id=cinematic_universe_id
    )
    db.add(new_series)
    db.commit()
    db.refresh(new_series)

    return RedirectResponse(url="/admin/series", status_code=303)


# Маршрут для редактирования сериала
# main.py

@app.get("/admin/series/{series_id}/edit", response_class=HTMLResponse)
async def edit_series_form(request: Request, series_id: int, db: Session = Depends(get_db),
                           username: str = Depends(get_current_username)):
    series = db.query(Series).filter_by(id=series_id).first()
    if not series:
        raise HTTPException(status_code=404, detail="Сериал не найден.")
    cinematic_universes = db.query(CinematicUniverse).all()
    return templates.TemplateResponse("admin/series_edit.html", {"request": request, "series": series,
                                                                 "cinematic_universes": cinematic_universes})



@app.post("/admin/series/{series_id}/edit", response_class=RedirectResponse)
async def edit_series_submit(
    request: Request,
    series_id: int,
    db: Session = Depends(get_db),
    username: str = Depends(get_current_username),
    poster_file: UploadFile = File(None)
):
    form = await request.form()
    series = db.query(Series).filter_by(id=series_id).first()
    if not series:
        raise HTTPException(status_code=404, detail="Сериал не найден.")

    # Обработка основных полей (как выше)...

    # Обработка сезонов и серий
    # Предполагаем, что новые сезоны имеют имена вида 'season_name_new_<id>'
    new_season_prefix = 'season_name_new_'
    for key in form.keys():
        if key.startswith(new_season_prefix):
            new_season_id = key[len(new_season_prefix):]
            season_name = form.get(key)
            # Создайте новый сезон
            new_season = Season(
                number=len(series.seasons) + 1,  # Простой способ определения номера сезона
                name=season_name,
                series=series
            )
            db.add(new_season)
            db.commit()
            db.refresh(new_season)

            # Обработка серий для этого сезона
            episode_prefix = f'episode_name_new_{new_season_id}_'
            for ep_key in form.keys():
                if ep_key.startswith(episode_prefix):
                    episode_id = ep_key[len(episode_prefix):]
                    episode_name = form.get(ep_key)
                    episode_link = form.get(f'episode_link_new_{new_season_id}_{episode_id}')
                    # Создайте новую серию
                    new_episode = Episode(
                        number=len(new_season.episodes) + 1,
                        name=episode_name,
                        link=episode_link,
                        season=new_season
                    )
                    db.add(new_episode)
            db.commit()

    # Аналогично, вы можете обработать обновления существующих сезонов и серий

    db.commit()

    return RedirectResponse(url="/admin/series", status_code=303)

@app.get('/series/{series_id}', response_class=HTMLResponse)
async def series_detail(request: Request, series_id: int, db: Session = Depends(get_db)):
    series = db.query(Series).filter(Series.id == series_id).first()
    if not series:
        raise HTTPException(status_code=404, detail="Сериал не найден.")
    return templates.TemplateResponse("series_detail.html", {"request": request, "series": series})

# Если у вас есть отдельные страницы для фильмов, добавьте маршруты аналогичным образом
# Маршрут для удаления сериала
@app.get("/admin/series/{series_id}/delete", response_class=HTMLResponse)
async def delete_series(series_id: int, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    series = db.query(Series).filter(Series.id == series_id).first()
    if not series:
        raise HTTPException(status_code=404, detail="Сериал не найден.")
    try:
        db.delete(series)
        db.commit()
        logger.info(f"Deleted series: {series.name}")
    except SQLAlchemyError as e:
        logger.error(f"Error deleting series: {e}")
        raise HTTPException(status_code=500, detail="Ошибка при удалении сериала.")
    return RedirectResponse(url="/admin/series", status_code=303)

# Маршрут для удаления сезона
@app.get("/admin/series/{season_id}/delete_season", response_class=HTMLResponse)
async def delete_season(season_id: int, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    season = db.query(Season).filter(Season.id == season_id).first()
    if not season:
        raise HTTPException(status_code=404, detail="Сезон не найден.")
    try:
        db.delete(season)
        db.commit()
        logger.info(f"Deleted season: {season.name}")
    except SQLAlchemyError as e:
        logger.error(f"Error deleting season: {e}")
        raise HTTPException(status_code=500, detail="Ошибка при удалении сезона.")
    return RedirectResponse(url="/admin/series", status_code=303)

# Маршрут для удаления серии
@app.get("/admin/series/{episode_id}/delete_episode", response_class=HTMLResponse)
async def delete_episode(episode_id: int, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    episode = db.query(Episode).filter(Episode.id == episode_id).first()
    if not episode:
        raise HTTPException(status_code=404, detail="Серия не найдена.")
    try:
        db.delete(episode)
        db.commit()
        logger.info(f"Deleted episode: {episode.name}")
    except SQLAlchemyError as e:
        logger.error(f"Error deleting episode: {e}")
        raise HTTPException(status_code=500, detail="Ошибка при удалении серии.")
    return RedirectResponse(url="/admin/series", status_code=303)
@app.get("/admin/users", response_class=HTMLResponse)
async def list_users(request: Request, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    try:
        users = db.query(TelegramUser).all()
        return templates.TemplateResponse("admin/users_list.html", {"request": request, "users": users})
    except Exception as e:
        logger.error(f"Error fetching users: {e}")
        raise HTTPException(status_code=500, detail="Ошибка при загрузке списка пользователей.")

@app.get("/admin/users/create", response_class=HTMLResponse)
async def create_user_form(request: Request, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    return templates.TemplateResponse("admin/users_create.html", {"request": request})

@app.post("/admin/users/create", response_class=RedirectResponse)
async def create_user_submit(request: Request, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    form = await request.form()
    telegram_id = form.get("telegram_id")
    username_field = form.get("username")
    points = form.get("points", 0)

    if not telegram_id:
        raise HTTPException(status_code=400, detail="Telegram ID обязателен.")

    existing_user = db.query(TelegramUser).filter_by(telegram_id=telegram_id).first()
    if existing_user:
        raise HTTPException(status_code=400, detail="Пользователь с таким Telegram ID уже существует.")

    new_user = TelegramUser(telegram_id=telegram_id, username=username_field)
    db.add(new_user)
    db.commit()
    db.refresh(new_user)

    user_points = UserPoints(telegram_user_id=new_user.id, points=int(points))
    db.add(user_points)
    db.commit()

    return RedirectResponse(url="/admin/users", status_code=303)

## Добавление маршрутов для управления видеофильмами

# app.py

# Просмотр списка фильмов
@app.get("/admin/videos", response_class=HTMLResponse)
async def list_videos(request: Request, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    try:
        videos = db.query(Video).all()
        return templates.TemplateResponse("admin/videos_list.html", {"request": request, "videos": videos})
    except Exception as e:
        logger.error(f"Error fetching videos: {e}")
        raise HTTPException(status_code=500, detail="Ошибка при загрузке списка видеофильмов.")

# Форма создания фильма
@app.get("/admin/videos/create", response_class=HTMLResponse)
async def create_video_form(request: Request, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    cinematic_universes = db.query(CinematicUniverse).all()
    return templates.TemplateResponse("admin/videos_create.html", {"request": request, "cinematic_universes": cinematic_universes})


# Обработка создания фильма
@app.post("/admin/videos/create", response_class=RedirectResponse)
async def create_video_submit(request: Request, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    form = await request.form()
    title = form.get("title")
    url = form.get("url")
    poster_url = form.get("poster_url")
    genre = form.get("genre")
    release_year = form.get("release_year")
    description = form.get("description")
    price_in_points = form.get("price_in_points", 0)
    cinematic_universe_id = form.get("cinematic_universe_id")

    if not title or not url:
        raise HTTPException(status_code=400, detail="Название и URL фильма обязательны.")

    existing_video = db.query(Video).filter_by(title=title).first()
    if existing_video:
        raise HTTPException(status_code=400, detail="Фильм с таким названием уже существует.")

    new_video = Video(
        title=title,
        url=url,
        poster_url=poster_url,
        genre=genre,
        release_year=release_year,
        description=description,
        price_in_points=int(price_in_points),
        cinematic_universe_id=cinematic_universe_id if cinematic_universe_id else None
    )
    db.add(new_video)
    db.commit()
    db.refresh(new_video)

    return RedirectResponse(url="/admin/videos", status_code=303)

# Основной запуск приложения
# Маршрут редактирования фильма (GET)
@app.get("/admin/videos/{video_id}/edit", response_class=HTMLResponse)
async def edit_video_form(request: Request, video_id: int, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    video = db.query(Video).filter_by(id=video_id).first()
    if not video:
        raise HTTPException(status_code=404, detail="Фильм не найден.")
    cinematic_universes = db.query(CinematicUniverse).all()
    return templates.TemplateResponse("admin/videos_edit.html", {"request": request, "video": video, "cinematic_universes": cinematic_universes})

# Маршруты редактирования фильма (POST)
@app.post("/admin/videos/{video_id}/edit", response_class=RedirectResponse)
async def edit_video_submit(request: Request, video_id: int, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    form = await request.form()
    video = db.query(Video).filter_by(id=video_id).first()
    if not video:
        raise HTTPException(status_code=404, detail="Фильм не найден.")

    video.title = form.get("title")
    video.url = form.get("url")
    video.poster_url = form.get("poster_url")
    video.genre = form.get("genre")
    release_year = form.get("release_year")
    video.release_year = int(release_year) if release_year else None
    video.description = form.get("description")
    price_in_points = form.get("price_in_points", 0)
    video.price_in_points = int(price_in_points) if price_in_points else 0
    cinematic_universe_id = form.get("cinematic_universe_id")
    video.cinematic_universe_id = int(cinematic_universe_id) if cinematic_universe_id else None

    db.commit()

    return RedirectResponse(url="/admin/videos", status_code=303)
    # Просмотр списка киновселенных
@app.get("/admin/cinematic_universes", response_class=HTMLResponse)
async def list_cinematic_universes(request: Request, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    try:
        universes = db.query(CinematicUniverse).all()
        return templates.TemplateResponse("admin/cinematic_universes_list.html", {"request": request, "universes": universes})
    except Exception as e:
        logger.error(f"Error fetching cinematic universes: {e}")
        raise HTTPException(status_code=500, detail="Ошибка при загрузке списка киновселенных.")

# Форма создания новой киновселенной
@app.get("/admin/cinematic_universes/create", response_class=HTMLResponse)
async def create_cinematic_universe_form(request: Request, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    return templates.TemplateResponse("admin/cinematic_universe_create.html", {"request": request})

# Обработка создания новой киновселенной
@app.post("/admin/cinematic_universes/create", response_class=RedirectResponse)
async def create_cinematic_universe_submit(request: Request, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    form = await request.form()
    name = form.get("name")
    description = form.get("description")
    logo_url = form.get("logo_url")

    if not name:
        raise HTTPException(status_code=400, detail="Название киновселенной обязательно.")

    existing_universe = db.query(CinematicUniverse).filter_by(name=name).first()
    if existing_universe:
        raise HTTPException(status_code=400, detail="Киновселенная с таким названием уже существует.")

    new_universe = CinematicUniverse(name=name, description=description, logo_url=logo_url)
    db.add(new_universe)
    db.commit()
    db.refresh(new_universe)

    return RedirectResponse(url="/admin/cinematic_universes", status_code=303)

# Форма редактирования киновселенной
@app.get("/admin/cinematic_universes/{universe_id}/edit", response_class=HTMLResponse)
async def edit_cinematic_universe_form(request: Request, universe_id: int, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    universe = db.query(CinematicUniverse).filter_by(id=universe_id).first()
    if not universe:
        raise HTTPException(status_code=404, detail="Киновселенная не найдена.")
    return templates.TemplateResponse("admin/cinematic_universe_edit.html", {"request": request, "universe": universe})

# Обработка редактирования киновселенной
@app.post("/admin/cinematic_universes/{universe_id}/edit", response_class=RedirectResponse)
async def edit_cinematic_universe_submit(request: Request, universe_id: int, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    form = await request.form()
    universe = db.query(CinematicUniverse).filter_by(id=universe_id).first()
    if not universe:
        raise HTTPException(status_code=404, detail="Киновселенная не найдена.")

    universe.name = form.get("name")
    universe.description = form.get("description")
    universe.logo_url = form.get("logo_url")
    db.commit()

    return RedirectResponse(url="/admin/cinematic_universes", status_code=303)

# Удаление киновселенной
@app.get("/admin/cinematic_universes/{universe_id}/delete", response_class=RedirectResponse)
async def delete_cinematic_universe(universe_id: int, db: Session = Depends(get_db), username: str = Depends(get_current_username)):
    universe = db.query(CinematicUniverse).filter_by(id=universe_id).first()
    if not universe:
        raise HTTPException(status_code=404, detail="Киновселенная не найдена.")
    try:
        db.delete(universe)
        db.commit()
        logger.info(f"Deleted cinematic universe: {universe.name}")
    except Exception as e:
        logger.error(f"Error deleting cinematic universe: {e}")
        raise HTTPException(status_code=500, detail="Ошибка при удалении киновселенной.")
    return RedirectResponse(url="/admin/cinematic_universes", status_code=303)



if __name__ == '__main__':
    import uvicorn
    uvicorn.run(app, host='0.0.0.0', port=8443)


<!-- templates/series_detail.html -->

{% extends "admin/base.html" %}

{% block title %}{{ series.name }}{% endblock %}

{% block content %}
<h1>{{ series.name }}</h1>

<h3>Сезоны</h3>
{% for season in series.seasons %}
    <div class="mb-4">
        <h4>Сезон {{ season.number }}: {{ season.name }}</h4>
        <ul>
            {% for episode in season.episodes %}
                <li>
                    Серия {{ episode.number }}: {{ episode.name }} -
                    <a href="{{ episode.link }}" target="_blank">Посмотреть</a>
                </li>
            {% endfor %}
        </ul>
    </div>
{% endfor %}
{% endblock %}



нужно чтобы когда я нажму на посмотреть он отправил видео из этой ссылки