From e58e548f84495a06734c7697f28f096ba8489ca0 Mon Sep 17 00:00:00 2001 From: serov <1@dmserov.ru> Date: Sun, 23 Feb 2025 08:22:12 +0000 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=201.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1.py | 1273 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1273 insertions(+) create mode 100644 1.py diff --git a/1.py b/1.py new file mode 100644 index 0000000..97887c1 --- /dev/null +++ b/1.py @@ -0,0 +1,1273 @@ +import os +import subprocess +import sys +import json +import shutil +from pathlib import Path + +class ProjectSetup: + def __init__(self): + self.root_dir = Path.cwd() + self.backend_dir = self.root_dir / "backend" + self.frontend_dir = self.root_dir / "frontend" + self.venv_python = None + + def clean(self): + """Очистка старых файлов и директорий""" + print("Очистка проекта...") + if self.backend_dir.exists(): + shutil.rmtree(self.backend_dir) + if self.frontend_dir.exists(): + shutil.rmtree(self.frontend_dir) + + def create_backend(self): + """Настройка backend""" + print("\nНастройка backend...") + + # Создаем структуру директорий + app_dir = self.backend_dir / "app" + app_dir.mkdir(parents=True) + + # Путь к Python 3.11 + python_path = "python.exe" # Измените путь на ваш + + # Создаем виртуальное окружение с Python 3.11 + print("Создание виртуального окружения...") + subprocess.run([python_path, "-m", "venv", str(self.backend_dir / "venv")], check=True) + + # Определяем путь к Python в виртуальном окружении + if sys.platform == "win32": + self.venv_python = self.backend_dir / "venv" / "Scripts" / "python.exe" + else: + self.venv_python = self.backend_dir / "venv" / "bin" / "python" + + # Обновляем pip + subprocess.run([str(self.venv_python), "-m", "pip", "install", "--upgrade", "pip"], check=True) + + # Создаем файлы проекта + self.create_backend_files() + + # Устанавливаем зависимости + print("Установка зависимостей...") + requirements_txt = self.backend_dir / "requirements.txt" + subprocess.run([str(self.venv_python), "-m", "pip", "install", "-r", str(requirements_txt)], check=True) + + # Инициализируем базу данных + self.init_database() + + def create_frontend(self): + """Настройка frontend""" + print("\nНастройка frontend...") + self.frontend_dir.mkdir(parents=True) + os.chdir(self.frontend_dir) + + # Инициализируем package.json + package_json = { + "name": "smart-player-frontend", + "private": True, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@emotion/react": "^11.11.0", + "@emotion/styled": "^11.11.0", + "@mui/icons-material": "^5.11.16", + "@mui/material": "^5.13.0", + "axios": "^1.4.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.11.1", + "zustand": "^4.3.8" + }, + "devDependencies": { + "@types/react": "^18.2.6", + "@types/react-dom": "^18.2.4", + "@vitejs/plugin-react": "^4.0.0", + "typescript": "^5.0.4", + "vite": "^4.3.5" + } + } + + with open("package.json", "w") as f: + json.dump(package_json, f, indent=2) + + # Устанавливаем зависимости + print("Установка npm пакетов...") + subprocess.run("npm install", shell=True, check=True) + + # Создаем файлы frontend + self.create_frontend_files() + + def create_backend_files(self): + """Создание файлов backend""" + files = { + self.backend_dir / "app" / "__init__.py": "", + + self.backend_dir / "app" / "config.py": ''' +from pydantic_settings import BaseSettings + +class Settings(BaseSettings): + DATABASE_URL: str = "sqlite:///./smartplayer.db" + SECRET_KEY: str = "your-super-secret-key-keep-it-safe" + ACCESS_TOKEN_EXPIRE_MINUTES: int = 30 + ALGORITHM: str = "HS256" + + model_config = { + "env_file": ".env", + "env_file_encoding": "utf-8" + } + +settings = Settings() +''', + + self.backend_dir / "app" / "database.py": ''' +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from .config import settings + +engine = create_engine(settings.DATABASE_URL, connect_args={"check_same_thread": False}) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) +Base = declarative_base() + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() +''', + + self.backend_dir / "app" / "models.py": ''' +from sqlalchemy import Boolean, Column, Integer, String, DateTime, ForeignKey, Table, Enum +import enum +from sqlalchemy.orm import relationship +from datetime import datetime +from .database import Base + +class MediaType(str, enum.Enum): + AUDIO = "audio" + VIDEO = "video" + STREAM = "stream" + +# Таблица для связи many-to-many между плейлистами и медиа +playlist_media = Table('playlist_media', Base.metadata, + Column('playlist_id', Integer, ForeignKey('playlists.id')), + Column('media_id', Integer, ForeignKey('media.id')) +) + +class User(Base): + __tablename__ = "users" + + id = Column(Integer, primary_key=True, index=True) + email = Column(String, unique=True, index=True) + username = Column(String, unique=True, index=True) + hashed_password = Column(String) + is_active = Column(Boolean, default=True) + is_admin = Column(Boolean, default=False) + created_at = Column(DateTime, default=datetime.utcnow) + + # Связи + playlists = relationship("Playlist", back_populates="owner") + media_items = relationship("Media", back_populates="owner") + streams = relationship("Stream", back_populates="owner") + +class Media(Base): + __tablename__ = "media" + + id = Column(Integer, primary_key=True, index=True) + title = Column(String, index=True) + artist = Column(String, index=True) + duration = Column(Integer) # Длительность в секундах + file_path = Column(String) + media_type = Column(Enum(MediaType)) + thumbnail_path = Column(String, nullable=True) + created_at = Column(DateTime, default=datetime.utcnow) + owner_id = Column(Integer, ForeignKey("users.id")) + + owner = relationship("User", back_populates="media_items") + playlists = relationship("Playlist", secondary=playlist_media, back_populates="media") + +class Stream(Base): + __tablename__ = "streams" + + id = Column(Integer, primary_key=True, index=True) + title = Column(String, index=True) + description = Column(String, nullable=True) + stream_key = Column(String, unique=True) + is_live = Column(Boolean, default=False) + thumbnail_path = Column(String, nullable=True) + created_at = Column(DateTime, default=datetime.utcnow) + owner_id = Column(Integer, ForeignKey("users.id")) + + owner = relationship("User", back_populates="streams") + +class Playlist(Base): + __tablename__ = "playlists" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String, index=True) + description = Column(String) + is_public = Column(Boolean, default=False) + created_at = Column(DateTime, default=datetime.utcnow) + owner_id = Column(Integer, ForeignKey("users.id")) + + owner = relationship("User", back_populates="playlists") + media = relationship("Media", secondary=playlist_media, back_populates="playlists") +''', + + self.backend_dir / "app" / "schemas.py": ''' +from pydantic import BaseModel, EmailStr +from typing import Optional, List +from datetime import datetime +from .models import MediaType + +class UserBase(BaseModel): + email: EmailStr + username: str + +class User(BaseModel): + id: int + is_active: bool + is_admin: bool + created_at: datetime + + model_config = { + "from_attributes": True + } + +class Media(BaseModel): + id: int + file_path: str + created_at: datetime + owner_id: int + + model_config = { + "from_attributes": True + } + +class Stream(BaseModel): + id: int + stream_key: str + is_live: bool + created_at: datetime + owner_id: int + + model_config = { + "from_attributes": True + } + +class Playlist(BaseModel): + id: int + created_at: datetime + owner_id: int + media_items: List[Media] = [] + + model_config = { + "from_attributes": True + } + +class UserCreate(UserBase): + password: str + +class User(UserBase): + id: int + is_active: bool + is_admin: bool + created_at: datetime + + class Config: + orm_mode = True + +class Token(BaseModel): + access_token: str + token_type: str + user: User + +class MediaBase(BaseModel): + title: str + artist: str + duration: int + media_type: MediaType + thumbnail_path: Optional[str] = None + +class MediaCreate(MediaBase): + file_path: str + +class Media(MediaBase): + id: int + file_path: str + created_at: datetime + owner_id: int + + class Config: + orm_mode = True + +class StreamBase(BaseModel): + title: str + description: Optional[str] = None + thumbnail_path: Optional[str] = None + +class StreamCreate(StreamBase): + pass + +class Stream(StreamBase): + id: int + stream_key: str + is_live: bool + created_at: datetime + owner_id: int + + class Config: + orm_mode = True + +class PlaylistBase(BaseModel): + name: str + description: Optional[str] = None + is_public: bool = False + +class PlaylistCreate(PlaylistBase): + pass + +class Playlist(PlaylistBase): + id: int + created_at: datetime + owner_id: int + media_items: List[Media] = [] + + class Config: + orm_mode = True +''', + + self.backend_dir / "app" / "auth.py": ''' +from datetime import datetime, timedelta +from typing import Optional +from jose import JWTError, jwt +from passlib.context import CryptContext +from fastapi import Depends, HTTPException, status +from fastapi.security import OAuth2PasswordBearer +from sqlalchemy.orm import Session +from . import models, schemas +from .database import get_db +from .config import settings + +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + +# Определите oauth2_scheme +oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") # Добавьте эту строку + +def verify_password(plain_password, hashed_password): + return pwd_context.verify(plain_password, hashed_password) + +def get_password_hash(password): + return pwd_context.hash(password) + +def create_access_token(data: dict, expires_delta: Optional[timedelta] = None): + to_encode = data.copy() + if expires_delta: + expire = datetime.utcnow() + expires_delta + else: + expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) + to_encode.update({"exp": expire}) + encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM) + return encoded_jwt + +def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)): + credentials_exception = HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Could not validate credentials", + headers={"WWW-Authenticate": "Bearer"}, + ) + try: + payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]) + username: str = payload.get("sub") + if username is None: + raise credentials_exception + except JWTError: + raise credentials_exception + + user = db.query(models.User).filter(models.User.username == username).first() + if user is None: + raise credentials_exception + return user + +def get_current_active_user(current_user: models.User = Depends(get_current_user)): + if not current_user.is_active: + raise HTTPException(status_code=400, detail="Inactive user") + return current_user +''', + + self.backend_dir / "app" / "main.py": ''' +from fastapi import FastAPI, Depends, HTTPException, status, File, UploadFile +from fastapi.security import OAuth2PasswordRequestForm +from fastapi.middleware.cors import CORSMiddleware +from fastapi.staticfiles import StaticFiles +from sqlalchemy.orm import Session +from typing import List, Optional +import os +import secrets +from . import models, schemas, auth +from .database import engine, get_db +from .models import MediaType + +models.Base.metadata.create_all(bind=engine) + +app = FastAPI(title="Smart Player API") + +app.add_middleware( + CORSMiddleware, + allow_origins=["http://localhost:5173"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +@app.post("/token", response_model=schemas.Token) +async def login(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)): + user = db.query(models.User).filter(models.User.username == form_data.username).first() + if not user or not auth.verify_password(form_data.password, user.hashed_password): + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Incorrect username or password", + headers={"WWW-Authenticate": "Bearer"}, + ) + + access_token = auth.create_access_token(data={"sub": user.username}) + return { + "access_token": access_token, + "token_type": "bearer", + "user": user + } + +@app.get("/users/me", response_model=schemas.User) +async def read_users_me(current_user: models.User = Depends(auth.get_current_active_user)): + return current_user + +@app.post("/upload/", response_model=schemas.Media) +async def upload_media( + file: UploadFile = File(...), + title: str = Form(...), + artist: str = Form(...), + media_type: MediaType = Form(...), + db: Session = Depends(get_db), + current_user: models.User = Depends(auth.get_current_active_user) +): + # Сохраняем файл на диск + file_path = f"uploads/{file.filename}" + with open(file_path, "wb") as buffer: + buffer.write(await file.read()) + + # Создаем запись в базе данных + new_media = models.Media( + title=title, + artist=artist, + duration=0, # Можно рассчитать длительность позже + file_path=file_path, + media_type=media_type, + owner_id=current_user.id + ) + db.add(new_media) + db.commit() + db.refresh(new_media) + return new_media + +@app.post("/media/", response_model=schemas.Media) +async def create_media( + file: UploadFile = File(...), + title: str = Form(...), + artist: str = Form(...), + media_type: MediaType = Form(...), + db: Session = Depends(get_db), + current_user: models.User = Depends(auth.get_current_active_user) +): + # Логика обработки загрузки файла + pass + +@app.post("/streams/", response_model=schemas.Stream) +async def create_stream( + title: str = Form(...), + description: Optional[str] = Form(None), + db: Session = Depends(get_db), + current_user: models.User = Depends(auth.get_current_active_user) +): + # Логика создания потока + pass +''', + + self.backend_dir / "create_admin.py": ''' +import sys +from pathlib import Path +backend_dir = Path(__file__).resolve().parent +sys.path.insert(0, str(backend_dir)) + +from app.database import SessionLocal, engine, Base +from app.models import User +from app.auth import get_password_hash + +def create_admin(): + Base.metadata.create_all(bind=engine) + db = SessionLocal() + try: + admin = db.query(User).filter(User.username == "admin").first() + if not admin: + admin = User( + email="admin@example.com", + username="admin", + hashed_password=get_password_hash("admin"), + is_active=True, + is_admin=True + ) + db.add(admin) + db.commit() + print("Админ создан успешно!") + print("Логин: admin") + print("Пароль: admin") + else: + print("Админ уже существует") + finally: + db.close() + +if __name__ == "__main__": + create_admin() +''', + + self.backend_dir / "requirements.txt": ''' +fastapi==0.103.1 +uvicorn==0.23.2 +sqlalchemy==1.4.46 +pydantic==2.10.6 +pydantic-settings==2.8.0 +python-jose[cryptography]==3.4.0 +passlib==1.7.4 +bcrypt==4.0.1 +python-multipart==0.0.6 +python-dotenv==1.0.0 +email-validator==2.0.0.post1 +''', + } + + # Создаем все файлы + for file_path, content in files.items(): + file_path.parent.mkdir(parents=True, exist_ok=True) + with open(file_path, 'w', encoding='utf-8') as f: + f.write(content.strip()) + + def create_frontend_files(self): + """Создание файлов frontend""" + files = { + self.frontend_dir / "index.html": ''' + + +
+ + +