1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300 |
- from datetime import datetime, timedelta
- from django.shortcuts import render
- from django.http import HttpResponseRedirect
- from django.conf import settings
- from django.shortcuts import get_object_or_404
- from django.db.models import Q
- from django.contrib.auth.decorators import login_required
- from django.contrib import messages
- from django.template.defaultfilters import slugify
- from django.core.mail import EmailMessage
- from django.contrib.postgres.search import SearchQuery
- from rest_framework import permissions
- from rest_framework.views import APIView
- from rest_framework.response import Response
- from rest_framework.settings import api_settings
- from rest_framework.exceptions import PermissionDenied
- from rest_framework import status
- from rest_framework.parsers import (
- JSONParser,
- MultiPartParser,
- FileUploadParser,
- FormParser,
- )
- from celery.task.control import revoke
- from cms.permissions import IsAuthorizedToAdd, IsUserOrEditor
- from cms.permissions import user_allowed_to_upload
- from cms.custom_pagination import FastPaginationWithoutCount
- from actions.models import MediaAction, USER_MEDIA_ACTIONS
- from users.models import User
- from .helpers import produce_ffmpeg_commands, clean_query
- from .models import (
- Media,
- EncodeProfile,
- Encoding,
- Playlist,
- PlaylistMedia,
- Comment,
- Category,
- Tag,
- )
- from .forms import MediaForm, ContactForm, SubtitleForm
- from .tasks import save_user_action
- from .methods import (
- list_tasks,
- get_user_or_session,
- show_recommended_media,
- show_related_media,
- is_mediacms_editor,
- is_mediacms_manager,
- update_user_ratings,
- notify_user_on_comment,
- )
- from .serializers import (
- MediaSerializer,
- CategorySerializer,
- TagSerializer,
- SingleMediaSerializer,
- EncodeProfileSerializer,
- MediaSearchSerializer,
- PlaylistSerializer,
- PlaylistDetailSerializer,
- CommentSerializer,
- )
- from .stop_words import STOP_WORDS
- VALID_USER_ACTIONS = [action for action, name in USER_MEDIA_ACTIONS]
- def about(request):
- """About view"""
- context = {}
- return render(request, "cms/about.html", context)
- @login_required
- def add_subtitle(request):
- """Add subtitle view"""
- friendly_token = request.GET.get("m", "").strip()
- if not friendly_token:
- return HttpResponseRedirect("/")
- media = Media.objects.filter(friendly_token=friendly_token).first()
- if not media:
- return HttpResponseRedirect("/")
- if not (
- request.user == media.user
- or is_mediacms_editor(request.user)
- or is_mediacms_manager(request.user)
- ):
- return HttpResponseRedirect("/")
- if request.method == "POST":
- form = SubtitleForm(media, request.POST, request.FILES)
- if form.is_valid():
- subtitle = form.save()
- messages.add_message(request, messages.INFO, "Subtitle was added!")
- return HttpResponseRedirect(subtitle.media.get_absolute_url())
- else:
- form = SubtitleForm(media_item=media)
- return render(request, "cms/add_subtitle.html", {"form": form})
- def categories(request):
- """List categories view"""
- context = {}
- return render(request, "cms/categories.html", context)
- def contact(request):
- """Contact view"""
- context = {}
- if request.method == "GET":
- form = ContactForm(request.user)
- context["form"] = form
- else:
- form = ContactForm(request.user, request.POST)
- if form.is_valid():
- if request.user.is_authenticated:
- from_email = request.user.email
- name = request.user.name
- else:
- from_email = request.POST.get("from_email")
- name = request.POST.get("name")
- message = request.POST.get("message")
- title = "[{}] - Contact form message received".format(settings.PORTAL_NAME)
- msg = """
- You have received a message through the contact form\n
- Sender name: %s
- Sender email: %s\n
- \n %s
- """ % (
- name,
- from_email,
- message,
- )
- email = EmailMessage(
- title,
- msg,
- settings.DEFAULT_FROM_EMAIL,
- settings.ADMIN_EMAIL_LIST,
- reply_to=[from_email],
- )
- email.send(fail_silently=True)
- success_msg = "Message was sent! Thanks for contacting"
- context["success_msg"] = success_msg
- return render(request, "cms/contact.html", context)
- def history(request):
- """Show personal history view"""
- context = {}
- return render(request, "cms/history.html", context)
- @login_required
- def edit_media(request):
- """Edit a media view"""
- friendly_token = request.GET.get("m", "").strip()
- if not friendly_token:
- return HttpResponseRedirect("/")
- media = Media.objects.filter(friendly_token=friendly_token).first()
- if not media:
- return HttpResponseRedirect("/")
- if not (
- request.user == media.user
- or is_mediacms_editor(request.user)
- or is_mediacms_manager(request.user)
- ):
- return HttpResponseRedirect("/")
- if request.method == "POST":
- form = MediaForm(request.user, request.POST, request.FILES, instance=media)
- if form.is_valid():
- media = form.save()
- for tag in media.tags.all():
- media.tags.remove(tag)
- if form.cleaned_data.get("new_tags"):
- for tag in form.cleaned_data.get("new_tags").split(","):
- tag = slugify(tag)
- if tag:
- try:
- tag = Tag.objects.get(title=tag)
- except Tag.DoesNotExist:
- tag = Tag.objects.create(title=tag, user=request.user)
- if tag not in media.tags.all():
- media.tags.add(tag)
- messages.add_message(request, messages.INFO, "Media was edited!")
- return HttpResponseRedirect(media.get_absolute_url())
- else:
- form = MediaForm(request.user, instance=media)
- return render(
- request,
- "cms/edit_media.html",
- {"form": form, "add_subtitle_url": media.add_subtitle_url},
- )
- def embed_media(request):
- """Embed media view"""
- friendly_token = request.GET.get("m", "").strip()
- if not friendly_token:
- return HttpResponseRedirect("/")
- media = Media.objects.values("title").filter(friendly_token=friendly_token).first()
- if not media:
- return HttpResponseRedirect("/")
- user_or_session = get_user_or_session(request)
- context = {}
- context["media"] = friendly_token
- return render(request, "cms/embed.html", context)
- def featured_media(request):
- """List featured media view"""
- context = {}
- return render(request, "cms/featured-media.html", context)
- def index(request):
- """Index view"""
- context = {}
- return render(request, "cms/index.html", context)
- def latest_media(request):
- """List latest media view"""
- context = {}
- return render(request, "cms/latest-media.html", context)
- def liked_media(request):
- """List user's liked media view"""
- context = {}
- return render(request, "cms/liked_media.html", context)
- @login_required
- def manage_users(request):
- """List users management view"""
- context = {}
- return render(request, "cms/manage_users.html", context)
- @login_required
- def manage_media(request):
- """List media management view"""
- context = {}
- return render(request, "cms/manage_media.html", context)
- @login_required
- def manage_comments(request):
- """List comments management view"""
- context = {}
- return render(request, "cms/manage_comments.html", context)
- def members(request):
- """List members view"""
- context = {}
- return render(request, "cms/members.html", context)
- def recommended_media(request):
- """List recommended media view"""
- context = {}
- return render(request, "cms/recommended-media.html", context)
- def search(request):
- """Search view"""
- context = {}
- RSS_URL = f"/rss{request.environ['REQUEST_URI']}"
- context["RSS_URL"] = RSS_URL
- return render(request, "cms/search.html", context)
- def tags(request):
- """List tags view"""
- context = {}
- return render(request, "cms/tags.html", context)
- def tos(request):
- """Terms of service view"""
- context = {}
- return render(request, "cms/tos.html", context)
- def upload_media(request):
- """Upload media view"""
- from allauth.account.forms import LoginForm
- form = LoginForm()
- context = {}
- context["form"] = form
- context["can_add"] = user_allowed_to_upload(request)
- can_upload_exp = settings.CANNOT_ADD_MEDIA_MESSAGE
- context["can_upload_exp"] = can_upload_exp
- return render(request, "cms/add-media.html", context)
- def view_media(request):
- """View media view"""
- friendly_token = request.GET.get("m", "").strip()
- context = {}
- media = Media.objects.filter(friendly_token=friendly_token).first()
- if not media:
- context["media"] = None
- return render(request, "cms/media.html", context)
- user_or_session = get_user_or_session(request)
- save_user_action.delay(
- user_or_session, friendly_token=friendly_token, action="watch"
- )
- context = {}
- context["media"] = friendly_token
- context["media_object"] = media
- context["CAN_DELETE_MEDIA"] = False
- context["CAN_EDIT_MEDIA"] = False
- context["CAN_DELETE_COMMENTS"] = False
- if request.user.is_authenticated:
- if (
- (media.user.id == request.user.id)
- or is_mediacms_editor(request.user)
- or is_mediacms_manager(request.user)
- ):
- context["CAN_DELETE_MEDIA"] = True
- context["CAN_EDIT_MEDIA"] = True
- context["CAN_DELETE_COMMENTS"] = True
- return render(request, "cms/media.html", context)
- def view_playlist(request, friendly_token):
- """View playlist view"""
- try:
- playlist = Playlist.objects.get(friendly_token=friendly_token)
- except BaseException:
- playlist = None
- context = {}
- context["playlist"] = playlist
- return render(request, "cms/playlist.html", context)
- class MediaList(APIView):
- """Media listings views"""
- permission_classes = (IsAuthorizedToAdd,)
- parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
- def get(self, request, format=None):
- # Show media
- params = self.request.query_params
- show_param = params.get("show", "")
- author_param = params.get("author", "").strip()
- if author_param:
- user_queryset = User.objects.all()
- user = get_object_or_404(user_queryset, username=author_param)
- if show_param == "recommended":
- pagination_class = FastPaginationWithoutCount
- media = show_recommended_media(request, limit=50)
- else:
- pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
- if author_param:
- # in case request.user is the user here, show
- # all media independant of state
- if self.request.user == user:
- basic_query = Q(user=user)
- else:
- basic_query = Q(listable=True, user=user)
- else:
- # base listings should show safe content
- basic_query = Q(listable=True)
- if show_param == "featured":
- media = Media.objects.filter(basic_query, featured=True)
- else:
- media = Media.objects.filter(basic_query).order_by("-add_date")
- paginator = pagination_class()
- if show_param != "recommended":
- media = media.prefetch_related("user")
- page = paginator.paginate_queryset(media, request)
- serializer = MediaSerializer(page, many=True, context={"request": request})
- return paginator.get_paginated_response(serializer.data)
- def post(self, request, format=None):
- # Add new media
- serializer = MediaSerializer(data=request.data, context={"request": request})
- if serializer.is_valid():
- media_file = request.data["media_file"]
- serializer.save(user=request.user, media_file=media_file)
- return Response(serializer.data, status=status.HTTP_201_CREATED)
- return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
- class MediaDetail(APIView):
- """
- Retrieve, update or delete a media instance.
- """
- permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsUserOrEditor)
- parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
- def get_object(self, friendly_token, password=None):
- try:
- media = (
- Media.objects.select_related("user")
- .prefetch_related("encodings__profile")
- .get(friendly_token=friendly_token)
- )
- # this need be explicitly called, and will call
- # has_object_permission() after has_permission has succeeded
- self.check_object_permissions(self.request, media)
- if media.state == "private" and not (
- self.request.user == media.user or is_mediacms_editor(self.request.user)
- ):
- if (
- (not password)
- or (not media.password)
- or (password != media.password)
- ):
- return Response(
- {"detail": "media is private"},
- status=status.HTTP_401_UNAUTHORIZED,
- )
- return media
- except PermissionDenied:
- return Response(
- {"detail": "bad permissions"}, status=status.HTTP_401_UNAUTHORIZED
- )
- except BaseException:
- return Response(
- {"detail": "media file does not exist"},
- status=status.HTTP_400_BAD_REQUEST,
- )
- def get(self, request, friendly_token, format=None):
- # Get media details
- password = request.GET.get("password")
- media = self.get_object(friendly_token, password=password)
- if isinstance(media, Response):
- return media
- serializer = SingleMediaSerializer(media, context={"request": request})
- if media.state == "private":
- related_media = []
- else:
- related_media = show_related_media(media, request=request, limit=100)
- related_media_serializer = MediaSerializer(
- related_media, many=True, context={"request": request}
- )
- related_media = related_media_serializer.data
- ret = serializer.data
- # update rattings info with user specific ratings
- # eg user has already rated for this media
- # this only affects user rating and only if enabled
- if (
- settings.ALLOW_RATINGS
- and ret.get("ratings_info")
- and not request.user.is_anonymous
- ):
- ret["ratings_info"] = update_user_ratings(
- request.user, media, ret.get("ratings_info")
- )
- ret["related_media"] = related_media
- return Response(ret)
- def post(self, request, friendly_token, format=None):
- """superuser actions
- Available only to MediaCMS editors and managers
- Action is a POST variable, review and encode are implemented
- """
- media = self.get_object(friendly_token)
- if isinstance(media, Response):
- return media
- if not (is_mediacms_editor(request.user) or is_mediacms_manager(request.user)):
- return Response(
- {"detail": "not allowed"}, status=status.HTTP_400_BAD_REQUEST
- )
- action = request.data.get("type")
- profiles_list = request.data.get("encoding_profiles")
- result = request.data.get("result", True)
- if action == "encode":
- # Create encoding tasks for specific profiles
- valid_profiles = []
- if profiles_list:
- if isinstance(profiles_list, list):
- for p in profiles_list:
- p = EncodeProfile.objects.filter(id=p).first()
- if p:
- valid_profiles.append(p)
- elif isinstance(profiles_list, str):
- try:
- p = EncodeProfile.objects.filter(id=int(profiles_list)).first()
- valid_profiles.append(p)
- except ValueError:
- return Response(
- {
- "detail": "encoding_profiles must be int or list of ints of valid encode profiles"
- },
- status=status.HTTP_400_BAD_REQUEST,
- )
- media.encode(profiles=valid_profiles)
- return Response(
- {"detail": "media will be encoded"}, status=status.HTTP_201_CREATED
- )
- elif action == "review":
- if result:
- media.is_reviewed = True
- elif result == False:
- media.is_reviewed = False
- media.save(update_fields=["is_reviewed"])
- return Response(
- {"detail": "media reviewed set"}, status=status.HTTP_201_CREATED
- )
- return Response(
- {"detail": "not valid action or no action specified"},
- status=status.HTTP_400_BAD_REQUEST,
- )
- def put(self, request, friendly_token, format=None):
- # Update a media object
- media = self.get_object(friendly_token)
- if isinstance(media, Response):
- return media
- serializer = MediaSerializer(
- media, data=request.data, context={"request": request}
- )
- if serializer.is_valid():
- media_file = request.data["media_file"]
- serializer.save(user=request.user, media_file=media_file)
- return Response(serializer.data, status=status.HTTP_201_CREATED)
- return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
- def delete(self, request, friendly_token, format=None):
- # Delete a media object
- media = self.get_object(friendly_token)
- if isinstance(media, Response):
- return media
- media.delete()
- return Response(status=status.HTTP_204_NO_CONTENT)
- class MediaActions(APIView):
- """
- Retrieve, update or delete a media action instance.
- """
- permission_classes = (permissions.AllowAny,)
- parser_classes = (JSONParser,)
- def get_object(self, friendly_token):
- try:
- media = (
- Media.objects.select_related("user")
- .prefetch_related("encodings__profile")
- .get(friendly_token=friendly_token)
- )
- if media.state == "private" and self.request.user != media.user:
- return Response(
- {"detail": "media is private"}, status=status.HTTP_400_BAD_REQUEST
- )
- return media
- except PermissionDenied:
- return Response(
- {"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST
- )
- except BaseException:
- return Response(
- {"detail": "media file does not exist"},
- status=status.HTTP_400_BAD_REQUEST,
- )
- def get(self, request, friendly_token, format=None):
- # show date and reason for each time media was reported
- media = self.get_object(friendly_token)
- if isinstance(media, Response):
- return media
- ret = {}
- reported = MediaAction.objects.filter(media=media, action="report")
- ret["reported"] = []
- for rep in reported:
- item = {"reported_date": rep.action_date, "reason": rep.extra_info}
- ret["reported"].append(item)
- return Response(ret, status=status.HTTP_200_OK)
- def post(self, request, friendly_token, format=None):
- # perform like/dislike/report actions
- media = self.get_object(friendly_token)
- if isinstance(media, Response):
- return media
- action = request.data.get("type")
- extra = request.data.get("extra_info")
- if request.user.is_anonymous:
- # there is a list of allowed actions for
- # anonymous users, specified in settings
- if action not in settings.ALLOW_ANONYMOUS_ACTIONS:
- return Response(
- {"detail": "action allowed on logged in users only"},
- status=status.HTTP_400_BAD_REQUEST,
- )
- if action:
- user_or_session = get_user_or_session(request)
- save_user_action.delay(
- user_or_session,
- friendly_token=media.friendly_token,
- action=action,
- extra_info=extra,
- )
- return Response(
- {"detail": "action received"}, status=status.HTTP_201_CREATED
- )
- else:
- return Response(
- {"detail": "no action specified"}, status=status.HTTP_400_BAD_REQUEST
- )
- def delete(self, request, friendly_token, format=None):
- media = self.get_object(friendly_token)
- if isinstance(media, Response):
- return media
- if not request.user.is_superuser:
- return Response(
- {"detail": "not allowed"}, status=status.HTTP_400_BAD_REQUEST
- )
- action = request.data.get("type")
- if action:
- if action == "report": # delete reported actions
- MediaAction.objects.filter(media=media, action="report").delete()
- media.reported_times = 0
- media.save(update_fields=["reported_times"])
- return Response(
- {"detail": "reset reported times counter"},
- status=status.HTTP_201_CREATED,
- )
- else:
- return Response(
- {"detail": "no action specified"}, status=status.HTTP_400_BAD_REQUEST
- )
- class MediaSearch(APIView):
- """
- Retrieve results for searc
- Only GET is implemented here
- """
- parser_classes = (JSONParser,)
- def get(self, request, format=None):
- params = self.request.query_params
- query = params.get("q", "").strip().lower()
- category = params.get("c", "").strip()
- tag = params.get("t", "").strip()
- ordering = params.get("ordering", "").strip()
- sort_by = params.get("sort_by", "").strip()
- media_type = params.get("media_type", "").strip()
- author = params.get("author", "").strip()
- upload_date = params.get('upload_date', '').strip()
- sort_by_options = ["title", "add_date", "edit_date", "views", "likes"]
- if sort_by not in sort_by_options:
- sort_by = "add_date"
- if ordering == "asc":
- ordering = ""
- else:
- ordering = "-"
- if media_type not in ["video", "image", "audio", "pdf"]:
- media_type = None
- if not (query or category or tag):
- ret = {}
- return Response(ret, status=status.HTTP_200_OK)
- media = Media.objects.filter(state="public", is_reviewed=True)
- if query:
- # move this processing to a prepare_query function
- query = clean_query(query)
- q_parts = [
- q_part.rstrip("y")
- for q_part in query.split()
- if q_part not in STOP_WORDS
- ]
- if q_parts:
- query = SearchQuery(q_parts[0] + ":*", search_type="raw")
- for part in q_parts[1:]:
- query &= SearchQuery(part + ":*", search_type="raw")
- else:
- query = None
- if query:
- media = media.filter(search=query)
- if tag:
- media = media.filter(tags__title=tag)
- if category:
- media = media.filter(category__title__contains=category)
- if media_type:
- media = media.filter(media_type=media_type)
- if author:
- media = media.filter(user__username=author)
- if upload_date:
- gte = lte = None
- if upload_date == 'today':
- gte = datetime.now().date()
- if upload_date == 'this_week':
- gte = datetime.now() - timedelta(days=7)
- if upload_date == 'this_month':
- year = datetime.now().date().year
- month = datetime.now().date().month
- gte = datetime(year,month,1)
- if upload_date == 'this_year':
- year = datetime.now().date().year
- gte = datetime(year,1,1)
- if lte:
- media = media.filter(add_date__lte=lte)
- if gte:
- media = media.filter(add_date__gte=gte)
- media = media.order_by(f"{ordering}{sort_by}")
- if self.request.query_params.get("show", "").strip() == "titles":
- media = media.values("title")[:40]
- return Response(media, status=status.HTTP_200_OK)
- else:
- media = media.prefetch_related("user")
- if category or tag:
- pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
- else:
- # pagination_class = FastPaginationWithoutCount
- pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
- paginator = pagination_class()
- page = paginator.paginate_queryset(media, request)
- serializer = MediaSearchSerializer(
- page, many=True, context={"request": request}
- )
- return paginator.get_paginated_response(serializer.data)
- class PlaylistList(APIView):
- """Playlists listings and creation views"""
- permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsAuthorizedToAdd)
- parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
- def get(self, request, format=None):
- pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
- paginator = pagination_class()
- playlists = Playlist.objects.filter().prefetch_related("user")
- if "author" in self.request.query_params:
- author = self.request.query_params["author"].strip()
- playlists = playlists.filter(user__username=author)
- page = paginator.paginate_queryset(playlists, request)
- serializer = PlaylistSerializer(page, many=True, context={"request": request})
- return paginator.get_paginated_response(serializer.data)
- def post(self, request, format=None):
- serializer = PlaylistSerializer(data=request.data, context={"request": request})
- if serializer.is_valid():
- serializer.save(user=request.user)
- return Response(serializer.data, status=status.HTTP_201_CREATED)
- return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
- class PlaylistDetail(APIView):
- """Playlist related views"""
- permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsUserOrEditor)
- parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
- def get_playlist(self, friendly_token):
- try:
- playlist = Playlist.objects.get(friendly_token=friendly_token)
- self.check_object_permissions(self.request, playlist)
- return playlist
- except PermissionDenied:
- return Response(
- {"detail": "not enough permissions"}, status=status.HTTP_400_BAD_REQUEST
- )
- except BaseException:
- return Response(
- {"detail": "Playlist does not exist"},
- status=status.HTTP_400_BAD_REQUEST,
- )
- def get(self, request, friendly_token, format=None):
- playlist = self.get_playlist(friendly_token)
- if isinstance(playlist, Response):
- return playlist
- serializer = PlaylistDetailSerializer(playlist, context={"request": request})
- playlist_media = PlaylistMedia.objects.filter(
- playlist=playlist
- ).prefetch_related("media__user")
- playlist_media = [c.media for c in playlist_media]
- playlist_media_serializer = MediaSerializer(
- playlist_media, many=True, context={"request": request}
- )
- ret = serializer.data
- ret["playlist_media"] = playlist_media_serializer.data
- return Response(ret)
- def post(self, request, friendly_token, format=None):
- playlist = self.get_playlist(friendly_token)
- if isinstance(playlist, Response):
- return playlist
- serializer = PlaylistDetailSerializer(
- playlist, data=request.data, context={"request": request}
- )
- if serializer.is_valid():
- serializer.save(user=request.user)
- return Response(serializer.data, status=status.HTTP_201_CREATED)
- return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
- def put(self, request, friendly_token, format=None):
- playlist = self.get_playlist(friendly_token)
- if isinstance(playlist, Response):
- return playlist
- action = request.data.get("type")
- media_friendly_token = request.data.get("media_friendly_token")
- ordering = 0
- if request.data.get("ordering"):
- try:
- ordering = int(request.data.get("ordering"))
- except ValueError:
- pass
- if action in ["add", "remove", "ordering"]:
- media = Media.objects.filter(
- friendly_token=media_friendly_token, state="public"
- ).first()
- if media:
- if action == "add":
- media_in_playlist = PlaylistMedia.objects.filter(
- playlist=playlist
- ).count()
- if media_in_playlist >= settings.MAX_MEDIA_PER_PLAYLIST:
- return Response(
- {"detail": "max number of media for a Playlist reached"},
- status=status.HTTP_400_BAD_REQUEST,
- )
- else:
- obj, created = PlaylistMedia.objects.get_or_create(
- playlist=playlist,
- media=media,
- ordering=media_in_playlist + 1,
- )
- obj.save()
- return Response(
- {"detail": "media added to Playlist"},
- status=status.HTTP_201_CREATED,
- )
- elif action == "remove":
- PlaylistMedia.objects.filter(
- playlist=playlist, media=media
- ).delete()
- return Response(
- {"detail": "media removed from Playlist"},
- status=status.HTTP_201_CREATED,
- )
- elif action == "ordering":
- if ordering:
- playlist.set_ordering(media, ordering)
- return Response(
- {"detail": "new ordering set"},
- status=status.HTTP_201_CREATED,
- )
- else:
- return Response(
- {"detail": "media is not valid"}, status=status.HTTP_400_BAD_REQUEST
- )
- return Response(
- {"detail": "invalid or not specified action"},
- status=status.HTTP_400_BAD_REQUEST,
- )
- def delete(self, request, friendly_token, format=None):
- playlist = self.get_playlist(friendly_token)
- if isinstance(playlist, Response):
- return playlist
- playlist.delete()
- return Response(status=status.HTTP_204_NO_CONTENT)
- class EncodingDetail(APIView):
- """Experimental. This View is used by remote workers
- Needs heavy testing and documentation.
- """
- permission_classes = (permissions.IsAdminUser,)
- parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
- def post(self, request, encoding_id):
- ret = {}
- force = request.data.get("force", False)
- task_id = request.data.get("task_id", False)
- action = request.data.get("action", "")
- chunk = request.data.get("chunk", False)
- chunk_file_path = request.data.get("chunk_file_path", "")
- encoding_status = request.data.get("status", "")
- progress = request.data.get("progress", "")
- commands = request.data.get("commands", "")
- logs = request.data.get("logs", "")
- retries = request.data.get("retries", "")
- worker = request.data.get("worker", "")
- temp_file = request.data.get("temp_file", "")
- total_run_time = request.data.get("total_run_time", "")
- if action == "start":
- try:
- encoding = Encoding.objects.get(id=encoding_id)
- media = encoding.media
- profile = encoding.profile
- except BaseException:
- Encoding.objects.filter(id=encoding_id).delete()
- return Response({"status": "fail"}, status=status.HTTP_400_BAD_REQUEST)
- # TODO: break chunk True/False logic here
- if (
- Encoding.objects.filter(
- media=media,
- profile=profile,
- chunk=chunk,
- chunk_file_path=chunk_file_path,
- ).count()
- > 1
- and force == False
- ):
- Encoding.objects.filter(id=encoding_id).delete()
- return Response({"status": "fail"}, status=status.HTTP_400_BAD_REQUEST)
- else:
- Encoding.objects.filter(
- media=media,
- profile=profile,
- chunk=chunk,
- chunk_file_path=chunk_file_path,
- ).exclude(id=encoding.id).delete()
- encoding.status = "running"
- if task_id:
- encoding.task_id = task_id
- encoding.save()
- if chunk:
- original_media_path = chunk_file_path
- original_media_md5sum = encoding.md5sum
- original_media_url = (
- settings.SSL_FRONTEND_HOST + encoding.media_chunk_url
- )
- else:
- original_media_path = media.media_file.path
- original_media_md5sum = media.md5sum
- original_media_url = (
- settings.SSL_FRONTEND_HOST + media.original_media_url
- )
- ret["original_media_url"] = original_media_url
- ret["original_media_path"] = original_media_path
- ret["original_media_md5sum"] = original_media_md5sum
- # generating the commands here, and will replace these with temporary
- # files created on the remote server
- tf = "TEMP_FILE_REPLACE"
- tfpass = "TEMP_FPASS_FILE_REPLACE"
- ffmpeg_commands = produce_ffmpeg_commands(
- original_media_path,
- media.media_info,
- resolution=profile.resolution,
- codec=profile.codec,
- output_filename=tf,
- pass_file=tfpass,
- chunk=chunk,
- )
- if not ffmpeg_commands:
- encoding.delete()
- return Response({"status": "fail"}, status=status.HTTP_400_BAD_REQUEST)
- ret["duration"] = media.duration
- ret["ffmpeg_commands"] = ffmpeg_commands
- ret["profile_extension"] = profile.extension
- return Response(ret, status=status.HTTP_201_CREATED)
- elif action == "update_fields":
- try:
- encoding = Encoding.objects.get(id=encoding_id)
- except BaseException:
- return Response({"status": "fail"}, status=status.HTTP_400_BAD_REQUEST)
- to_update = ["size", "update_date"]
- if encoding_status:
- encoding.status = encoding_status
- to_update.append("status")
- if progress:
- encoding.progress = progress
- to_update.append("progress")
- if logs:
- encoding.logs = logs
- to_update.append("logs")
- if commands:
- encoding.commands = commands
- to_update.append("commands")
- if task_id:
- encoding.task_id = task_id
- to_update.append("task_id")
- if total_run_time:
- encoding.total_run_time = total_run_time
- to_update.append("total_run_time")
- if worker:
- encoding.worker = worker
- to_update.append("worker")
- if temp_file:
- encoding.temp_file = temp_file
- to_update.append("temp_file")
- if retries:
- encoding.retries = retries
- to_update.append("retries")
- try:
- encoding.save(update_fields=to_update)
- except BaseException:
- return Response({"status": "fail"}, status=status.HTTP_400_BAD_REQUEST)
- return Response({"status": "success"}, status=status.HTTP_201_CREATED)
- def put(self, request, encoding_id, format=None):
- encoding_file = request.data["file"]
- encoding = Encoding.objects.filter(id=encoding_id).first()
- if not encoding:
- return Response(
- {"detail": "encoding does not exist"},
- status=status.HTTP_400_BAD_REQUEST,
- )
- encoding.media_file = encoding_file
- encoding.save()
- return Response({"detail": "ok"}, status=status.HTTP_201_CREATED)
- class CommentList(APIView):
- permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsAuthorizedToAdd)
- parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
- def get(self, request, format=None):
- pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
- paginator = pagination_class()
- comments = Comment.objects.filter()
- comments = comments.prefetch_related("user")
- comments = comments.prefetch_related("media")
- params = self.request.query_params
- if "author" in params:
- author_param = params["author"].strip()
- user_queryset = User.objects.all()
- user = get_object_or_404(user_queryset, username=author_param)
- comments = comments.filter(user=user)
- page = paginator.paginate_queryset(comments, request)
- serializer = CommentSerializer(page, many=True, context={"request": request})
- return paginator.get_paginated_response(serializer.data)
- class CommentDetail(APIView):
- """Comments related views
- Listings of comments for a media (GET)
- Create comment (POST)
- Delete comment (DELETE)
- """
- permission_classes = (IsAuthorizedToAdd,)
- parser_classes = (JSONParser, MultiPartParser, FormParser, FileUploadParser)
- def get_object(self, friendly_token):
- try:
- media = Media.objects.select_related("user").get(
- friendly_token=friendly_token
- )
- self.check_object_permissions(self.request, media)
- if media.state == "private" and self.request.user != media.user:
- return Response(
- {"detail": "media is private"}, status=status.HTTP_400_BAD_REQUEST
- )
- return media
- except PermissionDenied:
- return Response(
- {"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST
- )
- except BaseException:
- return Response(
- {"detail": "media file does not exist"},
- status=status.HTTP_400_BAD_REQUEST,
- )
- def get(self, request, friendly_token):
- # list comments for a media
- media = self.get_object(friendly_token)
- if isinstance(media, Response):
- return media
- comments = media.comments.filter().prefetch_related("user")
- pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
- paginator = pagination_class()
- page = paginator.paginate_queryset(comments, request)
- serializer = CommentSerializer(page, many=True, context={"request": request})
- return paginator.get_paginated_response(serializer.data)
- def delete(self, request, friendly_token, uid=None):
- """Delete a comment
- Administrators, MediaCMS editors and managers,
- media owner, and comment owners, can delete a comment
- """
- if uid:
- try:
- comment = Comment.objects.get(uid=uid)
- except BaseException:
- return Response(
- {"detail": "comment does not exist"},
- status=status.HTTP_400_BAD_REQUEST,
- )
- if (
- (comment.user == self.request.user)
- or comment.media.user == self.request.user
- or is_mediacms_editor(self.request.user)
- ):
- comment.delete()
- else:
- return Response(
- {"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST
- )
- return Response(status=status.HTTP_204_NO_CONTENT)
- def post(self, request, friendly_token):
- """Create a comment"""
- media = self.get_object(friendly_token)
- if isinstance(media, Response):
- return media
- if not media.enable_comments:
- return Response(
- {"detail": "comments not allowed here"},
- status=status.HTTP_400_BAD_REQUEST,
- )
- serializer = CommentSerializer(data=request.data, context={"request": request})
- if serializer.is_valid():
- serializer.save(user=request.user, media=media)
- if request.user != media.user:
- notify_user_on_comment(friendly_token=media.friendly_token)
- return Response(serializer.data, status=status.HTTP_201_CREATED)
- return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
- class UserActions(APIView):
- parser_classes = (JSONParser,)
- def get(self, request, action):
- media = []
- if action in VALID_USER_ACTIONS:
- if request.user.is_authenticated:
- media = (
- Media.objects.select_related("user")
- .filter(
- mediaactions__user=request.user, mediaactions__action=action
- )
- .order_by("-mediaactions__action_date")
- )
- elif request.session.session_key:
- media = (
- Media.objects.select_related("user")
- .filter(
- mediaactions__session_key=request.session.session_key,
- mediaactions__action=action,
- )
- .order_by("-mediaactions__action_date")
- )
- pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
- paginator = pagination_class()
- page = paginator.paginate_queryset(media, request)
- serializer = MediaSerializer(page, many=True, context={"request": request})
- return paginator.get_paginated_response(serializer.data)
- class CategoryList(APIView):
- """List categories"""
- def get(self, request, format=None):
- categories = Category.objects.filter().order_by("title")
- serializer = CategorySerializer(
- categories, many=True, context={"request": request}
- )
- ret = serializer.data
- return Response(ret)
- class TagList(APIView):
- """List tags"""
- def get(self, request, format=None):
- tags = Tag.objects.filter().order_by("-media_count")
- pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
- paginator = pagination_class()
- page = paginator.paginate_queryset(tags, request)
- serializer = TagSerializer(page, many=True, context={"request": request})
- return paginator.get_paginated_response(serializer.data)
- class EncodeProfileList(APIView):
- """List encode profiles"""
- def get(self, request, format=None):
- profiles = EncodeProfile.objects.all()
- serializer = EncodeProfileSerializer(
- profiles, many=True, context={"request": request}
- )
- return Response(serializer.data)
- class TasksList(APIView):
- """List tasks"""
- permission_classes = (permissions.IsAdminUser,)
- def get(self, request, format=None):
- ret = list_tasks()
- return Response(ret)
- class TaskDetail(APIView):
- """Cancel a task"""
- permission_classes = (permissions.IsAdminUser,)
- def delete(self, request, uid, format=None):
- revoke(uid, terminate=True)
- return Response(status=status.HTTP_204_NO_CONTENT)
|