Files
oxpanel25/app/torrent/views.py
T
2026-04-11 22:07:59 +02:00

215 lines
6.9 KiB
Python

import anyio
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Count, OuterRef, Q, Sum
from django.db.models.functions import Coalesce
from django.http import Http404, HttpResponse, StreamingHttpResponse
from django.shortcuts import redirect
from django.urls import reverse
from django.views.generic import TemplateView
from rest_framework import mixins
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
from app.utils import StreamingZipFileResponse
from .models import File, SharedUser, Torrent
from .serializers import FileSerializer, TorrentSerializer
from .utils import torrent_proceed, torrent_share
class HomeView(LoginRequiredMixin, TemplateView):
template_name = "torrent/home.html"
def pping(request):
return HttpResponse(str(dict(request.session)))
async def download_file(request, file_id):
user = await request.auser()
qs = File.objects.filter(
Q(torrent__user=user)
| Q(torrent__shared_users=user)
| Q(torrent__user__friends=user)
| Q(torrent__shared_users__friends=user),
torrent__transmission_data__progress__gte=100,
pk=file_id,
).distinct()
try:
file = await qs.aget()
except File.DoesNotExist:
raise Http404()
else:
if int(request.GET.get("dl_hotfix", 0)) == 1:
async def read_file():
async with await anyio.open_file(file.abs_pathname, "rb") as f:
while chunk := await f.read(128 * 1024):
yield chunk
response = StreamingHttpResponse(read_file())
response["Content-Length"] = file.size
response["Content-Type"] = "application/octet-stream"
response["Content-Disposition"] = file.disposition
return response
else:
response = HttpResponse()
response["X-Accel-Redirect"] = file.accel_redirect
response["Content-Type"] = file.mime_types
response["Content-Disposition"] = file.disposition
return response
async def flux_file(request, file_id):
# todo : version non sécurisé, voir pour ajouter un contrôle IP (par ex)
qs = File.objects.filter(pk=file_id)
try:
file = await qs.aget()
except File.DoesNotExist:
raise Http404()
else:
response = HttpResponse()
response["X-Accel-Redirect"] = file.accel_redirect
response["X-Accel-Buffering"] = "no"
response["Content-Type"] = file.mime_types
response["Content-Disposition"] = file.disposition
return response
async def secured_flux_file(request, file_id):
user = await request.auser()
qs = File.objects.filter(
Q(torrent__user=user)
| Q(torrent__shared_users=user)
| Q(torrent__user__friends=user)
| Q(torrent__shared_users__friends=user),
torrent__transmission_data__progress__gte=100,
pk=file_id,
).distinct()
try:
file = await qs.aget()
except File.DoesNotExist:
raise Http404()
else:
response = HttpResponse()
response["X-Accel-Redirect"] = file.accel_redirect
response["X-Accel-Buffering"] = "no"
response["Content-Type"] = file.mime_types
response["Content-Disposition"] = file.disposition
return response
async def download_torrent(request, torrent_id):
# py version
user = await request.auser()
qs = (
Torrent.objects.filter(
Q(user=user)
| Q(shared_users=user)
| Q(user__friends=user)
| Q(shared_users__friends=user),
transmission_data__progress__gte=100,
pk=torrent_id,
)
.annotate(count_files=Count("files"))
.distinct()
)
torrent = await qs.aget()
if await torrent.alen_files == 1:
file = await torrent.files.afirst()
return redirect(reverse("torrent:download_file", kwargs={"file_id": file.pk}))
response = StreamingZipFileResponse(
filename=f"{torrent.name}.zip",
file_list=[
(file.abs_pathname, file.rel_name) async for file in torrent.files.all()
],
is_async=True,
)
return response
class TorrentViewSet(
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet,
):
queryset = Torrent.objects.all().annotate(count_files=Count("files"))
serializer_class = TorrentSerializer
def get_queryset(self):
qs = super().get_queryset()
qs.filter(
Q(user=self.request.user)
| Q(shared_users=self.request.user)
| Q(user__friends=self.request.user)
| Q(shared_users__friends=self.request.user)
)
# Récupération des torrents de l'utilisateur et de ceux partagé à celui-ci (ordonné par ordre de partage ou par date d'ajout du torrent)
user_id = self.request.query_params.get("user", None)
if user_id:
qs = qs.filter(Q(user_id=user_id) | Q(shared_users=user_id))
else:
user_id = self.request.user.id
sub = SharedUser.objects.filter(
torrent_id=OuterRef("pk"), user_id=user_id
).values("date_created")
qs = qs.annotate(last_date=Coalesce(sub, "date_created")).order_by("-last_date")
search = self.request.query_params.get("search", None)
if search:
qs = qs.filter(name__icontains=search)
return qs
def create(self, request, *args, **kwargs):
file = request.data["file"]
file_mode = request.data.get("file_mode", "file_object")
r = torrent_proceed(self.request.user, file, file_mode)
if r["torrent"]:
r["torrent"] = self.get_serializer_class()(r["torrent"]).data
return Response(r)
def perform_destroy(self, instance):
instance: Torrent
if instance.user == self.request.user:
return super().perform_destroy(instance)
else:
if instance.shared_users.filter(id=self.request.user.id).exists():
instance.shared_users.remove(self.request.user)
@action(methods=["post"], detail=True)
def share(self, request, pk):
user_id = self.request.data.get("user_id")
torrent = self.get_object()
is_share_success = torrent_share(
torrent=torrent, current_user=self.request.user, target_user_id=user_id
)
return Response({"success": is_share_success})
@action(methods=["get"], detail=False)
def user_stats(self, request):
Torrent.objects.filter(user=self.request.user).aggregate(total_size=Sum("size"))
class FileViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin, GenericViewSet):
queryset = File.objects.all()
serializer_class = FileSerializer
filterset_fields = ["torrent"]
def get_queryset(self):
qs = super().get_queryset()
return qs