2020-12-15 22:33:43 +01:00
|
|
|
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 = {}
|
2020-12-24 23:49:38 +01:00
|
|
|
RSS_URL = f"/rss{request.environ['REQUEST_URI']}"
|
|
|
|
context["RSS_URL"] = RSS_URL
|
2020-12-15 22:33:43 +01:00
|
|
|
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()
|
|
|
|
|
|
|
|
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:
|
|
|
|
query = clean_query(query)
|
|
|
|
q_parts = [q_part 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)
|
|
|
|
|
|
|
|
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", media_type="video"
|
|
|
|
).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 = (permissions.IsAuthenticatedOrReadOnly, IsUserOrEditor)
|
|
|
|
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)
|