From d789a31642b0f7e93348dba8bbd480618f05451e Mon Sep 17 00:00:00 2001 From: Marek Date: Sat, 4 Apr 2026 23:04:10 +0200 Subject: [PATCH] update --- backend/database.py | 20 +++++ backend/main.py | 18 ++++ backend/models.py | 17 ++++ backend/requirements.txt | 3 + backend/routes/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 120 bytes .../routes/__pycache__/videos.cpython-312.pyc | Bin 0 -> 4869 bytes backend/routes/videos.py | 81 ++++++++++++++++++ backend/schemas.py | 35 ++++++++ backend/services/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 122 bytes .../download_service.cpython-312.pyc | Bin 0 -> 977 bytes .../__pycache__/video_service.cpython-312.pyc | Bin 0 -> 2284 bytes backend/services/download_service.py | 27 ++++++ backend/services/video_service.py | 31 +++++++ 15 files changed, 232 insertions(+) create mode 100644 backend/database.py create mode 100644 backend/models.py create mode 100644 backend/routes/__init__.py create mode 100644 backend/routes/__pycache__/__init__.cpython-312.pyc create mode 100644 backend/routes/__pycache__/videos.cpython-312.pyc create mode 100644 backend/routes/videos.py create mode 100644 backend/schemas.py create mode 100644 backend/services/__init__.py create mode 100644 backend/services/__pycache__/__init__.cpython-312.pyc create mode 100644 backend/services/__pycache__/download_service.cpython-312.pyc create mode 100644 backend/services/__pycache__/video_service.cpython-312.pyc create mode 100644 backend/services/download_service.py create mode 100644 backend/services/video_service.py diff --git a/backend/database.py b/backend/database.py new file mode 100644 index 0000000..fe753e8 --- /dev/null +++ b/backend/database.py @@ -0,0 +1,20 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker, declarative_base + +DATABASE_URL = "sqlite:///videos/youtubeapp.db" + +engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False}) +SessionLocal = sessionmaker(bind=engine) +Base = declarative_base() + + +def create_tables(): + Base.metadata.create_all(bind=engine) + + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() diff --git a/backend/main.py b/backend/main.py index c593ad8..9d1d085 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,7 +1,25 @@ from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware + +from database import create_tables +from routes.videos import router as videos_router app = FastAPI() +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_methods=["*"], + allow_headers=["*"], +) + +app.include_router(videos_router) + + +@app.on_event("startup") +def startup(): + create_tables() + @app.get("/") def root(): diff --git a/backend/models.py b/backend/models.py new file mode 100644 index 0000000..f647058 --- /dev/null +++ b/backend/models.py @@ -0,0 +1,17 @@ +from datetime import datetime + +from sqlalchemy import Column, DateTime, Integer, String + +from database import Base + + +class Video(Base): + __tablename__ = "videos" + + id = Column(Integer, primary_key=True, autoincrement=True) + title = Column(String, nullable=False) + youtuber = Column(String, nullable=False) + thumbnail_url = Column(String, nullable=False) + youtube_url = Column(String, nullable=False, unique=True) + file_path = Column(String, nullable=True) + created_at = Column(DateTime, default=datetime.utcnow) diff --git a/backend/requirements.txt b/backend/requirements.txt index 96172c4..feec010 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,3 +1,6 @@ fastapi uvicorn yt-dlp +sqlalchemy +aiosqlite +python-multipart diff --git a/backend/routes/__init__.py b/backend/routes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/routes/__pycache__/__init__.cpython-312.pyc b/backend/routes/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69808bfff03637b772378763588225c7b95905fc GIT binary patch literal 120 zcmX@j%ge<81ZFiCGePuY5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!64y^GD9|s;FD*$e t){l?R%*!l^kJl@xyv1RYo1apelWJGQ3RK4k#Kj=SM`lJw#v*1Q3jj|c7*qfN literal 0 HcmV?d00001 diff --git a/backend/routes/__pycache__/videos.cpython-312.pyc b/backend/routes/__pycache__/videos.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..80a52308af8d5995e6ca1e4262beed5804d94e7c GIT binary patch literal 4869 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 Video: + video = Video(**video_data.model_dump()) + db.add(video) + db.commit() + db.refresh(video) + return video + + +def get_all_videos(db: Session) -> list[Video]: + return db.query(Video).order_by(Video.created_at.desc()).all() + + +def get_downloaded_videos(db: Session) -> list[Video]: + return db.query(Video).filter(Video.file_path.isnot(None)).order_by(Video.created_at.desc()).all() + + +def get_video(db: Session, video_id: int) -> Video | None: + return db.query(Video).filter(Video.id == video_id).first() + + +def update_file_path(db: Session, video_id: int, path: str): + video = get_video(db, video_id) + if video: + video.file_path = path + db.commit()