mirror of
https://github.com/StepanovPlaton/torrent_backend.git
synced 2026-04-03 20:30:38 +04:00
Add file upload
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
content
|
||||||
dev_database.db
|
dev_database.db
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ from .. import models as mdl
|
|||||||
from .. import schemas as sch
|
from .. import schemas as sch
|
||||||
from ..database import add_transaction
|
from ..database import add_transaction
|
||||||
|
|
||||||
async def add_game(db: AsyncSession, game_info: sch.GameCreate, user_id: int):
|
async def add_game(db: AsyncSession,
|
||||||
|
game_info: sch.GameCreate,
|
||||||
|
user_id: int):
|
||||||
game = mdl.Game(**game_info.model_dump(), owner_id=user_id)
|
game = mdl.Game(**game_info.model_dump(), owner_id=user_id)
|
||||||
return await add_transaction(db, game)
|
return await add_transaction(db, game)
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class Game(Base):
|
|||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
cover = Column(String)
|
cover = Column(String)
|
||||||
title = Column(String, nullable=False)
|
title = Column(String, nullable=False, unique=True)
|
||||||
description = Column(String)
|
description = Column(String)
|
||||||
torrent_file = Column(String, nullable=False)
|
torrent_file = Column(String, nullable=False)
|
||||||
language = Column(String)
|
language = Column(String)
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ from pydantic import BaseModel
|
|||||||
|
|
||||||
|
|
||||||
class GameBase(BaseModel):
|
class GameBase(BaseModel):
|
||||||
cover: str | None = None
|
|
||||||
title: str
|
title: str
|
||||||
|
cover: str | None = None
|
||||||
description: str | None = None
|
description: str | None = None
|
||||||
torrent_file: str
|
torrent_file: str
|
||||||
language: str | None = None
|
language: str | None = None
|
||||||
|
|||||||
42
file_handler.py
Normal file
42
file_handler.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import hashlib
|
||||||
|
from io import BytesIO
|
||||||
|
import mimetypes
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Literal
|
||||||
|
import aiofiles
|
||||||
|
from fastapi import UploadFile
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
def create_hash_name(filename: str):
|
||||||
|
return str(hashlib.sha1(filename.encode()).hexdigest())
|
||||||
|
|
||||||
|
async def save_torrent_file(torrent: UploadFile):
|
||||||
|
hash_filename = create_hash_name(torrent.filename)+".torrent"
|
||||||
|
async with aiofiles.open(Path() / "content" / "torrent"
|
||||||
|
/ hash_filename, 'wb') as file:
|
||||||
|
await file.write(await torrent.read())
|
||||||
|
return hash_filename
|
||||||
|
|
||||||
|
async def save_image(cover: UploadFile, type: Literal["cover", "screenshot"]):
|
||||||
|
hash_filename = create_hash_name(cover.filename) \
|
||||||
|
+ mimetypes.guess_extension(cover.content_type)
|
||||||
|
async with aiofiles.open(Path() / "content" / "images" / type / "full_size"
|
||||||
|
/ hash_filename, 'wb') as full_size_file, \
|
||||||
|
aiofiles.open(Path() / "content" / "images" / type /
|
||||||
|
"preview" / hash_filename, 'wb') as preview_file:
|
||||||
|
cover_data = await cover.read()
|
||||||
|
await full_size_file.write(cover_data)
|
||||||
|
image = Image.open(BytesIO(cover_data))
|
||||||
|
compressed_coefficient = (image.size[0] * image.size[1]) / (1280*720/4)
|
||||||
|
if(compressed_coefficient < 1): compressed_coefficient = 1
|
||||||
|
compressed_image = image.resize(
|
||||||
|
( int(image.size[0] / compressed_coefficient),
|
||||||
|
int(image.size[1] / compressed_coefficient) )
|
||||||
|
)
|
||||||
|
|
||||||
|
buf = BytesIO()
|
||||||
|
compressed_image.save(buf, format=
|
||||||
|
cover.content_type.upper().replace("IMAGE/", ""))
|
||||||
|
await preview_file.write(buf.getbuffer())
|
||||||
|
return hash_filename
|
||||||
|
|
||||||
2
main.py
2
main.py
@@ -6,7 +6,9 @@ import cli_commands
|
|||||||
from routes import *
|
from routes import *
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
app.include_router(startup_router)
|
||||||
app.include_router(games_router)
|
app.include_router(games_router)
|
||||||
|
app.include_router(files_router)
|
||||||
|
|
||||||
cli = typer.Typer()
|
cli = typer.Typer()
|
||||||
cli.add_typer(cli_commands.cli, name="database")
|
cli.add_typer(cli_commands.cli, name="database")
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ pydantic==2.7.1
|
|||||||
SQLAlchemy==2.0.30
|
SQLAlchemy==2.0.30
|
||||||
aiosqlite==0.20.0
|
aiosqlite==0.20.0
|
||||||
typer==0.12.3
|
typer==0.12.3
|
||||||
|
aiofiles==23.2.1
|
||||||
@@ -1 +1,3 @@
|
|||||||
from .games import router as games_router
|
from .games import router as games_router
|
||||||
|
from .files import router as files_router
|
||||||
|
from .startup import router as startup_router
|
||||||
20
routes/files.py
Normal file
20
routes/files.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
from fastapi import APIRouter, Depends, HTTPException, UploadFile
|
||||||
|
|
||||||
|
from database import *
|
||||||
|
from file_handler import *
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/files", tags=["Files"])
|
||||||
|
|
||||||
|
@router.post("/torrent", response_model=str)
|
||||||
|
async def upload_torrent(torrent: UploadFile):
|
||||||
|
try: return await save_torrent_file(torrent)
|
||||||
|
except Exception as ex:
|
||||||
|
print(ex)
|
||||||
|
raise HTTPException(500)
|
||||||
|
|
||||||
|
@router.post("/cover", response_model=str)
|
||||||
|
async def upload_cover(cover: UploadFile):
|
||||||
|
try: return await save_image(cover, "cover")
|
||||||
|
except Exception as ex:
|
||||||
|
print(ex)
|
||||||
|
raise HTTPException(500)
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException, UploadFile
|
||||||
|
|
||||||
from database import *
|
from database import *
|
||||||
|
from file_handler import *
|
||||||
|
|
||||||
router = APIRouter(prefix="/games", tags=["Games"])
|
router = APIRouter(prefix="/games", tags=["Games"])
|
||||||
|
|
||||||
@@ -14,7 +15,13 @@ async def get_game(game_id: int, db: AsyncSession = Depends(get_session)):
|
|||||||
return await crud.get_game(db, game_id)
|
return await crud.get_game(db, game_id)
|
||||||
|
|
||||||
@router.post("/", response_model=Game)
|
@router.post("/", response_model=Game)
|
||||||
async def add_game(game: GameCreate, user_id: int, db:AsyncSession = Depends(get_session)):
|
async def add_game(game: GameCreate,
|
||||||
try: return await crud.add_game(db, game, user_id)
|
user_id: int,
|
||||||
except Exception as ex: raise HTTPException(500)
|
db:AsyncSession = Depends(get_session)):
|
||||||
|
try:
|
||||||
|
torrent_filename = save_torrent_file(torrent, game.title)
|
||||||
|
cover_filename = save_image(cover, game.title, "cover")
|
||||||
|
return await crud.add_game(db, game, user_id)
|
||||||
|
except Exception as ex:
|
||||||
|
raise HTTPException(500)
|
||||||
|
|
||||||
16
routes/startup.py
Normal file
16
routes/startup.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from fastapi import APIRouter
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
@router.on_event("startup")
|
||||||
|
def startup():
|
||||||
|
need_paths = [
|
||||||
|
Path() / "content" / "images" / "cover" / "full_size",
|
||||||
|
Path() / "content" / "images" / "cover" / "preview",
|
||||||
|
Path() / "content" / "images" / "screenshot" / "full_size",
|
||||||
|
Path() / "content" / "images" / "screenshot" / "preview",
|
||||||
|
Path() / "content" / "torrent"
|
||||||
|
]
|
||||||
|
for path in need_paths:
|
||||||
|
path.mkdir(parents=True, exist_ok=True)
|
||||||
Reference in New Issue
Block a user