Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Yujeong/materials 기본 틀 구현 #140

Merged
merged 6 commits into from
Oct 7, 2024
Merged

Yujeong/materials 기본 틀 구현 #140

merged 6 commits into from
Oct 7, 2024

Conversation

devnproyj22
Copy link
Contributor

settings.py

  • AWS S3 설정
  • .env 값은 discord에 업로드 완료

urls.py

  • 이미지 업로드 URL 패턴 추가 "images/upload/" (전: "images/")
  • 동영상 업로드 URL 패턴 추가 "videos/upload/" (전: "videos/")

models.py

  • related_name 변경: videos -> video (1:1 관계)
  • 마이그레이션은 history 모델 추가 후 할 예정

serializers.py

  • ImageSerializers 생성
  • VideoSerializers 생성
  • WatchHistorySerializer 생성 (작성 중)

views.py

  • boto3, botocore, django-storages 로 클라우드 스토리지에 연동: AWS S3 버켓에 파일 업로드 및 관리, 로컬로 다운로드

@devnproyj22 devnproyj22 linked an issue Oct 6, 2024 that may be closed by this pull request
@devnproyj22 devnproyj22 added the enhancement New feature or request label Oct 6, 2024
Comment on lines +167 to +169
# GET 요청: 특정 이미지 파일을 조회합니다.
# PUT 요청: 특정 이미지 파일을 변경합니다.
# DELETE 요청: 특정 이미지 파일을 삭제합니다.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런 형태의 주석이 표시가 안될 것 같아요. 아래 docstring 주석으로 변경하면 어떨까요?

"""
    GET 요청: 특정 이미지 파일을 조회합니다.
    PUT 요청: 특정 이미지 파일을 변경합니다.
    DELETE 요청: 특정 이미지 파일을 삭제합니다.
"""

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네. 아니면 extend_schema_view 데코레이션을 추가하시는 것도 하나의 고려사항이 될수도 있을 것 같습니다

Comment on lines 233 to 281
class VideoCreateView(generics.CreateAPIView):
# POST 요청: 영상 파일을 업로드합니다.

queryset = Video.objects.all()
serializer_class = VideoSerializer
permission_classes = []
parser_classes = (MultiPartParser, FormParser)

def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
file = request.FILES.get("file")
if not file:
return Response(
{"error": "No file provided"}, status=status.HTTP_400_BAD_REQUEST
)

# S3 클라이언트 설정
s3_client = boto3.client(
"s3",
aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
region_name=settings.AWS_S3_REGION_NAME,
)

try:
# S3에 파일 업로드
file_name = f"videos/{file.name}"
s3_client.upload_fileobj(
file,
settings.AWS_STORAGE_BUCKET_NAME,
file_name,
ExtraArgs={"ContentType": file.content_type},
)

# 업로드된 파일의 URL 생성
file_url = f"https://{settings.AWS_S3_CUSTOM_DOMAIN}/{file_name}"

# 비디오 객체 생성 및 저장
video = serializer.save(file=file_url)

return Response(
self.get_serializer(video).data, status=status.HTTP_201_CREATED
)
except ClientError as e:
return Response(
{"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

중복 코드입니다.

Comment on lines 284 to 293
class VideoListCreateView(generics.ListCreateAPIView):
# GET 요청: 영상 파일 목록을 조회합니다.

queryset = Video.objects.all()
serializer_class = VideoSerializer
permission_classes = []
permission_classes = []

def perform_create(self, serializer):
serializer.save(topic_id=self.request.data.get("topic_id"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createView가 있기 때문에 ListAPIView만 사용하면 어떨까요 ? 아니면 위 createView를 이 View와 합쳐야 할 것 같아요.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 두 view를 합치는 쪽이 괜찮아 보일 것 같습니다.
그런데 course를 조회하는 view가 있는데, 굳이 video들을 따로 조회하는 view가 필요할까요? 어차피 사용자들은 curriculum이나 course 단위로 상품을 보는게 아닌가 싶어서요. 관리자를 위한 view인걸까요?


def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
if request.query_params.get("stream", "").lower() == "true":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s3도 stream 허용한가요? 신기하네요!!

Comment on lines +312 to +352
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
if request.query_params.get("stream", "").lower() == "true":
return self.stream_video(instance)
serializer = self.get_serializer(instance)
return Response(serializer.data)

def stream_video(self, video):
s3 = boto3.client(
"s3",
aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
region_name=settings.AWS_S3_REGION_NAME,
)

file_key = str(video.file) # S3에 저장된 파일의 키

def generate():
offset = 0
chunk_size = 8388608 # 8MB 청크 크기
while True:
try:
file_object = s3.get_object(
Bucket=settings.AWS_STORAGE_BUCKET_NAME,
Key=file_key,
Range=f"bytes={offset}-{offset+chunk_size-1}",
)
data = file_object["Body"].read()
if not data:
break
yield data
offset += len(data)
except Exception as e:
print(f"영상 스트림 실패: {str(e)}")
break

response = StreamingHttpResponse(
streaming_content=generate(), content_type="video/mp4"
)
response["Content-Disposition"] = f'inline; filename="{video.title}.mp4"'
return response
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

간단히 설명해주세요.

img.verify()
except:
return Response(
{"error": "Invalid image file"}, status=status.HTTP_400_BAD_REQUEST
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나중에 에러 메세지를 한국어로 수정해주시면 좋을 것 같습니다!

Comment on lines +167 to +169
# GET 요청: 특정 이미지 파일을 조회합니다.
# PUT 요청: 특정 이미지 파일을 변경합니다.
# DELETE 요청: 특정 이미지 파일을 삭제합니다.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네. 아니면 extend_schema_view 데코레이션을 추가하시는 것도 하나의 고려사항이 될수도 있을 것 같습니다

Comment on lines 284 to 293
class VideoListCreateView(generics.ListCreateAPIView):
# GET 요청: 영상 파일 목록을 조회합니다.

queryset = Video.objects.all()
serializer_class = VideoSerializer
permission_classes = []
permission_classes = []

def perform_create(self, serializer):
serializer.save(topic_id=self.request.data.get("topic_id"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 두 view를 합치는 쪽이 괜찮아 보일 것 같습니다.
그런데 course를 조회하는 view가 있는데, 굳이 video들을 따로 조회하는 view가 필요할까요? 어차피 사용자들은 curriculum이나 course 단위로 상품을 보는게 아닌가 싶어서요. 관리자를 위한 view인걸까요?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나중에 .env에 추가할 내용을 README나 Wiki에 추가해주시면 감사하겠습니다. 사실 근데 이부분은 Wiki에만 넣는게 좋지 않을가 싶은데, 다른 분들 생각이 궁금하네요.

@devnproyj22 devnproyj22 merged commit 805b315 into dev Oct 7, 2024
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

새로운 비디오를 업로드 기능 POST /api/videos/
4 participants