format content (#198)

This commit is contained in:
Markos Gogoulos 2021-05-26 18:35:21 +03:00 committed by GitHub
parent 2d49b1df29
commit 6df942ac4e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 420 additions and 962 deletions

View file

@ -1,7 +1,7 @@
# Generated by Django 3.1.4 on 2020-12-01 07:12 # Generated by Django 3.1.4 on 2020-12-01 07:12
from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):

View file

@ -1,8 +1,8 @@
# Generated by Django 3.1.4 on 2020-12-01 07:12 # Generated by Django 3.1.4 on 2020-12-01 07:12
import django.db.models.deletion
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -35,8 +35,6 @@ class Migration(migrations.Migration):
), ),
migrations.AddIndex( migrations.AddIndex(
model_name="mediaaction", model_name="mediaaction",
index=models.Index( index=models.Index(fields=["session_key", "action"], name="actions_med_session_fac55a_idx"),
fields=["session_key", "action"], name="actions_med_session_fac55a_idx"
),
), ),
] ]

View file

@ -1,6 +1,7 @@
from django.db import models from django.db import models
from users.models import User
from files.models import Media from files.models import Media
from users.models import User
USER_MEDIA_ACTIONS = ( USER_MEDIA_ACTIONS = (
("like", "Like"), ("like", "Like"),
@ -30,15 +31,11 @@ class MediaAction(models.Model):
help_text="for not logged in users", help_text="for not logged in users",
) )
action = models.CharField( action = models.CharField(max_length=20, choices=USER_MEDIA_ACTIONS, default="watch")
max_length=20, choices=USER_MEDIA_ACTIONS, default="watch"
)
# keeps extra info, eg on report action, why it is reported # keeps extra info, eg on report action, why it is reported
extra_info = models.TextField(blank=True, null=True) extra_info = models.TextField(blank=True, null=True)
media = models.ForeignKey( media = models.ForeignKey(Media, on_delete=models.CASCADE, related_name="mediaactions")
Media, on_delete=models.CASCADE, related_name="mediaactions"
)
action_date = models.DateTimeField(auto_now_add=True) action_date = models.DateTimeField(auto_now_add=True)
remote_ip = models.CharField(max_length=40, blank=True, null=True) remote_ip = models.CharField(max_length=40, blank=True, null=True)

View file

@ -1,4 +1,5 @@
from __future__ import absolute_import from __future__ import absolute_import
from .celery import app as celery_app from .celery import app as celery_app
__all__ = ["celery_app"] __all__ = ["celery_app"]

View file

@ -1,5 +1,7 @@
from __future__ import absolute_import from __future__ import absolute_import
import os import os
from celery import Celery from celery import Celery
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cms.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cms.settings")

View file

@ -1,8 +1,9 @@
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from collections import OrderedDict # requires Python 2.7 or later from collections import OrderedDict # requires Python 2.7 or later
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.utils.functional import cached_property from django.utils.functional import cached_property
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
class FasterDjangoPaginator(Paginator): class FasterDjangoPaginator(Paginator):

View file

@ -1,5 +1,6 @@
from django.conf import settings from django.conf import settings
from rest_framework import permissions from rest_framework import permissions
from files.methods import is_mediacms_editor, is_mediacms_manager from files.methods import is_mediacms_editor, is_mediacms_manager

View file

@ -1,4 +1,5 @@
import os import os
from celery.schedules import crontab from celery.schedules import crontab
DEBUG = False DEBUG = False
@ -213,9 +214,7 @@ POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY = ""
CANNOT_ADD_MEDIA_MESSAGE = "" CANNOT_ADD_MEDIA_MESSAGE = ""
# mp4hls command, part of Bendo4 # mp4hls command, part of Bendo4
MP4HLS_COMMAND = ( MP4HLS_COMMAND = "/home/mediacms.io/mediacms/Bento4-SDK-1-6-0-637.x86_64-unknown-linux/bin/mp4hls"
"/home/mediacms.io/mediacms/Bento4-SDK-1-6-0-637.x86_64-unknown-linux/bin/mp4hls"
)
# highly experimental, related with remote workers # highly experimental, related with remote workers
ADMIN_TOKEN = "c2b8e1838b6128asd333ddc5e24" ADMIN_TOKEN = "c2b8e1838b6128asd333ddc5e24"

View file

@ -1,7 +1,7 @@
import debug_toolbar
from django.conf.urls import include, url
from django.contrib import admin from django.contrib import admin
from django.urls import path from django.urls import path
from django.conf.urls import url, include
import debug_toolbar
urlpatterns = [ urlpatterns = [
url(r"^__debug__/", include(debug_toolbar.urls)), url(r"^__debug__/", include(debug_toolbar.urls)),

View file

@ -29,8 +29,6 @@ CACHES = {
BROKER_URL = REDIS_LOCATION BROKER_URL = REDIS_LOCATION
CELERY_RESULT_BACKEND = BROKER_URL CELERY_RESULT_BACKEND = BROKER_URL
MP4HLS_COMMAND = ( MP4HLS_COMMAND = "/home/mediacms.io/bento4/bin/mp4hls"
"/home/mediacms.io/bento4/bin/mp4hls"
)
DEBUG = False DEBUG = False

View file

@ -1,14 +1,14 @@
from django.contrib import admin from django.contrib import admin
from .models import ( from .models import (
Media,
Encoding,
EncodeProfile,
Category, Category,
Comment, Comment,
Tag, EncodeProfile,
Encoding,
Language, Language,
Media,
Subtitle, Subtitle,
Tag,
) )

View file

@ -1,9 +1,9 @@
# ffmpeg only backend # ffmpeg only backend
from subprocess import PIPE, Popen
import locale import locale
import re
import logging import logging
import re
from subprocess import PIPE, Popen
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View file

@ -1,4 +1,5 @@
from django.conf import settings from django.conf import settings
from .methods import is_mediacms_editor, is_mediacms_manager from .methods import is_mediacms_editor, is_mediacms_manager
@ -19,18 +20,12 @@ def stuff(request):
ret["UPLOAD_MAX_SIZE"] = settings.UPLOAD_MAX_SIZE ret["UPLOAD_MAX_SIZE"] = settings.UPLOAD_MAX_SIZE
ret["UPLOAD_MAX_FILES_NUMBER"] = settings.UPLOAD_MAX_FILES_NUMBER ret["UPLOAD_MAX_FILES_NUMBER"] = settings.UPLOAD_MAX_FILES_NUMBER
ret["PRE_UPLOAD_MEDIA_MESSAGE"] = settings.PRE_UPLOAD_MEDIA_MESSAGE ret["PRE_UPLOAD_MEDIA_MESSAGE"] = settings.PRE_UPLOAD_MEDIA_MESSAGE
ret[ ret["POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY"] = settings.POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY
"POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY"
] = settings.POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY
ret["IS_MEDIACMS_ADMIN"] = request.user.is_superuser ret["IS_MEDIACMS_ADMIN"] = request.user.is_superuser
ret["IS_MEDIACMS_EDITOR"] = is_mediacms_editor(request.user) ret["IS_MEDIACMS_EDITOR"] = is_mediacms_editor(request.user)
ret["IS_MEDIACMS_MANAGER"] = is_mediacms_manager(request.user) ret["IS_MEDIACMS_MANAGER"] = is_mediacms_manager(request.user)
ret["ALLOW_RATINGS"] = settings.ALLOW_RATINGS ret["ALLOW_RATINGS"] = settings.ALLOW_RATINGS
ret[ ret["ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY"] = settings.ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY
"ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY" ret["VIDEO_PLAYER_FEATURED_VIDEO_ON_INDEX_PAGE"] = settings.VIDEO_PLAYER_FEATURED_VIDEO_ON_INDEX_PAGE
] = settings.ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY
ret[
"VIDEO_PLAYER_FEATURED_VIDEO_ON_INDEX_PAGE"
] = settings.VIDEO_PLAYER_FEATURED_VIDEO_ON_INDEX_PAGE
ret["RSS_URL"] = "/rss" ret["RSS_URL"] = "/rss"
return ret return ret

View file

@ -1,12 +1,12 @@
from django.contrib.syndication.views import Feed
from django.utils.feedgenerator import Rss201rev2Feed
from django.urls import reverse
from django.db.models import Q
from django.conf import settings from django.conf import settings
from django.contrib.postgres.search import SearchQuery from django.contrib.postgres.search import SearchQuery
from django.contrib.syndication.views import Feed
from django.db.models import Q
from django.urls import reverse
from django.utils.feedgenerator import Rss201rev2Feed
from .models import Media, Category
from . import helpers from . import helpers
from .models import Category, Media
from .stop_words import STOP_WORDS from .stop_words import STOP_WORDS
@ -119,11 +119,7 @@ class SearchRSSFeed(Feed):
elif query: elif query:
# same as on files.views.MediaSearch: move this processing to a prepare_query function # same as on files.views.MediaSearch: move this processing to a prepare_query function
query = helpers.clean_query(query) query = helpers.clean_query(query)
q_parts = [ q_parts = [q_part.rstrip("y") for q_part in query.split() if q_part not in STOP_WORDS]
q_part.rstrip("y")
for q_part in query.split()
if q_part not in STOP_WORDS
]
if q_parts: if q_parts:
query = SearchQuery(q_parts[0] + ":*", search_type="raw") query = SearchQuery(q_parts[0] + ":*", search_type="raw")
for part in q_parts[1:]: for part in q_parts[1:]:

View file

@ -1,6 +1,7 @@
from django import forms from django import forms
from .methods import get_next_state, is_mediacms_editor
from .models import Media, Subtitle from .models import Media, Subtitle
from .methods import is_mediacms_editor, get_next_state
class MultipleSelect(forms.CheckboxSelectMultiple): class MultipleSelect(forms.CheckboxSelectMultiple):
@ -8,9 +9,7 @@ class MultipleSelect(forms.CheckboxSelectMultiple):
class MediaForm(forms.ModelForm): class MediaForm(forms.ModelForm):
new_tags = forms.CharField( new_tags = forms.CharField(label="Tags", help_text="a comma separated list of new tags.", required=False)
label="Tags", help_text="a comma separated list of new tags.", required=False
)
class Meta: class Meta:
model = Media model = Media
@ -27,7 +26,7 @@ class MediaForm(forms.ModelForm):
"thumbnail_time", "thumbnail_time",
"reported_times", "reported_times",
"is_reviewed", "is_reviewed",
"allow_download" "allow_download",
) )
widgets = { widgets = {
"tags": MultipleSelect(), "tags": MultipleSelect(),
@ -42,9 +41,7 @@ class MediaForm(forms.ModelForm):
self.fields.pop("featured") self.fields.pop("featured")
self.fields.pop("reported_times") self.fields.pop("reported_times")
self.fields.pop("is_reviewed") self.fields.pop("is_reviewed")
self.fields["new_tags"].initial = ", ".join( self.fields["new_tags"].initial = ", ".join([tag.title for tag in self.instance.tags.all()])
[tag.title for tag in self.instance.tags.all()]
)
def clean_uploaded_poster(self): def clean_uploaded_poster(self):
image = self.cleaned_data.get("uploaded_poster", False) image = self.cleaned_data.get("uploaded_poster", False)
@ -57,9 +54,7 @@ class MediaForm(forms.ModelForm):
data = self.cleaned_data data = self.cleaned_data
state = data.get("state") state = data.get("state")
if state != self.initial["state"]: if state != self.initial["state"]:
self.instance.state = get_next_state( self.instance.state = get_next_state(self.user, self.initial["state"], self.instance.state)
self.user, self.initial["state"], self.instance.state
)
media = super(MediaForm, self).save(*args, **kwargs) media = super(MediaForm, self).save(*args, **kwargs)
return media return media

View file

@ -1,19 +1,19 @@
# Kudos to Werner Robitza, AVEQ GmbH, for helping with ffmpeg # Kudos to Werner Robitza, AVEQ GmbH, for helping with ffmpeg
# related content # related content
import os
import math
import shutil
import tempfile
import random
import hashlib import hashlib
import subprocess
import json import json
import math
import os
import random
import shutil
import subprocess
import tempfile
from fractions import Fraction from fractions import Fraction
import filetype import filetype
from django.conf import settings from django.conf import settings
CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
CRF_ENCODING_NUM_SECONDS = 2 # 0 * 60 # videos with greater duration will get CRF_ENCODING_NUM_SECONDS = 2 # 0 * 60 # videos with greater duration will get
@ -168,9 +168,7 @@ def rm_dir(directory):
def url_from_path(filename): def url_from_path(filename):
# TODO: find a way to preserver http - https ... # TODO: find a way to preserver http - https ...
return "{0}{1}".format( return "{0}{1}".format(settings.MEDIA_URL, filename.replace(settings.MEDIA_ROOT, ""))
settings.MEDIA_URL, filename.replace(settings.MEDIA_ROOT, "")
)
def create_temp_file(suffix=None, dir=settings.TEMP_DIRECTORY): def create_temp_file(suffix=None, dir=settings.TEMP_DIRECTORY):
@ -210,9 +208,7 @@ def run_command(cmd, cwd=None):
cmd = cmd.split() cmd = cmd.split()
ret = {} ret = {}
if cwd: if cwd:
process = subprocess.Popen( process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd)
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd
)
else: else:
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = process.communicate() stdout, stderr = process.communicate()
@ -331,9 +327,7 @@ def media_file_info(input_file):
except ValueError: except ValueError:
hms, msec = duration_str.split(",") hms, msec = duration_str.split(",")
total_dur = sum( total_dur = sum(int(x) * 60 ** i for i, x in enumerate(reversed(hms.split(":"))))
int(x) * 60 ** i for i, x in enumerate(reversed(hms.split(":")))
)
video_duration = total_dur + float("0." + msec) video_duration = total_dur + float("0." + msec)
else: else:
# fallback to format, eg for webm # fallback to format, eg for webm
@ -370,7 +364,7 @@ def media_file_info(input_file):
input_file, input_file,
] ]
stdout = run_command(cmd).get("out") stdout = run_command(cmd).get("out")
stream_size = sum([int(l) for l in stdout.split("\n") if l != ""]) stream_size = sum([int(line) for line in stdout.split("\n") if line != ""])
video_bitrate = round((stream_size * 8 / 1024.0) / video_duration, 2) video_bitrate = round((stream_size * 8 / 1024.0) / video_duration, 2)
ret = { ret = {
@ -396,9 +390,7 @@ def media_file_info(input_file):
hms, msec = duration_str.split(".") hms, msec = duration_str.split(".")
except ValueError: except ValueError:
hms, msec = duration_str.split(",") hms, msec = duration_str.split(",")
total_dur = sum( total_dur = sum(int(x) * 60 ** i for i, x in enumerate(reversed(hms.split(":"))))
int(x) * 60 ** i for i, x in enumerate(reversed(hms.split(":")))
)
audio_duration = total_dur + float("0." + msec) audio_duration = total_dur + float("0." + msec)
else: else:
# fallback to format, eg for webm # fallback to format, eg for webm
@ -432,7 +424,7 @@ def media_file_info(input_file):
input_file, input_file,
] ]
stdout = run_command(cmd).get("out") stdout = run_command(cmd).get("out")
stream_size = sum([int(l) for l in stdout.split("\n") if l != ""]) stream_size = sum([int(line) for line in stdout.split("\n") if line != ""])
audio_bitrate = round((stream_size * 8 / 1024.0) / audio_duration, 2) audio_bitrate = round((stream_size * 8 / 1024.0) / audio_duration, 2)
ret.update( ret.update(
@ -660,9 +652,7 @@ def get_base_ffmpeg_command(
return cmd return cmd
def produce_ffmpeg_commands( def produce_ffmpeg_commands(media_file, media_info, resolution, codec, output_filename, pass_file, chunk=False):
media_file, media_info, resolution, codec, output_filename, pass_file, chunk=False
):
try: try:
media_info = json.loads(media_info) media_info = json.loads(media_info)
except BaseException: except BaseException:
@ -699,9 +689,7 @@ def produce_ffmpeg_commands(
# else: # else:
# adjust the target frame rate if the input is fractional # adjust the target frame rate if the input is fractional
target_fps = ( target_fps = src_framerate if isinstance(src_framerate, int) else math.ceil(src_framerate)
src_framerate if isinstance(src_framerate, int) else math.ceil(src_framerate)
)
if media_info.get("video_duration") > CRF_ENCODING_NUM_SECONDS: if media_info.get("video_duration") > CRF_ENCODING_NUM_SECONDS:
enc_type = "crf" enc_type = "crf"

View file

@ -1,16 +1,16 @@
from rest_framework.views import APIView
from rest_framework.parsers import JSONParser
from rest_framework.settings import api_settings
from rest_framework.response import Response
from rest_framework import status from rest_framework import status
from rest_framework.parsers import JSONParser
from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.views import APIView
from users.models import User from users.models import User
from users.serializers import UserSerializer from users.serializers import UserSerializer
from .permissions import IsMediacmsEditor
from .models import Media, Comment
from .methods import is_mediacms_manager
from .serializers import MediaSerializer, CommentSerializer from .methods import is_mediacms_manager
from .models import Comment, Media
from .permissions import IsMediacmsEditor
from .serializers import CommentSerializer, MediaSerializer
class MediaList(APIView): class MediaList(APIView):
@ -189,9 +189,7 @@ class UserList(APIView):
def delete(self, request, format=None): def delete(self, request, format=None):
if not is_mediacms_manager(request.user): if not is_mediacms_manager(request.user):
return Response( return Response({"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST)
{"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST
)
tokens = request.GET.get("tokens") tokens = request.GET.get("tokens")
if tokens: if tokens:

View file

@ -1,15 +1,17 @@
# Kudos to Werner Robitza, AVEQ GmbH, for helping with ffmpeg # Kudos to Werner Robitza, AVEQ GmbH, for helping with ffmpeg
# related content # related content
import itertools
import logging import logging
import random import random
import itertools
from datetime import datetime from datetime import datetime
from cms import celery_app
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.db.models import Q
from django.core.mail import EmailMessage from django.core.mail import EmailMessage
from django.db.models import Q
from cms import celery_app
from . import models from . import models
from .helpers import mask_ip from .helpers import mask_ip
@ -48,9 +50,7 @@ def pre_save_action(media, user, session_key, action, remote_ip):
if user: if user:
query = MediaAction.objects.filter(media=media, action=action, user=user) query = MediaAction.objects.filter(media=media, action=action, user=user)
else: else:
query = MediaAction.objects.filter( query = MediaAction.objects.filter(media=media, action=action, session_key=session_key)
media=media, action=action, session_key=session_key
)
query = query.order_by("-action_date") query = query.order_by("-action_date")
if query: if query:
@ -71,11 +71,7 @@ def pre_save_action(media, user, session_key, action, remote_ip):
# perform some checking for requests where no session # perform some checking for requests where no session
# id is specified (and user is anonymous) to avoid spam # id is specified (and user is anonymous) to avoid spam
# eg allow for the same remote_ip for a specific number of actions # eg allow for the same remote_ip for a specific number of actions
query = ( query = MediaAction.objects.filter(media=media, action=action, remote_ip=remote_ip).filter(user=None).order_by("-action_date")
MediaAction.objects.filter(media=media, action=action, remote_ip=remote_ip)
.filter(user=None)
.order_by("-action_date")
)
if query: if query:
query = query.first() query = query.first()
now = datetime.now(query.action_date.tzinfo) now = datetime.now(query.action_date.tzinfo)
@ -204,11 +200,8 @@ URL: %s
d["to"] = [media.user.email] d["to"] = [media.user.email]
notify_items.append(d) notify_items.append(d)
for item in notify_items: for item in notify_items:
email = EmailMessage( email = EmailMessage(item["title"], item["msg"], settings.DEFAULT_FROM_EMAIL, item["to"])
item["title"], item["msg"], settings.DEFAULT_FROM_EMAIL, item["to"]
)
email.send(fail_silently=True) email.send(fail_silently=True)
return True return True
@ -222,17 +215,9 @@ def show_recommended_media(request, limit=100):
pmi = cache.get("popular_media_ids") pmi = cache.get("popular_media_ids")
# produced by task get_list_of_popular_media and cached # produced by task get_list_of_popular_media and cached
if pmi: if pmi:
media = list( media = list(models.Media.objects.filter(friendly_token__in=pmi).filter(basic_query).prefetch_related("user")[:limit])
models.Media.objects.filter(friendly_token__in=pmi)
.filter(basic_query)
.prefetch_related("user")[:limit]
)
else: else:
media = list( media = list(models.Media.objects.filter(basic_query).order_by("-views", "-likes").prefetch_related("user")[:limit])
models.Media.objects.filter(basic_query)
.order_by("-views", "-likes")
.prefetch_related("user")[:limit]
)
random.shuffle(media) random.shuffle(media)
return media return media
@ -257,11 +242,7 @@ def show_related_media_content(media, request, limit):
# and include author videos in any case # and include author videos in any case
q_author = Q(listable=True, user=media.user) q_author = Q(listable=True, user=media.user)
m = list( m = list(models.Media.objects.filter(q_author).order_by().prefetch_related("user")[:limit])
models.Media.objects.filter(q_author)
.order_by()
.prefetch_related("user")[:limit]
)
# order by random criteria so that it doesn't bring the same results # order by random criteria so that it doesn't bring the same results
# attention: only fields that are indexed make sense here! also need # attention: only fields that are indexed make sense here! also need
@ -282,20 +263,12 @@ def show_related_media_content(media, request, limit):
category = media.category.first() category = media.category.first()
if category: if category:
q_category = Q(listable=True, category=category) q_category = Q(listable=True, category=category)
q_res = ( q_res = models.Media.objects.filter(q_category).order_by(order_criteria[random.randint(0, len(order_criteria) - 1)]).prefetch_related("user")[: limit - media.user.media_count]
models.Media.objects.filter(q_category)
.order_by(order_criteria[random.randint(0, len(order_criteria) - 1)])
.prefetch_related("user")[: limit - media.user.media_count]
)
m = list(itertools.chain(m, q_res)) m = list(itertools.chain(m, q_res))
if len(m) < limit: if len(m) < limit:
q_generic = Q(listable=True) q_generic = Q(listable=True)
q_res = ( q_res = models.Media.objects.filter(q_generic).order_by(order_criteria[random.randint(0, len(order_criteria) - 1)]).prefetch_related("user")[: limit - media.user.media_count]
models.Media.objects.filter(q_generic)
.order_by(order_criteria[random.randint(0, len(order_criteria) - 1)])
.prefetch_related("user")[: limit - media.user.media_count]
)
m = list(itertools.chain(m, q_res)) m = list(itertools.chain(m, q_res))
m = list(set(m[:limit])) # remove duplicates m = list(set(m[:limit])) # remove duplicates
@ -313,11 +286,7 @@ def show_related_media_author(media, request, limit):
"""Return a list of related media form the same author""" """Return a list of related media form the same author"""
q_author = Q(listable=True, user=media.user) q_author = Q(listable=True, user=media.user)
m = list( m = list(models.Media.objects.filter(q_author).order_by().prefetch_related("user")[:limit])
models.Media.objects.filter(q_author)
.order_by()
.prefetch_related("user")[:limit]
)
# order by random criteria so that it doesn't bring the same results # order by random criteria so that it doesn't bring the same results
# attention: only fields that are indexed make sense here! also need # attention: only fields that are indexed make sense here! also need
@ -347,13 +316,7 @@ def update_user_ratings(user, media, user_ratings):
"""Populate user ratings for a media""" """Populate user ratings for a media"""
for rating in user_ratings: for rating in user_ratings:
user_rating = ( user_rating = models.Rating.objects.filter(user=user, media_id=media, rating_category_id=rating.get("category_id")).only("score").first()
models.Rating.objects.filter(
user=user, media_id=media, rating_category_id=rating.get("category_id")
)
.only("score")
.first()
)
if user_rating: if user_rating:
rating["score"] = user_rating.score rating["score"] = user_rating.score
return user_ratings return user_ratings
@ -379,9 +342,7 @@ View it on %s
media.title, media.title,
media_url, media_url,
) )
email = EmailMessage( email = EmailMessage(title, msg, settings.DEFAULT_FROM_EMAIL, [media.user.email])
title, msg, settings.DEFAULT_FROM_EMAIL, [media.user.email]
)
email.send(fail_silently=True) email.send(fail_silently=True)
return True return True
@ -420,27 +381,17 @@ def list_tasks():
friendly_token = task_args.split()[0] friendly_token = task_args.split()[0]
profile_id = task_args.split()[1] profile_id = task_args.split()[1]
media = models.Media.objects.filter( media = models.Media.objects.filter(friendly_token=friendly_token).first()
friendly_token=friendly_token
).first()
if media: if media:
profile = models.EncodeProfile.objects.filter( profile = models.EncodeProfile.objects.filter(id=profile_id).first()
id=profile_id
).first()
if profile: if profile:
media_profile_pairs.append( media_profile_pairs.append((media.friendly_token, profile.id))
(media.friendly_token, profile.id)
)
task_dict["info"] = {} task_dict["info"] = {}
task_dict["info"]["profile name"] = profile.name task_dict["info"]["profile name"] = profile.name
task_dict["info"]["media title"] = media.title task_dict["info"]["media title"] = media.title
encoding = models.Encoding.objects.filter( encoding = models.Encoding.objects.filter(task_id=task.get("id")).first()
task_id=task.get("id")
).first()
if encoding: if encoding:
task_dict["info"][ task_dict["info"]["encoding progress"] = encoding.progress
"encoding progress"
] = encoding.progress
ret[state]["tasks"].append(task_dict) ret[state]["tasks"].append(task_dict)
ret["task_ids"] = task_ids ret["task_ids"] = task_ids

View file

@ -1,11 +1,13 @@
# Generated by Django 3.1.4 on 2020-12-01 07:12 # Generated by Django 3.1.4 on 2020-12-01 07:12
import django.contrib.postgres.search
from django.db import migrations, models
import files.models
import imagekit.models.fields
import uuid import uuid
import django.contrib.postgres.search
import imagekit.models.fields
from django.db import migrations, models
import files.models
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -32,9 +34,7 @@ class Migration(migrations.Migration):
("description", models.TextField(blank=True)), ("description", models.TextField(blank=True)),
( (
"is_global", "is_global",
models.BooleanField( models.BooleanField(default=False, help_text="global categories or user specific"),
default=False, help_text="global categories or user specific"
),
), ),
( (
"media_count", "media_count",
@ -42,9 +42,7 @@ class Migration(migrations.Migration):
), ),
( (
"thumbnail", "thumbnail",
imagekit.models.fields.ProcessedImageField( imagekit.models.fields.ProcessedImageField(blank=True, upload_to=files.models.category_thumb_path),
blank=True, upload_to=files.models.category_thumb_path
),
), ),
( (
"listings_thumbnail", "listings_thumbnail",
@ -153,9 +151,7 @@ class Migration(migrations.Migration):
("commands", models.TextField(blank=True, help_text="commands run")), ("commands", models.TextField(blank=True, help_text="commands run")),
( (
"chunk", "chunk",
models.BooleanField( models.BooleanField(db_index=True, default=False, help_text="is chunk?"),
db_index=True, default=False, help_text="is chunk?"
),
), ),
("chunk_file_path", models.CharField(blank=True, max_length=400)), ("chunk_file_path", models.CharField(blank=True, max_length=400)),
("chunks_info", models.TextField(blank=True)), ("chunks_info", models.TextField(blank=True)),
@ -317,9 +313,7 @@ class Migration(migrations.Migration):
("likes", models.IntegerField(db_index=True, default=1)), ("likes", models.IntegerField(db_index=True, default=1)),
( (
"listable", "listable",
models.BooleanField( models.BooleanField(default=False, help_text="Whether it will appear on listings"),
default=False, help_text="Whether it will appear on listings"
),
), ),
( (
"md5sum", "md5sum",
@ -341,9 +335,7 @@ class Migration(migrations.Migration):
), ),
( (
"media_info", "media_info",
models.TextField( models.TextField(blank=True, help_text="extracted media metadata info"),
blank=True, help_text="extracted media metadata info"
),
), ),
( (
"media_type", "media_type",
@ -387,9 +379,7 @@ class Migration(migrations.Migration):
), ),
( (
"reported_times", "reported_times",
models.IntegerField( models.IntegerField(default=0, help_text="how many time a Medis is reported"),
default=0, help_text="how many time a Medis is reported"
),
), ),
( (
"search", "search",
@ -485,9 +475,7 @@ class Migration(migrations.Migration):
), ),
( (
"user_featured", "user_featured",
models.BooleanField( models.BooleanField(default=False, help_text="Featured by the user"),
default=False, help_text="Featured by the user"
),
), ),
("video_height", models.IntegerField(default=1)), ("video_height", models.IntegerField(default=1)),
("views", models.IntegerField(db_index=True, default=1)), ("views", models.IntegerField(db_index=True, default=1)),

View file

@ -1,10 +1,10 @@
# Generated by Django 3.1.4 on 2020-12-01 07:12 # Generated by Django 3.1.4 on 2020-12-01 07:12
from django.conf import settings
import django.contrib.postgres.indexes import django.contrib.postgres.indexes
from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import mptt.fields import mptt.fields
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -31,9 +31,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="subtitle", model_name="subtitle",
name="language", name="language",
field=models.ForeignKey( field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="files.language"),
on_delete=django.db.models.deletion.CASCADE, to="files.language"
),
), ),
migrations.AddField( migrations.AddField(
model_name="subtitle", model_name="subtitle",
@ -47,9 +45,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="subtitle", model_name="subtitle",
name="user", name="user",
field=models.ForeignKey( field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
),
), ),
migrations.AddField( migrations.AddField(
model_name="rating", model_name="rating",
@ -63,37 +59,27 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="rating", model_name="rating",
name="rating_category", name="rating_category",
field=models.ForeignKey( field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="files.ratingcategory"),
on_delete=django.db.models.deletion.CASCADE, to="files.ratingcategory"
),
), ),
migrations.AddField( migrations.AddField(
model_name="rating", model_name="rating",
name="user", name="user",
field=models.ForeignKey( field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
),
), ),
migrations.AddField( migrations.AddField(
model_name="playlistmedia", model_name="playlistmedia",
name="media", name="media",
field=models.ForeignKey( field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="files.media"),
on_delete=django.db.models.deletion.CASCADE, to="files.media"
),
), ),
migrations.AddField( migrations.AddField(
model_name="playlistmedia", model_name="playlistmedia",
name="playlist", name="playlist",
field=models.ForeignKey( field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="files.playlist"),
on_delete=django.db.models.deletion.CASCADE, to="files.playlist"
),
), ),
migrations.AddField( migrations.AddField(
model_name="playlist", model_name="playlist",
name="media", name="media",
field=models.ManyToManyField( field=models.ManyToManyField(blank=True, through="files.PlaylistMedia", to="files.Media"),
blank=True, through="files.PlaylistMedia", to="files.Media"
),
), ),
migrations.AddField( migrations.AddField(
model_name="playlist", model_name="playlist",
@ -173,9 +159,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="encoding", model_name="encoding",
name="profile", name="profile",
field=models.ForeignKey( field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="files.encodeprofile"),
on_delete=django.db.models.deletion.CASCADE, to="files.encodeprofile"
),
), ),
migrations.AddField( migrations.AddField(
model_name="comment", model_name="comment",
@ -200,9 +184,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="comment", model_name="comment",
name="user", name="user",
field=models.ForeignKey( field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
),
), ),
migrations.AddField( migrations.AddField(
model_name="category", model_name="category",
@ -216,9 +198,7 @@ class Migration(migrations.Migration):
), ),
migrations.AddIndex( migrations.AddIndex(
model_name="rating", model_name="rating",
index=models.Index( index=models.Index(fields=["user", "media"], name="files_ratin_user_id_72ca6a_idx"),
fields=["user", "media"], name="files_ratin_user_id_72ca6a_idx"
),
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name="rating", name="rating",
@ -226,8 +206,6 @@ class Migration(migrations.Migration):
), ),
migrations.AddIndex( migrations.AddIndex(
model_name="media", model_name="media",
index=django.contrib.postgres.indexes.GinIndex( index=django.contrib.postgres.indexes.GinIndex(fields=["search"], name="files_media_search_7194c6_gin"),
fields=["search"], name="files_media_search_7194c6_gin"
),
), ),
] ]

View file

@ -1,28 +1,27 @@
import json
import logging import logging
import uuid
import os import os
import random
import re import re
import tempfile import tempfile
import random import uuid
import json
import m3u8 import m3u8
from django.utils import timezone
from django.db import connection
from django.db import models
from django.template.defaultfilters import slugify
from django.conf import settings from django.conf import settings
from django.contrib.postgres.indexes import GinIndex from django.contrib.postgres.indexes import GinIndex
from django.db.models.signals import pre_delete, post_delete, post_save, m2m_changed
from django.core.files import File
from django.core.exceptions import ValidationError
from django.dispatch import receiver
from django.urls import reverse
from django.utils.html import strip_tags
from django.contrib.postgres.search import SearchVectorField from django.contrib.postgres.search import SearchVectorField
from mptt.models import MPTTModel, TreeForeignKey from django.core.exceptions import ValidationError
from django.core.files import File
from imagekit.processors import ResizeToFit from django.db import connection, models
from django.db.models.signals import m2m_changed, post_delete, post_save, pre_delete
from django.dispatch import receiver
from django.template.defaultfilters import slugify
from django.urls import reverse
from django.utils import timezone
from django.utils.html import strip_tags
from imagekit.models import ProcessedImageField from imagekit.models import ProcessedImageField
from imagekit.processors import ResizeToFit
from mptt.models import MPTTModel, TreeForeignKey
from . import helpers from . import helpers
from .methods import notify_users from .methods import notify_users
@ -88,36 +87,26 @@ ENCODE_RESOLUTIONS_KEYS = [resolution for resolution, name in ENCODE_RESOLUTIONS
def original_media_file_path(instance, filename): def original_media_file_path(instance, filename):
"""Helper function to place original media file""" """Helper function to place original media file"""
file_name = "{0}.{1}".format(instance.uid.hex, helpers.get_file_name(filename)) file_name = "{0}.{1}".format(instance.uid.hex, helpers.get_file_name(filename))
return settings.MEDIA_UPLOAD_DIR + "user/{0}/{1}".format( return settings.MEDIA_UPLOAD_DIR + "user/{0}/{1}".format(instance.user.username, file_name)
instance.user.username, file_name
)
def encoding_media_file_path(instance, filename): def encoding_media_file_path(instance, filename):
"""Helper function to place encoded media file""" """Helper function to place encoded media file"""
file_name = "{0}.{1}".format( file_name = "{0}.{1}".format(instance.media.uid.hex, helpers.get_file_name(filename))
instance.media.uid.hex, helpers.get_file_name(filename) return settings.MEDIA_ENCODING_DIR + "{0}/{1}/{2}".format(instance.profile.id, instance.media.user.username, file_name)
)
return settings.MEDIA_ENCODING_DIR + "{0}/{1}/{2}".format(
instance.profile.id, instance.media.user.username, file_name
)
def original_thumbnail_file_path(instance, filename): def original_thumbnail_file_path(instance, filename):
"""Helper function to place original media thumbnail file""" """Helper function to place original media thumbnail file"""
return settings.THUMBNAIL_UPLOAD_DIR + "user/{0}/{1}".format( return settings.THUMBNAIL_UPLOAD_DIR + "user/{0}/{1}".format(instance.user.username, filename)
instance.user.username, filename
)
def subtitles_file_path(instance, filename): def subtitles_file_path(instance, filename):
"""Helper function to place subtitle file""" """Helper function to place subtitle file"""
return settings.SUBTITLES_UPLOAD_DIR + "user/{0}/{1}".format( return settings.SUBTITLES_UPLOAD_DIR + "user/{0}/{1}".format(instance.media.user.username, filename)
instance.media.user.username, filename
)
def category_thumb_path(instance, filename): def category_thumb_path(instance, filename):
@ -130,17 +119,11 @@ def category_thumb_path(instance, filename):
class Media(models.Model): class Media(models.Model):
"""The most important model for MediaCMS""" """The most important model for MediaCMS"""
add_date = models.DateTimeField( add_date = models.DateTimeField("Date produced", blank=True, null=True, db_index=True)
"Date produced", blank=True, null=True, db_index=True
)
allow_download = models.BooleanField( allow_download = models.BooleanField(default=True, help_text="Whether option to download media is shown")
default=True, help_text="Whether option to download media is shown"
)
category = models.ManyToManyField( category = models.ManyToManyField("Category", blank=True, help_text="Media can be part of one or more categories")
"Category", blank=True, help_text="Media can be part of one or more categories"
)
channel = models.ForeignKey( channel = models.ForeignKey(
"users.Channel", "users.Channel",
@ -158,13 +141,9 @@ class Media(models.Model):
edit_date = models.DateTimeField(auto_now=True) edit_date = models.DateTimeField(auto_now=True)
enable_comments = models.BooleanField( enable_comments = models.BooleanField(default=True, help_text="Whether comments will be allowed for this media")
default=True, help_text="Whether comments will be allowed for this media"
)
encoding_status = models.CharField( encoding_status = models.CharField(max_length=20, choices=MEDIA_ENCODING_STATUS, default="pending", db_index=True)
max_length=20, choices=MEDIA_ENCODING_STATUS, default="pending", db_index=True
)
featured = models.BooleanField( featured = models.BooleanField(
default=False, default=False,
@ -172,13 +151,9 @@ class Media(models.Model):
help_text="Whether media is globally featured by a MediaCMS editor", help_text="Whether media is globally featured by a MediaCMS editor",
) )
friendly_token = models.CharField( friendly_token = models.CharField(blank=True, max_length=12, db_index=True, help_text="Identifier for the Media")
blank=True, max_length=12, db_index=True, help_text="Identifier for the Media"
)
hls_file = models.CharField( hls_file = models.CharField(max_length=1000, blank=True, help_text="Path to HLS file for videos")
max_length=1000, blank=True, help_text="Path to HLS file for videos"
)
is_reviewed = models.BooleanField( is_reviewed = models.BooleanField(
default=settings.MEDIA_IS_REVIEWED, default=settings.MEDIA_IS_REVIEWED,
@ -186,19 +161,13 @@ class Media(models.Model):
help_text="Whether media is reviewed, so it can appear on public listings", help_text="Whether media is reviewed, so it can appear on public listings",
) )
license = models.ForeignKey( license = models.ForeignKey("License", on_delete=models.CASCADE, db_index=True, blank=True, null=True)
"License", on_delete=models.CASCADE, db_index=True, blank=True, null=True
)
likes = models.IntegerField(db_index=True, default=1) likes = models.IntegerField(db_index=True, default=1)
listable = models.BooleanField( listable = models.BooleanField(default=False, help_text="Whether it will appear on listings")
default=False, help_text="Whether it will appear on listings"
)
md5sum = models.CharField( md5sum = models.CharField(max_length=50, blank=True, null=True, help_text="Not exposed, used internally")
max_length=50, blank=True, null=True, help_text="Not exposed, used internally"
)
media_file = models.FileField( media_file = models.FileField(
"media file", "media file",
@ -217,9 +186,7 @@ class Media(models.Model):
default="video", default="video",
) )
password = models.CharField( password = models.CharField(max_length=100, blank=True, help_text="password for private media")
max_length=100, blank=True, help_text="password for private media"
)
preview_file_path = models.CharField( preview_file_path = models.CharField(
max_length=500, max_length=500,
@ -243,9 +210,7 @@ class Media(models.Model):
help_text="Rating category, if media Rating is allowed", help_text="Rating category, if media Rating is allowed",
) )
reported_times = models.IntegerField( reported_times = models.IntegerField(default=0, help_text="how many time a Medis is reported")
default=0, help_text="how many time a Medis is reported"
)
search = SearchVectorField( search = SearchVectorField(
null=True, null=True,
@ -274,13 +239,9 @@ class Media(models.Model):
help_text="state of Media", help_text="state of Media",
) )
tags = models.ManyToManyField( tags = models.ManyToManyField("Tag", blank=True, help_text="select one or more out of the existing tags")
"Tag", blank=True, help_text="select one or more out of the existing tags"
)
title = models.CharField( title = models.CharField(max_length=100, help_text="media title", blank=True, db_index=True)
max_length=100, help_text="media title", blank=True, db_index=True
)
thumbnail = ProcessedImageField( thumbnail = ProcessedImageField(
upload_to=original_thumbnail_file_path, upload_to=original_thumbnail_file_path,
@ -292,13 +253,9 @@ class Media(models.Model):
help_text="media extracted small thumbnail, shown on listings", help_text="media extracted small thumbnail, shown on listings",
) )
thumbnail_time = models.FloatField( thumbnail_time = models.FloatField(blank=True, null=True, help_text="Time on video that a thumbnail will be taken")
blank=True, null=True, help_text="Time on video that a thumbnail will be taken"
)
uid = models.UUIDField( uid = models.UUIDField(unique=True, default=uuid.uuid4, help_text="A unique identifier for the Media")
unique=True, default=uuid.uuid4, help_text="A unique identifier for the Media"
)
uploaded_thumbnail = ProcessedImageField( uploaded_thumbnail = ProcessedImageField(
upload_to=original_thumbnail_file_path, upload_to=original_thumbnail_file_path,
@ -321,9 +278,7 @@ class Media(models.Model):
max_length=500, max_length=500,
) )
user = models.ForeignKey( user = models.ForeignKey("users.User", on_delete=models.CASCADE, help_text="user that uploads the media")
"users.User", on_delete=models.CASCADE, help_text="user that uploads the media"
)
user_featured = models.BooleanField(default=False, help_text="Featured by the user") user_featured = models.BooleanField(default=False, help_text="Featured by the user")
@ -406,11 +361,7 @@ class Media(models.Model):
self.state = helpers.get_default_state(user=self.user) self.state = helpers.get_default_state(user=self.user)
# condition to appear on listings # condition to appear on listings
if ( if self.state == "public" and self.encoding_status == "success" and self.is_reviewed is True:
self.state == "public"
and self.encoding_status == "success"
and self.is_reviewed == True
):
self.listable = True self.listable = True
else: else:
self.listable = False self.listable = False
@ -419,10 +370,7 @@ class Media(models.Model):
# produce a thumbnail out of an uploaded poster # produce a thumbnail out of an uploaded poster
# will run only when a poster is uploaded for the first time # will run only when a poster is uploaded for the first time
if ( if self.uploaded_poster and self.uploaded_poster != self.__original_uploaded_poster:
self.uploaded_poster
and self.uploaded_poster != self.__original_uploaded_poster
):
with open(self.uploaded_poster.path, "rb") as f: with open(self.uploaded_poster.path, "rb") as f:
# set this otherwise gets to infinite loop # set this otherwise gets to infinite loop
@ -458,9 +406,7 @@ class Media(models.Model):
] ]
items = [item for item in items if item] items = [item for item in items if item]
text = " ".join(items) text = " ".join(items)
text = " ".join( text = " ".join([token for token in text.lower().split(" ") if token not in STOP_WORDS])
[token for token in text.lower().split(" ") if token not in STOP_WORDS]
)
sql_code = """ sql_code = """
UPDATE {db_table} SET search = to_tsvector( UPDATE {db_table} SET search = to_tsvector(
@ -561,9 +507,7 @@ class Media(models.Model):
if self.media_type == "image": if self.media_type == "image":
with open(self.media_file.path, "rb") as f: with open(self.media_file.path, "rb") as f:
myfile = File(f) myfile = File(f)
thumbnail_name = ( thumbnail_name = helpers.get_file_name(self.media_file.path) + ".jpg"
helpers.get_file_name(self.media_file.path) + ".jpg"
)
self.thumbnail.save(content=myfile, name=thumbnail_name) self.thumbnail.save(content=myfile, name=thumbnail_name)
self.poster.save(content=myfile, name=thumbnail_name) self.poster.save(content=myfile, name=thumbnail_name)
return True return True
@ -585,9 +529,7 @@ class Media(models.Model):
command = [ command = [
settings.FFMPEG_COMMAND, settings.FFMPEG_COMMAND,
"-ss", "-ss",
str( str(thumbnail_time), # -ss need to be firt here otherwise time taken is huge
thumbnail_time
), # -ss need to be firt here otherwise time taken is huge
"-i", "-i",
self.media_file.path, self.media_file.path,
"-vframes", "-vframes",
@ -650,10 +592,7 @@ class Media(models.Model):
for profile in profiles: for profile in profiles:
if profile.extension != "gif": if profile.extension != "gif":
if self.video_height and self.video_height < profile.resolution: if self.video_height and self.video_height < profile.resolution:
if ( if profile.resolution not in settings.MINIMUM_RESOLUTIONS_TO_ENCODE:
profile.resolution
not in settings.MINIMUM_RESOLUTIONS_TO_ENCODE
):
continue continue
encoding = Encoding(media=self, profile=profile) encoding = Encoding(media=self, profile=profile)
encoding.save() encoding.save()
@ -688,12 +627,7 @@ class Media(models.Model):
self.save(update_fields=["encoding_status", "listable"]) self.save(update_fields=["encoding_status", "listable"])
if ( if encoding and encoding.status == "success" and encoding.profile.codec == "h264" and action == "add":
encoding
and encoding.status == "success"
and encoding.profile.codec == "h264"
and action == "add"
):
from . import tasks from . import tasks
tasks.create_hls(self.friendly_token) tasks.create_hls(self.friendly_token)
@ -704,10 +638,7 @@ class Media(models.Model):
"""Set encoding_status for videos """Set encoding_status for videos
Set success if at least one mp4 exists Set success if at least one mp4 exists
""" """
mp4_statuses = set( mp4_statuses = set(encoding.status for encoding in self.encodings.filter(profile__extension="mp4", chunk=False))
encoding.status
for encoding in self.encodings.filter(profile__extension="mp4", chunk=False)
)
if not mp4_statuses: if not mp4_statuses:
encoding_status = "pending" encoding_status = "pending"
@ -752,12 +683,8 @@ class Media(models.Model):
extra.append(encoding.profile.codec) extra.append(encoding.profile.codec)
for codec in extra: for codec in extra:
ret[resolution][codec] = {} ret[resolution][codec] = {}
v = self.encodings.filter(chunk=True, profile__codec=codec).values( v = self.encodings.filter(chunk=True, profile__codec=codec).values("progress")
"progress" ret[resolution][codec]["progress"] = sum([p["progress"] for p in v]) / v.count()
)
ret[resolution][codec]["progress"] = (
sum([p["progress"] for p in v]) / v.count()
)
# TODO; status/logs/errors # TODO; status/logs/errors
return ret return ret
@ -897,19 +824,13 @@ class Media(models.Model):
for iframe_playlist in m3u8_obj.iframe_playlists: for iframe_playlist in m3u8_obj.iframe_playlists:
uri = os.path.join(p, iframe_playlist.uri) uri = os.path.join(p, iframe_playlist.uri)
if os.path.exists(uri): if os.path.exists(uri):
resolution = iframe_playlist.iframe_stream_info.resolution[ resolution = iframe_playlist.iframe_stream_info.resolution[1]
1 res["{}_iframe".format(resolution)] = helpers.url_from_path(uri)
]
res["{}_iframe".format(resolution)] = helpers.url_from_path(
uri
)
for playlist in m3u8_obj.playlists: for playlist in m3u8_obj.playlists:
uri = os.path.join(p, playlist.uri) uri = os.path.join(p, playlist.uri)
if os.path.exists(uri): if os.path.exists(uri):
resolution = playlist.stream_info.resolution[1] resolution = playlist.stream_info.resolution[1]
res[ res["{}_playlist".format(resolution)] = helpers.url_from_path(uri)
"{}_playlist".format(resolution)
] = helpers.url_from_path(uri)
return res return res
@property @property
@ -930,9 +851,7 @@ class Media(models.Model):
if edit: if edit:
return reverse("edit_media") + "?m={0}".format(self.friendly_token) return reverse("edit_media") + "?m={0}".format(self.friendly_token)
if api: if api:
return reverse( return reverse("api_get_media", kwargs={"friendly_token": self.friendly_token})
"api_get_media", kwargs={"friendly_token": self.friendly_token}
)
else: else:
return reverse("get_media") + "?m={0}".format(self.friendly_token) return reverse("get_media") + "?m={0}".format(self.friendly_token)
@ -988,13 +907,9 @@ class Category(models.Model):
description = models.TextField(blank=True) description = models.TextField(blank=True)
user = models.ForeignKey( user = models.ForeignKey("users.User", on_delete=models.CASCADE, blank=True, null=True)
"users.User", on_delete=models.CASCADE, blank=True, null=True
)
is_global = models.BooleanField( is_global = models.BooleanField(default=False, help_text="global categories or user specific")
default=False, help_text="global categories or user specific"
)
media_count = models.IntegerField(default=0, help_text="number of media") media_count = models.IntegerField(default=0, help_text="number of media")
@ -1006,9 +921,7 @@ class Category(models.Model):
blank=True, blank=True,
) )
listings_thumbnail = models.CharField( listings_thumbnail = models.CharField(max_length=400, blank=True, null=True, help_text="Thumbnail to show on listings")
max_length=400, blank=True, null=True, help_text="Thumbnail to show on listings"
)
def __str__(self): def __str__(self):
return self.title return self.title
@ -1039,11 +952,7 @@ class Category(models.Model):
if self.thumbnail: if self.thumbnail:
return helpers.url_from_path(self.thumbnail.path) return helpers.url_from_path(self.thumbnail.path)
media = ( media = Media.objects.filter(category=self, state="public").order_by("-views").first()
Media.objects.filter(category=self, state="public")
.order_by("-views")
.first()
)
if media: if media:
return media.thumbnail_url return media.thumbnail_url
@ -1061,9 +970,7 @@ class Tag(models.Model):
title = models.CharField(max_length=100, unique=True, db_index=True) title = models.CharField(max_length=100, unique=True, db_index=True)
user = models.ForeignKey( user = models.ForeignKey("users.User", on_delete=models.CASCADE, blank=True, null=True)
"users.User", on_delete=models.CASCADE, blank=True, null=True
)
media_count = models.IntegerField(default=0, help_text="number of media") media_count = models.IntegerField(default=0, help_text="number of media")
@ -1085,9 +992,7 @@ class Tag(models.Model):
return reverse("search") + "?t={0}".format(self.title) return reverse("search") + "?t={0}".format(self.title)
def update_tag_media(self): def update_tag_media(self):
self.media_count = Media.objects.filter( self.media_count = Media.objects.filter(state="public", is_reviewed=True, tags=self).count()
state="public", is_reviewed=True, tags=self
).count()
self.save(update_fields=["media_count"]) self.save(update_fields=["media_count"])
return True return True
@ -1102,9 +1007,7 @@ class Tag(models.Model):
def thumbnail_url(self): def thumbnail_url(self):
if self.listings_thumbnail: if self.listings_thumbnail:
return self.listings_thumbnail return self.listings_thumbnail
media = ( media = Media.objects.filter(tags=self, state="public").order_by("-views").first()
Media.objects.filter(tags=self, state="public").order_by("-views").first()
)
if media: if media:
return media.thumbnail_url return media.thumbnail_url
@ -1154,9 +1057,7 @@ class Encoding(models.Model):
media = models.ForeignKey(Media, on_delete=models.CASCADE, related_name="encodings") media = models.ForeignKey(Media, on_delete=models.CASCADE, related_name="encodings")
media_file = models.FileField( media_file = models.FileField("encoding file", upload_to=encoding_media_file_path, blank=True, max_length=500)
"encoding file", upload_to=encoding_media_file_path, blank=True, max_length=500
)
profile = models.ForeignKey(EncodeProfile, on_delete=models.CASCADE) profile = models.ForeignKey(EncodeProfile, on_delete=models.CASCADE)
@ -1168,9 +1069,7 @@ class Encoding(models.Model):
size = models.CharField(max_length=20, blank=True) size = models.CharField(max_length=20, blank=True)
status = models.CharField( status = models.CharField(max_length=20, choices=MEDIA_ENCODING_STATUS, default="pending")
max_length=20, choices=MEDIA_ENCODING_STATUS, default="pending"
)
temp_file = models.CharField(max_length=400, blank=True) temp_file = models.CharField(max_length=400, blank=True)
@ -1305,9 +1204,7 @@ class Rating(models.Model):
unique_together = ("user", "media", "rating_category") unique_together = ("user", "media", "rating_category")
def __str__(self): def __str__(self):
return "{0}, rate for {1} for category {2}".format( return "{0}, rate for {1} for category {2}".format(self.user.username, self.media.title, self.rating_category.title)
self.user.username, self.media.title, self.rating_category.title
)
class Playlist(models.Model): class Playlist(models.Model):
@ -1325,9 +1222,7 @@ class Playlist(models.Model):
uid = models.UUIDField(unique=True, default=uuid.uuid4) uid = models.UUIDField(unique=True, default=uuid.uuid4)
user = models.ForeignKey( user = models.ForeignKey("users.User", on_delete=models.CASCADE, db_index=True, related_name="playlists")
"users.User", on_delete=models.CASCADE, db_index=True, related_name="playlists"
)
def __str__(self): def __str__(self):
return self.title return self.title
@ -1338,13 +1233,9 @@ class Playlist(models.Model):
def get_absolute_url(self, api=False): def get_absolute_url(self, api=False):
if api: if api:
return reverse( return reverse("api_get_playlist", kwargs={"friendly_token": self.friendly_token})
"api_get_playlist", kwargs={"friendly_token": self.friendly_token}
)
else: else:
return reverse( return reverse("get_playlist", kwargs={"friendly_token": self.friendly_token})
"get_playlist", kwargs={"friendly_token": self.friendly_token}
)
@property @property
def url(self): def url(self):
@ -1411,13 +1302,9 @@ class Comment(MPTTModel):
add_date = models.DateTimeField(auto_now_add=True) add_date = models.DateTimeField(auto_now_add=True)
media = models.ForeignKey( media = models.ForeignKey(Media, on_delete=models.CASCADE, db_index=True, related_name="comments")
Media, on_delete=models.CASCADE, db_index=True, related_name="comments"
)
parent = TreeForeignKey( parent = TreeForeignKey("self", on_delete=models.CASCADE, null=True, blank=True, related_name="children")
"self", on_delete=models.CASCADE, null=True, blank=True, related_name="children"
)
text = models.TextField(help_text="text") text = models.TextField(help_text="text")
@ -1566,13 +1453,9 @@ def encoding_file_save(sender, instance, created, **kwargs):
# concatenate chunks and create final encoding file # concatenate chunks and create final encoding file
chunks_paths = [f.media_file.path for f in chunks] chunks_paths = [f.media_file.path for f in chunks]
with tempfile.TemporaryDirectory( with tempfile.TemporaryDirectory(dir=settings.TEMP_DIRECTORY) as temp_dir:
dir=settings.TEMP_DIRECTORY
) as temp_dir:
seg_file = helpers.create_temp_file(suffix=".txt", dir=temp_dir) seg_file = helpers.create_temp_file(suffix=".txt", dir=temp_dir)
tf = helpers.create_temp_file( tf = helpers.create_temp_file(suffix=".{0}".format(instance.profile.extension), dir=temp_dir)
suffix=".{0}".format(instance.profile.extension), dir=temp_dir
)
with open(seg_file, "w") as ff: with open(seg_file, "w") as ff:
for f in chunks_paths: for f in chunks_paths:
ff.write("file {}\n".format(f)) ff.write("file {}\n".format(f))
@ -1602,9 +1485,7 @@ def encoding_file_save(sender, instance, created, **kwargs):
progress=100, progress=100,
) )
all_logs = "\n".join([st.logs for st in chunks]) all_logs = "\n".join([st.logs for st in chunks])
encoding.logs = "{0}\n{1}\n{2}".format( encoding.logs = "{0}\n{1}\n{2}".format(chunks_paths, stdout, all_logs)
chunks_paths, stdout, all_logs
)
workers = list(set([st.worker for st in chunks])) workers = list(set([st.worker for st in chunks]))
encoding.worker = json.dumps({"workers": workers}) encoding.worker = json.dumps({"workers": workers})
@ -1635,9 +1516,7 @@ def encoding_file_save(sender, instance, created, **kwargs):
): ):
# if two chunks are finished at the same time, this # if two chunks are finished at the same time, this
# will be changed # will be changed
who = Encoding.objects.filter( who = Encoding.objects.filter(media=encoding.media, profile=encoding.profile).exclude(id=encoding.id)
media=encoding.media, profile=encoding.profile
).exclude(id=encoding.id)
who.delete() who.delete()
else: else:
encoding.delete() encoding.delete()
@ -1652,13 +1531,9 @@ def encoding_file_save(sender, instance, created, **kwargs):
instance.media.post_encode_actions(encoding=instance, action="add") instance.media.post_encode_actions(encoding=instance, action="add")
elif instance.chunk and instance.status == "fail": elif instance.chunk and instance.status == "fail":
encoding = Encoding( encoding = Encoding(media=instance.media, profile=instance.profile, status="fail", progress=100)
media=instance.media, profile=instance.profile, status="fail", progress=100
)
chunks = Encoding.objects.filter( chunks = Encoding.objects.filter(media=instance.media, chunks_info=instance.chunks_info, chunk=True).order_by("add_date")
media=instance.media, chunks_info=instance.chunks_info, chunk=True
).order_by("add_date")
chunks_paths = [f.media_file.path for f in chunks] chunks_paths = [f.media_file.path for f in chunks]
@ -1671,9 +1546,7 @@ def encoding_file_save(sender, instance, created, **kwargs):
encoding.total_run_time = (end_date - start_date).seconds encoding.total_run_time = (end_date - start_date).seconds
encoding.save() encoding.save()
who = Encoding.objects.filter( who = Encoding.objects.filter(media=encoding.media, profile=encoding.profile).exclude(id=encoding.id)
media=encoding.media, profile=encoding.profile
).exclude(id=encoding.id)
who.delete() who.delete()
pass # TODO: merge with above if, do not repeat code pass # TODO: merge with above if, do not repeat code
@ -1681,22 +1554,10 @@ def encoding_file_save(sender, instance, created, **kwargs):
if instance.status in ["fail", "success"]: if instance.status in ["fail", "success"]:
instance.media.post_encode_actions(encoding=instance, action="add") instance.media.post_encode_actions(encoding=instance, action="add")
encodings = set( encodings = set([encoding.status for encoding in Encoding.objects.filter(media=instance.media)])
[
encoding.status
for encoding in Encoding.objects.filter(media=instance.media)
]
)
if ("running" in encodings) or ("pending" in encodings): if ("running" in encodings) or ("pending" in encodings):
return return
workers = list( workers = list(set([encoding.worker for encoding in Encoding.objects.filter(media=instance.media)]))
set(
[
encoding.worker
for encoding in Encoding.objects.filter(media=instance.media)
]
)
)
@receiver(post_delete, sender=Encoding) @receiver(post_delete, sender=Encoding)
@ -1712,4 +1573,3 @@ def encoding_file_delete(sender, instance, **kwargs):
instance.media.post_encode_actions(encoding=instance, action="delete") instance.media.post_encode_actions(encoding=instance, action="delete")
# delete local chunks, and remote chunks + media file. Only when the # delete local chunks, and remote chunks + media file. Only when the
# last encoding of a media is complete # last encoding of a media is complete

View file

@ -1,4 +1,5 @@
from rest_framework import permissions from rest_framework import permissions
from .methods import is_mediacms_editor from .methods import is_mediacms_editor

View file

@ -1,6 +1,6 @@
from rest_framework import serializers from rest_framework import serializers
from .models import Media, EncodeProfile, Playlist, Comment, Category, Tag from .models import Category, Comment, EncodeProfile, Media, Playlist, Tag
# TODO: put them in a more DRY way # TODO: put them in a more DRY way
@ -18,9 +18,7 @@ class MediaSerializer(serializers.ModelSerializer):
return self.context["request"].build_absolute_uri(obj.get_absolute_url()) return self.context["request"].build_absolute_uri(obj.get_absolute_url())
def get_api_url(self, obj): def get_api_url(self, obj):
return self.context["request"].build_absolute_uri( return self.context["request"].build_absolute_uri(obj.get_absolute_url(api=True))
obj.get_absolute_url(api=True)
)
def get_thumbnail_url(self, obj): def get_thumbnail_url(self, obj):
if obj.thumbnail_url: if obj.thumbnail_url:
@ -210,16 +208,7 @@ class PlaylistSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Playlist model = Playlist
read_only_fields = ("add_date", "user") read_only_fields = ("add_date", "user")
fields = ( fields = ("add_date", "title", "description", "user", "media_count", "url", "api_url", "thumbnail_url")
"add_date",
"title",
"description",
"user",
"media_count",
"url",
"api_url",
"thumbnail_url"
)
class PlaylistDetailSerializer(serializers.ModelSerializer): class PlaylistDetailSerializer(serializers.ModelSerializer):
@ -228,16 +217,7 @@ class PlaylistDetailSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Playlist model = Playlist
read_only_fields = ("add_date", "user") read_only_fields = ("add_date", "user")
fields = ( fields = ("title", "add_date", "user_thumbnail_url", "description", "user", "media_count", "url", "thumbnail_url")
"title",
"add_date",
"user_thumbnail_url",
"description",
"user",
"media_count",
"url",
"thumbnail_url"
)
class CommentSerializer(serializers.ModelSerializer): class CommentSerializer(serializers.ModelSerializer):

View file

@ -1,41 +1,40 @@
import re
import os
import json import json
import subprocess import os
from datetime import datetime, timedelta import re
import tempfile
import shutil import shutil
from django.core.cache import cache import subprocess
from django.conf import settings import tempfile
from django.core.files import File from datetime import datetime, timedelta
from django.db.models import Q
from celery import Task from celery import Task
from celery.decorators import task from celery.decorators import task
from celery.utils.log import get_task_logger
from celery.exceptions import SoftTimeLimitExceeded from celery.exceptions import SoftTimeLimitExceeded
from celery.task.control import revoke
from celery.signals import task_revoked from celery.signals import task_revoked
from celery.task.control import revoke
from celery.utils.log import get_task_logger
from django.conf import settings
from django.core.cache import cache
from django.core.files import File
from django.db.models import Q
from actions.models import USER_MEDIA_ACTIONS, MediaAction
from users.models import User
from .backends import FFmpegBackend from .backends import FFmpegBackend
from .exceptions import VideoEncodingError from .exceptions import VideoEncodingError
from .helpers import ( from .helpers import (
calculate_seconds, calculate_seconds,
rm_file,
create_temp_file, create_temp_file,
get_file_name, get_file_name,
get_file_type, get_file_type,
media_file_info, media_file_info,
run_command,
produce_ffmpeg_commands, produce_ffmpeg_commands,
produce_friendly_token, produce_friendly_token,
rm_file,
run_command,
) )
from .methods import list_tasks, notify_users, pre_save_action
from actions.models import MediaAction, USER_MEDIA_ACTIONS from .models import Category, EncodeProfile, Encoding, Media, Rating, Tag
from users.models import User
from .models import Encoding, EncodeProfile, Media, Category, Rating, Tag
from .methods import list_tasks, pre_save_action, notify_users
logger = get_task_logger(__name__) logger = get_task_logger(__name__)
@ -83,10 +82,7 @@ def chunkize_media(self, friendly_token, profiles, force=True):
chunks.append(ch[0]) chunks.append(ch[0])
if not chunks: if not chunks:
# command completely failed to segment file.putting to normal encode # command completely failed to segment file.putting to normal encode
logger.info( logger.info("Failed to break file {0} in chunks." " Putting to normal encode queue".format(friendly_token))
"Failed to break file {0} in chunks."
" Putting to normal encode queue".format(friendly_token)
)
for profile in profiles: for profile in profiles:
if media.video_height and media.video_height < profile.resolution: if media.video_height and media.video_height < profile.resolution:
if profile.resolution not in settings.MINIMUM_RESOLUTIONS_TO_ENCODE: if profile.resolution not in settings.MINIMUM_RESOLUTIONS_TO_ENCODE:
@ -94,9 +90,7 @@ def chunkize_media(self, friendly_token, profiles, force=True):
encoding = Encoding(media=media, profile=profile) encoding = Encoding(media=media, profile=profile)
encoding.save() encoding.save()
enc_url = settings.SSL_FRONTEND_HOST + encoding.get_absolute_url() enc_url = settings.SSL_FRONTEND_HOST + encoding.get_absolute_url()
encode_media.delay( encode_media.delay(friendly_token, profile.id, encoding.id, enc_url, force=force)
friendly_token, profile.id, encoding.id, enc_url, force=force
)
return False return False
chunks = [os.path.join(cwd, ch) for ch in chunks] chunks = [os.path.join(cwd, ch) for ch in chunks]
@ -137,11 +131,7 @@ def chunkize_media(self, friendly_token, profiles, force=True):
priority=priority, priority=priority,
) )
logger.info( logger.info("got {0} chunks and will encode to {1} profiles".format(len(chunks), to_profiles))
"got {0} chunks and will encode to {1} profiles".format(
len(chunks), to_profiles
)
)
return True return True
@ -180,11 +170,7 @@ def encode_media(
): ):
"""Encode a media to given profile, using ffmpeg, storing progress""" """Encode a media to given profile, using ffmpeg, storing progress"""
logger.info( logger.info("Encode Media started, friendly token {0}, profile id {1}, force {2}".format(friendly_token, profile_id, force))
"Encode Media started, friendly token {0}, profile id {1}, force {2}".format(
friendly_token, profile_id, force
)
)
if self.request.id: if self.request.id:
task_id = self.request.id task_id = self.request.id
@ -202,13 +188,7 @@ def encode_media(
# TODO: in case a video is chunkized and this enters here many times # TODO: in case a video is chunkized and this enters here many times
# it will always run since chunk_file_path is always different # it will always run since chunk_file_path is always different
# thus find a better way for this check # thus find a better way for this check
if ( if Encoding.objects.filter(media=media, profile=profile, chunk_file_path=chunk_file_path).count() > 1 and force is False:
Encoding.objects.filter(
media=media, profile=profile, chunk_file_path=chunk_file_path
).count()
> 1
and force == False
):
Encoding.objects.filter(id=encoding_id).delete() Encoding.objects.filter(id=encoding_id).delete()
return False return False
else: else:
@ -230,19 +210,14 @@ def encode_media(
chunk_file_path=chunk_file_path, chunk_file_path=chunk_file_path,
) )
else: else:
if ( if Encoding.objects.filter(media=media, profile=profile).count() > 1 and force is False:
Encoding.objects.filter(media=media, profile=profile).count() > 1
and force is False
):
Encoding.objects.filter(id=encoding_id).delete() Encoding.objects.filter(id=encoding_id).delete()
return False return False
else: else:
try: try:
encoding = Encoding.objects.get(id=encoding_id) encoding = Encoding.objects.get(id=encoding_id)
encoding.status = "running" encoding.status = "running"
Encoding.objects.filter(media=media, profile=profile).exclude( Encoding.objects.filter(media=media, profile=profile).exclude(id=encoding_id).delete()
id=encoding_id
).delete()
except BaseException: except BaseException:
encoding = Encoding(media=media, profile=profile, status="running") encoding = Encoding(media=media, profile=profile, status="running")
@ -287,7 +262,7 @@ def encode_media(
else: else:
original_media_path = media.media_file.path original_media_path = media.media_file.path
#if not media.duration: # if not media.duration:
# encoding.status = "fail" # encoding.status = "fail"
# encoding.save(update_fields=["status"]) # encoding.save(update_fields=["status"])
# return False # return False
@ -337,9 +312,7 @@ def encode_media(
if n_times % 60 == 0: if n_times % 60 == 0:
encoding.progress = percent encoding.progress = percent
try: try:
encoding.save( encoding.save(update_fields=["progress", "update_date"])
update_fields=["progress", "update_date"]
)
logger.info("Saved {0}".format(round(percent, 2))) logger.info("Saved {0}".format(round(percent, 2)))
except BaseException: except BaseException:
pass pass
@ -383,18 +356,12 @@ def encode_media(
with open(tf, "rb") as f: with open(tf, "rb") as f:
myfile = File(f) myfile = File(f)
output_name = "{0}.{1}".format( output_name = "{0}.{1}".format(get_file_name(original_media_path), profile.extension)
get_file_name(original_media_path), profile.extension
)
encoding.media_file.save(content=myfile, name=output_name) encoding.media_file.save(content=myfile, name=output_name)
encoding.total_run_time = ( encoding.total_run_time = (encoding.update_date - encoding.add_date).seconds
encoding.update_date - encoding.add_date
).seconds
try: try:
encoding.save( encoding.save(update_fields=["status", "logs", "progress", "total_run_time"])
update_fields=["status", "logs", "progress", "total_run_time"]
)
# this will raise a django.db.utils.DatabaseError error when task is revoked, # this will raise a django.db.utils.DatabaseError error when task is revoked,
# since we delete the encoding at that stage # since we delete the encoding at that stage
except BaseException: except BaseException:
@ -457,18 +424,14 @@ def create_hls(friendly_token):
p = media.uid.hex p = media.uid.hex
output_dir = os.path.join(settings.HLS_DIR, p) output_dir = os.path.join(settings.HLS_DIR, p)
encodings = media.encodings.filter( encodings = media.encodings.filter(profile__extension="mp4", status="success", chunk=False, profile__codec="h264")
profile__extension="mp4", status="success", chunk=False, profile__codec="h264"
)
if encodings: if encodings:
existing_output_dir = None existing_output_dir = None
if os.path.exists(output_dir): if os.path.exists(output_dir):
existing_output_dir = output_dir existing_output_dir = output_dir
output_dir = os.path.join(settings.HLS_DIR, p + produce_friendly_token()) output_dir = os.path.join(settings.HLS_DIR, p + produce_friendly_token())
files = " ".join([f.media_file.path for f in encodings if f.media_file]) files = " ".join([f.media_file.path for f in encodings if f.media_file])
cmd = "{0} --segment-duration=4 --output-dir={1} {2}".format( cmd = "{0} --segment-duration=4 --output-dir={1} {2}".format(settings.MP4HLS_COMMAND, output_dir, files)
settings.MP4HLS_COMMAND, output_dir, files
)
ret = subprocess.run(cmd, stdout=subprocess.PIPE, shell=True) ret = subprocess.run(cmd, stdout=subprocess.PIPE, shell=True)
if existing_output_dir: if existing_output_dir:
# override content with -T ! # override content with -T !
@ -515,11 +478,7 @@ def check_running_states():
def check_media_states(): def check_media_states():
# Experimental - unused # Experimental - unused
# check encoding status of not success media # check encoding status of not success media
media = Media.objects.filter( media = Media.objects.filter(Q(encoding_status="running") | Q(encoding_status="fail") | Q(encoding_status="pending"))
Q(encoding_status="running")
| Q(encoding_status="fail")
| Q(encoding_status="pending")
)
logger.info("got {0} media that are not in state success".format(media.count())) logger.info("got {0} media that are not in state success".format(media.count()))
@ -564,11 +523,7 @@ def check_pending_states():
media.encode(profiles=[profile], force=False) media.encode(profiles=[profile], force=False)
changed += 1 changed += 1
if changed: if changed:
logger.info( logger.info("set to the encode queue {0} encodings that were on pending state".format(changed))
"set to the encode queue {0} encodings that were on pending state".format(
changed
)
)
return True return True
@ -602,6 +557,7 @@ def clear_sessions():
try: try:
from importlib import import_module from importlib import import_module
from django.conf import settings from django.conf import settings
engine = import_module(settings.SESSION_ENGINE) engine = import_module(settings.SESSION_ENGINE)
@ -612,9 +568,7 @@ def clear_sessions():
@task(name="save_user_action", queue="short_tasks") @task(name="save_user_action", queue="short_tasks")
def save_user_action( def save_user_action(user_or_session, friendly_token=None, action="watch", extra_info=None):
user_or_session, friendly_token=None, action="watch", extra_info=None
):
"""Short task that saves a user action""" """Short task that saves a user action"""
if action not in VALID_USER_ACTIONS: if action not in VALID_USER_ACTIONS:
@ -652,9 +606,7 @@ def save_user_action(
if user: if user:
MediaAction.objects.filter(user=user, media=media, action="watch").delete() MediaAction.objects.filter(user=user, media=media, action="watch").delete()
else: else:
MediaAction.objects.filter( MediaAction.objects.filter(session_key=session_key, media=media, action="watch").delete()
session_key=session_key, media=media, action="watch"
).delete()
if action == "rate": if action == "rate":
try: try:
score = extra_info.get("score") score = extra_info.get("score")
@ -663,9 +615,7 @@ def save_user_action(
# TODO: better error handling? # TODO: better error handling?
return False return False
try: try:
rating = Rating.objects.filter( rating = Rating.objects.filter(user=user, media=media, rating_category_id=rating_category).first()
user=user, media=media, rating_category_id=rating_category
).first()
if rating: if rating:
rating.score = score rating.score = score
rating.save(update_fields=["score"]) rating.save(update_fields=["score"])
@ -735,14 +685,10 @@ def get_list_of_popular_media():
for media in media_x: for media in media_x:
ft = media["friendly_token"] ft = media["friendly_token"]
num = MediaAction.objects.filter( num = MediaAction.objects.filter(action_date__gte=period_x, action="watch", media__friendly_token=ft).count()
action_date__gte=period_x, action="watch", media__friendly_token=ft
).count()
if num: if num:
valid_media_x[ft] = num valid_media_x[ft] = num
num = MediaAction.objects.filter( num = MediaAction.objects.filter(action_date__gte=period_y, action="like", media__friendly_token=ft).count()
action_date__gte=period_y, action="like", media__friendly_token=ft
).count()
if num: if num:
valid_media_y[ft] = num valid_media_y[ft] = num
@ -767,12 +713,7 @@ def update_listings_thumbnails():
saved = 0 saved = 0
qs = Category.objects.filter().order_by("-media_count") qs = Category.objects.filter().order_by("-media_count")
for object in qs: for object in qs:
media = ( media = Media.objects.exclude(friendly_token__in=used_media).filter(category=object, state="public", is_reviewed=True).order_by("-views").first()
Media.objects.exclude(friendly_token__in=used_media)
.filter(category=object, state="public", is_reviewed=True)
.order_by("-views")
.first()
)
if media: if media:
object.listings_thumbnail = media.thumbnail_url object.listings_thumbnail = media.thumbnail_url
object.save(update_fields=["listings_thumbnail"]) object.save(update_fields=["listings_thumbnail"])
@ -785,12 +726,7 @@ def update_listings_thumbnails():
saved = 0 saved = 0
qs = Tag.objects.filter().order_by("-media_count") qs = Tag.objects.filter().order_by("-media_count")
for object in qs: for object in qs:
media = ( media = Media.objects.exclude(friendly_token__in=used_media).filter(tags=object, state="public", is_reviewed=True).order_by("-views").first()
Media.objects.exclude(friendly_token__in=used_media)
.filter(tags=object, state="public", is_reviewed=True)
.order_by("-views")
.first()
)
if media: if media:
object.listings_thumbnail = media.thumbnail_url object.listings_thumbnail = media.thumbnail_url
object.save(update_fields=["listings_thumbnail"]) object.save(update_fields=["listings_thumbnail"])

View file

@ -1,10 +1,9 @@
from django.conf.urls.static import static
from django.conf import settings from django.conf import settings
from django.conf.urls import url, include from django.conf.urls import include, url
from django.conf.urls.static import static
from django.urls import path from django.urls import path
from . import views from . import management_views, views
from . import management_views
from .feeds import IndexRSSFeed, SearchRSSFeed from .feeds import IndexRSSFeed, SearchRSSFeed
urlpatterns = [ urlpatterns = [

View file

@ -1,69 +1,67 @@
from datetime import datetime, timedelta 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 celery.task.control import revoke
from cms.permissions import IsAuthorizedToAdd, IsUserOrEditor from django.conf import settings
from cms.permissions import user_allowed_to_upload from django.contrib import messages
from cms.custom_pagination import FastPaginationWithoutCount from django.contrib.auth.decorators import login_required
from actions.models import MediaAction, USER_MEDIA_ACTIONS from django.contrib.postgres.search import SearchQuery
from users.models import User from django.core.mail import EmailMessage
from .helpers import produce_ffmpeg_commands, clean_query from django.db.models import Q
from .models import ( from django.http import HttpResponseRedirect
Media, from django.shortcuts import get_object_or_404, render
EncodeProfile, from django.template.defaultfilters import slugify
Encoding, from rest_framework import permissions, status
Playlist, from rest_framework.exceptions import PermissionDenied
PlaylistMedia, from rest_framework.parsers import (
Comment, FileUploadParser,
Category, FormParser,
Tag, JSONParser,
MultiPartParser,
) )
from .forms import MediaForm, ContactForm, SubtitleForm from rest_framework.response import Response
from .tasks import save_user_action from rest_framework.settings import api_settings
from rest_framework.views import APIView
from actions.models import USER_MEDIA_ACTIONS, MediaAction
from cms.custom_pagination import FastPaginationWithoutCount
from cms.permissions import IsAuthorizedToAdd, IsUserOrEditor, user_allowed_to_upload
from users.models import User
from .forms import ContactForm, MediaForm, SubtitleForm
from .helpers import clean_query, produce_ffmpeg_commands
from .methods import ( from .methods import (
list_tasks,
get_user_or_session, get_user_or_session,
show_recommended_media,
show_related_media,
is_mediacms_editor, is_mediacms_editor,
is_mediacms_manager, is_mediacms_manager,
update_user_ratings, list_tasks,
notify_user_on_comment, notify_user_on_comment,
show_recommended_media,
show_related_media,
update_user_ratings,
)
from .models import (
Category,
Comment,
EncodeProfile,
Encoding,
Media,
Playlist,
PlaylistMedia,
Tag,
) )
from .serializers import ( from .serializers import (
MediaSerializer,
CategorySerializer, CategorySerializer,
TagSerializer, CommentSerializer,
SingleMediaSerializer,
EncodeProfileSerializer, EncodeProfileSerializer,
MediaSearchSerializer, MediaSearchSerializer,
PlaylistSerializer, MediaSerializer,
PlaylistDetailSerializer, PlaylistDetailSerializer,
CommentSerializer, PlaylistSerializer,
SingleMediaSerializer,
TagSerializer,
) )
from .stop_words import STOP_WORDS from .stop_words import STOP_WORDS
from .tasks import save_user_action
VALID_USER_ACTIONS = [action for action, name in USER_MEDIA_ACTIONS] VALID_USER_ACTIONS = [action for action, name in USER_MEDIA_ACTIONS]
@ -86,11 +84,7 @@ def add_subtitle(request):
if not media: if not media:
return HttpResponseRedirect("/") return HttpResponseRedirect("/")
if not ( if not (request.user == media.user or is_mediacms_editor(request.user) or is_mediacms_manager(request.user)):
request.user == media.user
or is_mediacms_editor(request.user)
or is_mediacms_manager(request.user)
):
return HttpResponseRedirect("/") return HttpResponseRedirect("/")
if request.method == "POST": if request.method == "POST":
@ -175,11 +169,7 @@ def edit_media(request):
if not media: if not media:
return HttpResponseRedirect("/") return HttpResponseRedirect("/")
if not ( if not (request.user == media.user or is_mediacms_editor(request.user) or is_mediacms_manager(request.user)):
request.user == media.user
or is_mediacms_editor(request.user)
or is_mediacms_manager(request.user)
):
return HttpResponseRedirect("/") return HttpResponseRedirect("/")
if request.method == "POST": if request.method == "POST":
form = MediaForm(request.user, request.POST, request.FILES, instance=media) form = MediaForm(request.user, request.POST, request.FILES, instance=media)
@ -342,9 +332,7 @@ def view_media(request):
return render(request, "cms/media.html", context) return render(request, "cms/media.html", context)
user_or_session = get_user_or_session(request) user_or_session = get_user_or_session(request)
save_user_action.delay( save_user_action.delay(user_or_session, friendly_token=friendly_token, action="watch")
user_or_session, friendly_token=friendly_token, action="watch"
)
context = {} context = {}
context["media"] = friendly_token context["media"] = friendly_token
context["media_object"] = media context["media_object"] = media
@ -354,11 +342,7 @@ def view_media(request):
context["CAN_DELETE_COMMENTS"] = False context["CAN_DELETE_COMMENTS"] = False
if request.user.is_authenticated: if request.user.is_authenticated:
if ( if (media.user.id == request.user.id) or is_mediacms_editor(request.user) or is_mediacms_manager(request.user):
(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_DELETE_MEDIA"] = True
context["CAN_EDIT_MEDIA"] = True context["CAN_EDIT_MEDIA"] = True
context["CAN_DELETE_COMMENTS"] = True context["CAN_DELETE_COMMENTS"] = True
@ -443,33 +427,21 @@ class MediaDetail(APIView):
def get_object(self, friendly_token, password=None): def get_object(self, friendly_token, password=None):
try: try:
media = ( media = Media.objects.select_related("user").prefetch_related("encodings__profile").get(friendly_token=friendly_token)
Media.objects.select_related("user")
.prefetch_related("encodings__profile")
.get(friendly_token=friendly_token)
)
# this need be explicitly called, and will call # this need be explicitly called, and will call
# has_object_permission() after has_permission has succeeded # has_object_permission() after has_permission has succeeded
self.check_object_permissions(self.request, media) self.check_object_permissions(self.request, media)
if media.state == "private" and not ( if media.state == "private" and not (self.request.user == media.user or is_mediacms_editor(self.request.user)):
self.request.user == media.user or is_mediacms_editor(self.request.user) if (not password) or (not media.password) or (password != media.password):
):
if (
(not password)
or (not media.password)
or (password != media.password)
):
return Response( return Response(
{"detail": "media is private"}, {"detail": "media is private"},
status=status.HTTP_401_UNAUTHORIZED, status=status.HTTP_401_UNAUTHORIZED,
) )
return media return media
except PermissionDenied: except PermissionDenied:
return Response( return Response({"detail": "bad permissions"}, status=status.HTTP_401_UNAUTHORIZED)
{"detail": "bad permissions"}, status=status.HTTP_401_UNAUTHORIZED
)
except BaseException: except BaseException:
return Response( return Response(
{"detail": "media file does not exist"}, {"detail": "media file does not exist"},
@ -488,23 +460,15 @@ class MediaDetail(APIView):
related_media = [] related_media = []
else: else:
related_media = show_related_media(media, request=request, limit=100) related_media = show_related_media(media, request=request, limit=100)
related_media_serializer = MediaSerializer( related_media_serializer = MediaSerializer(related_media, many=True, context={"request": request})
related_media, many=True, context={"request": request}
)
related_media = related_media_serializer.data related_media = related_media_serializer.data
ret = serializer.data ret = serializer.data
# update rattings info with user specific ratings # update rattings info with user specific ratings
# eg user has already rated for this media # eg user has already rated for this media
# this only affects user rating and only if enabled # this only affects user rating and only if enabled
if ( if settings.ALLOW_RATINGS and ret.get("ratings_info") and not request.user.is_anonymous:
settings.ALLOW_RATINGS ret["ratings_info"] = update_user_ratings(request.user, media, ret.get("ratings_info"))
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 ret["related_media"] = related_media
return Response(ret) return Response(ret)
@ -521,9 +485,7 @@ class MediaDetail(APIView):
return media return media
if not (is_mediacms_editor(request.user) or is_mediacms_manager(request.user)): if not (is_mediacms_editor(request.user) or is_mediacms_manager(request.user)):
return Response( return Response({"detail": "not allowed"}, status=status.HTTP_400_BAD_REQUEST)
{"detail": "not allowed"}, status=status.HTTP_400_BAD_REQUEST
)
action = request.data.get("type") action = request.data.get("type")
profiles_list = request.data.get("encoding_profiles") profiles_list = request.data.get("encoding_profiles")
@ -544,24 +506,18 @@ class MediaDetail(APIView):
valid_profiles.append(p) valid_profiles.append(p)
except ValueError: except ValueError:
return Response( return Response(
{ {"detail": "encoding_profiles must be int or list of ints of valid encode profiles"},
"detail": "encoding_profiles must be int or list of ints of valid encode profiles"
},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
media.encode(profiles=valid_profiles) media.encode(profiles=valid_profiles)
return Response( return Response({"detail": "media will be encoded"}, status=status.HTTP_201_CREATED)
{"detail": "media will be encoded"}, status=status.HTTP_201_CREATED
)
elif action == "review": elif action == "review":
if result: if result:
media.is_reviewed = True media.is_reviewed = True
elif result == False: elif result is False:
media.is_reviewed = False media.is_reviewed = False
media.save(update_fields=["is_reviewed"]) media.save(update_fields=["is_reviewed"])
return Response( return Response({"detail": "media reviewed set"}, status=status.HTTP_201_CREATED)
{"detail": "media reviewed set"}, status=status.HTTP_201_CREATED
)
return Response( return Response(
{"detail": "not valid action or no action specified"}, {"detail": "not valid action or no action specified"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
@ -573,9 +529,7 @@ class MediaDetail(APIView):
if isinstance(media, Response): if isinstance(media, Response):
return media return media
serializer = MediaSerializer( serializer = MediaSerializer(media, data=request.data, context={"request": request})
media, data=request.data, context={"request": request}
)
if serializer.is_valid(): if serializer.is_valid():
media_file = request.data["media_file"] media_file = request.data["media_file"]
serializer.save(user=request.user, media_file=media_file) serializer.save(user=request.user, media_file=media_file)
@ -601,20 +555,12 @@ class MediaActions(APIView):
def get_object(self, friendly_token): def get_object(self, friendly_token):
try: try:
media = ( media = Media.objects.select_related("user").prefetch_related("encodings__profile").get(friendly_token=friendly_token)
Media.objects.select_related("user")
.prefetch_related("encodings__profile")
.get(friendly_token=friendly_token)
)
if media.state == "private" and self.request.user != media.user: if media.state == "private" and self.request.user != media.user:
return Response( return Response({"detail": "media is private"}, status=status.HTTP_400_BAD_REQUEST)
{"detail": "media is private"}, status=status.HTTP_400_BAD_REQUEST
)
return media return media
except PermissionDenied: except PermissionDenied:
return Response( return Response({"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST)
{"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST
)
except BaseException: except BaseException:
return Response( return Response(
{"detail": "media file does not exist"}, {"detail": "media file does not exist"},
@ -661,13 +607,9 @@ class MediaActions(APIView):
extra_info=extra, extra_info=extra,
) )
return Response( return Response({"detail": "action received"}, status=status.HTTP_201_CREATED)
{"detail": "action received"}, status=status.HTTP_201_CREATED
)
else: else:
return Response( return Response({"detail": "no action specified"}, status=status.HTTP_400_BAD_REQUEST)
{"detail": "no action specified"}, status=status.HTTP_400_BAD_REQUEST
)
def delete(self, request, friendly_token, format=None): def delete(self, request, friendly_token, format=None):
media = self.get_object(friendly_token) media = self.get_object(friendly_token)
@ -675,9 +617,7 @@ class MediaActions(APIView):
return media return media
if not request.user.is_superuser: if not request.user.is_superuser:
return Response( return Response({"detail": "not allowed"}, status=status.HTTP_400_BAD_REQUEST)
{"detail": "not allowed"}, status=status.HTTP_400_BAD_REQUEST
)
action = request.data.get("type") action = request.data.get("type")
if action: if action:
@ -690,9 +630,7 @@ class MediaActions(APIView):
status=status.HTTP_201_CREATED, status=status.HTTP_201_CREATED,
) )
else: else:
return Response( return Response({"detail": "no action specified"}, status=status.HTTP_400_BAD_REQUEST)
{"detail": "no action specified"}, status=status.HTTP_400_BAD_REQUEST
)
class MediaSearch(APIView): class MediaSearch(APIView):
@ -736,11 +674,7 @@ class MediaSearch(APIView):
if query: if query:
# move this processing to a prepare_query function # move this processing to a prepare_query function
query = clean_query(query) query = clean_query(query)
q_parts = [ q_parts = [q_part.rstrip("y") for q_part in query.split() if q_part not in STOP_WORDS]
q_part.rstrip("y")
for q_part in query.split()
if q_part not in STOP_WORDS
]
if q_parts: if q_parts:
query = SearchQuery(q_parts[0] + ":*", search_type="raw") query = SearchQuery(q_parts[0] + ":*", search_type="raw")
for part in q_parts[1:]: for part in q_parts[1:]:
@ -771,10 +705,10 @@ class MediaSearch(APIView):
if upload_date == 'this_month': if upload_date == 'this_month':
year = datetime.now().date().year year = datetime.now().date().year
month = datetime.now().date().month month = datetime.now().date().month
gte = datetime(year,month,1) gte = datetime(year, month, 1)
if upload_date == 'this_year': if upload_date == 'this_year':
year = datetime.now().date().year year = datetime.now().date().year
gte = datetime(year,1,1) gte = datetime(year, 1, 1)
if lte: if lte:
media = media.filter(add_date__lte=lte) media = media.filter(add_date__lte=lte)
if gte: if gte:
@ -794,9 +728,7 @@ class MediaSearch(APIView):
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
paginator = pagination_class() paginator = pagination_class()
page = paginator.paginate_queryset(media, request) page = paginator.paginate_queryset(media, request)
serializer = MediaSearchSerializer( serializer = MediaSearchSerializer(page, many=True, context={"request": request})
page, many=True, context={"request": request}
)
return paginator.get_paginated_response(serializer.data) return paginator.get_paginated_response(serializer.data)
@ -840,9 +772,7 @@ class PlaylistDetail(APIView):
self.check_object_permissions(self.request, playlist) self.check_object_permissions(self.request, playlist)
return playlist return playlist
except PermissionDenied: except PermissionDenied:
return Response( return Response({"detail": "not enough permissions"}, status=status.HTTP_400_BAD_REQUEST)
{"detail": "not enough permissions"}, status=status.HTTP_400_BAD_REQUEST
)
except BaseException: except BaseException:
return Response( return Response(
{"detail": "Playlist does not exist"}, {"detail": "Playlist does not exist"},
@ -856,14 +786,10 @@ class PlaylistDetail(APIView):
serializer = PlaylistDetailSerializer(playlist, context={"request": request}) serializer = PlaylistDetailSerializer(playlist, context={"request": request})
playlist_media = PlaylistMedia.objects.filter( playlist_media = PlaylistMedia.objects.filter(playlist=playlist).prefetch_related("media__user")
playlist=playlist
).prefetch_related("media__user")
playlist_media = [c.media for c in playlist_media] playlist_media = [c.media for c in playlist_media]
playlist_media_serializer = MediaSerializer( playlist_media_serializer = MediaSerializer(playlist_media, many=True, context={"request": request})
playlist_media, many=True, context={"request": request}
)
ret = serializer.data ret = serializer.data
ret["playlist_media"] = playlist_media_serializer.data ret["playlist_media"] = playlist_media_serializer.data
@ -873,9 +799,7 @@ class PlaylistDetail(APIView):
playlist = self.get_playlist(friendly_token) playlist = self.get_playlist(friendly_token)
if isinstance(playlist, Response): if isinstance(playlist, Response):
return playlist return playlist
serializer = PlaylistDetailSerializer( serializer = PlaylistDetailSerializer(playlist, data=request.data, context={"request": request})
playlist, data=request.data, context={"request": request}
)
if serializer.is_valid(): if serializer.is_valid():
serializer.save(user=request.user) serializer.save(user=request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.data, status=status.HTTP_201_CREATED)
@ -895,14 +819,10 @@ class PlaylistDetail(APIView):
pass pass
if action in ["add", "remove", "ordering"]: if action in ["add", "remove", "ordering"]:
media = Media.objects.filter( media = Media.objects.filter(friendly_token=media_friendly_token).first()
friendly_token=media_friendly_token
).first()
if media: if media:
if action == "add": if action == "add":
media_in_playlist = PlaylistMedia.objects.filter( media_in_playlist = PlaylistMedia.objects.filter(playlist=playlist).count()
playlist=playlist
).count()
if media_in_playlist >= settings.MAX_MEDIA_PER_PLAYLIST: if media_in_playlist >= settings.MAX_MEDIA_PER_PLAYLIST:
return Response( return Response(
{"detail": "max number of media for a Playlist reached"}, {"detail": "max number of media for a Playlist reached"},
@ -920,9 +840,7 @@ class PlaylistDetail(APIView):
status=status.HTTP_201_CREATED, status=status.HTTP_201_CREATED,
) )
elif action == "remove": elif action == "remove":
PlaylistMedia.objects.filter( PlaylistMedia.objects.filter(playlist=playlist, media=media).delete()
playlist=playlist, media=media
).delete()
return Response( return Response(
{"detail": "media removed from Playlist"}, {"detail": "media removed from Playlist"},
status=status.HTTP_201_CREATED, status=status.HTTP_201_CREATED,
@ -935,9 +853,7 @@ class PlaylistDetail(APIView):
status=status.HTTP_201_CREATED, status=status.HTTP_201_CREATED,
) )
else: else:
return Response( return Response({"detail": "media is not valid"}, status=status.HTTP_400_BAD_REQUEST)
{"detail": "media is not valid"}, status=status.HTTP_400_BAD_REQUEST
)
return Response( return Response(
{"detail": "invalid or not specified action"}, {"detail": "invalid or not specified action"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
@ -993,7 +909,7 @@ class EncodingDetail(APIView):
chunk_file_path=chunk_file_path, chunk_file_path=chunk_file_path,
).count() ).count()
> 1 > 1
and force == False and force is False
): ):
Encoding.objects.filter(id=encoding_id).delete() Encoding.objects.filter(id=encoding_id).delete()
return Response({"status": "fail"}, status=status.HTTP_400_BAD_REQUEST) return Response({"status": "fail"}, status=status.HTTP_400_BAD_REQUEST)
@ -1013,15 +929,11 @@ class EncodingDetail(APIView):
if chunk: if chunk:
original_media_path = chunk_file_path original_media_path = chunk_file_path
original_media_md5sum = encoding.md5sum original_media_md5sum = encoding.md5sum
original_media_url = ( original_media_url = settings.SSL_FRONTEND_HOST + encoding.media_chunk_url
settings.SSL_FRONTEND_HOST + encoding.media_chunk_url
)
else: else:
original_media_path = media.media_file.path original_media_path = media.media_file.path
original_media_md5sum = media.md5sum original_media_md5sum = media.md5sum
original_media_url = ( original_media_url = settings.SSL_FRONTEND_HOST + media.original_media_url
settings.SSL_FRONTEND_HOST + media.original_media_url
)
ret["original_media_url"] = original_media_url ret["original_media_url"] = original_media_url
ret["original_media_path"] = original_media_path ret["original_media_path"] = original_media_path
@ -1137,19 +1049,13 @@ class CommentDetail(APIView):
def get_object(self, friendly_token): def get_object(self, friendly_token):
try: try:
media = Media.objects.select_related("user").get( media = Media.objects.select_related("user").get(friendly_token=friendly_token)
friendly_token=friendly_token
)
self.check_object_permissions(self.request, media) self.check_object_permissions(self.request, media)
if media.state == "private" and self.request.user != media.user: if media.state == "private" and self.request.user != media.user:
return Response( return Response({"detail": "media is private"}, status=status.HTTP_400_BAD_REQUEST)
{"detail": "media is private"}, status=status.HTTP_400_BAD_REQUEST
)
return media return media
except PermissionDenied: except PermissionDenied:
return Response( return Response({"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST)
{"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST
)
except BaseException: except BaseException:
return Response( return Response(
{"detail": "media file does not exist"}, {"detail": "media file does not exist"},
@ -1181,16 +1087,10 @@ class CommentDetail(APIView):
{"detail": "comment does not exist"}, {"detail": "comment does not exist"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
if ( if (comment.user == self.request.user) or comment.media.user == self.request.user or is_mediacms_editor(self.request.user):
(comment.user == self.request.user)
or comment.media.user == self.request.user
or is_mediacms_editor(self.request.user)
):
comment.delete() comment.delete()
else: else:
return Response( return Response({"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST)
{"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST
)
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
def post(self, request, friendly_token): def post(self, request, friendly_token):
@ -1221,13 +1121,7 @@ class UserActions(APIView):
media = [] media = []
if action in VALID_USER_ACTIONS: if action in VALID_USER_ACTIONS:
if request.user.is_authenticated: if request.user.is_authenticated:
media = ( media = Media.objects.select_related("user").filter(mediaactions__user=request.user, mediaactions__action=action).order_by("-mediaactions__action_date")
Media.objects.select_related("user")
.filter(
mediaactions__user=request.user, mediaactions__action=action
)
.order_by("-mediaactions__action_date")
)
elif request.session.session_key: elif request.session.session_key:
media = ( media = (
Media.objects.select_related("user") Media.objects.select_related("user")
@ -1250,9 +1144,7 @@ class CategoryList(APIView):
def get(self, request, format=None): def get(self, request, format=None):
categories = Category.objects.filter().order_by("title") categories = Category.objects.filter().order_by("title")
serializer = CategorySerializer( serializer = CategorySerializer(categories, many=True, context={"request": request})
categories, many=True, context={"request": request}
)
ret = serializer.data ret = serializer.data
return Response(ret) return Response(ret)
@ -1274,9 +1166,7 @@ class EncodeProfileList(APIView):
def get(self, request, format=None): def get(self, request, format=None):
profiles = EncodeProfile.objects.all() profiles = EncodeProfile.objects.all()
serializer = EncodeProfileSerializer( serializer = EncodeProfileSerializer(profiles, many=True, context={"request": request})
profiles, many=True, context={"request": request}
)
return Response(serializer.data) return Response(serializer.data)

View file

@ -7,9 +7,5 @@ if __name__ == "__main__":
try: try:
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
except ImportError as exc: except ImportError as exc:
raise ImportError( raise ImportError("Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?") from exc
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv) execute_from_command_line(sys.argv)

View file

@ -1,4 +1,4 @@
[flake8] [flake8]
exclude = .git,*migrations* exclude = .git,*migrations*
max-line-length = 119 max-line-length = 119
ignore=F401,E711,E302,E201,E501,E303,E231,F841,E722 ignore=F401,F403,W503,E711,E302,E201,E501,E303,E231,F841,E722

View file

@ -1,6 +1,7 @@
from os.path import join
from io import StringIO
import shutil import shutil
from io import StringIO
from os.path import join
from django.conf import settings from django.conf import settings
from . import utils from . import utils

View file

@ -1,6 +1,7 @@
from django.core.exceptions import ImproperlyConfigured
from importlib import import_module from importlib import import_module
from django.core.exceptions import ImproperlyConfigured
def import_class(path): def import_class(path):
path_bits = path.split(".") path_bits = path.split(".")
@ -14,9 +15,7 @@ def import_class(path):
module_itself = import_module(module_path) module_itself = import_module(module_path)
if not hasattr(module_itself, class_name): if not hasattr(module_itself, class_name):
message = "The Python module '{}' has no '{}' class.".format( message = "The Python module '{}' has no '{}' class.".format(module_path, class_name)
module_path, class_name
)
raise ImportError(message) raise ImportError(message)
return getattr(module_itself, class_name) return getattr(module_itself, class_name)

View file

@ -2,17 +2,18 @@
import os import os
import shutil import shutil
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.core.files import File
from django.http import JsonResponse from django.http import JsonResponse
from django.views import generic from django.views import generic
from django.conf import settings
from django.core.files import File
from django.core.exceptions import PermissionDenied
from cms.permissions import user_allowed_to_upload from cms.permissions import user_allowed_to_upload
from files.models import Media
from files.helpers import rm_file from files.helpers import rm_file
from .forms import FineUploaderUploadForm, FineUploaderUploadSuccessForm from files.models import Media
from .fineuploader import ChunkedFineUploader from .fineuploader import ChunkedFineUploader
from .forms import FineUploaderUploadForm, FineUploaderUploadSuccessForm
class FineUploaderView(generic.FormView): class FineUploaderView(generic.FormView):
@ -67,9 +68,7 @@ class FineUploaderView(generic.FormView):
new = Media.objects.create(media_file=myfile, user=self.request.user) new = Media.objects.create(media_file=myfile, user=self.request.user)
rm_file(media_file) rm_file(media_file)
shutil.rmtree(os.path.join(settings.MEDIA_ROOT, self.upload.file_path)) shutil.rmtree(os.path.join(settings.MEDIA_ROOT, self.upload.file_path))
return self.make_response( return self.make_response({"success": True, "media_url": new.get_absolute_url()})
{"success": True, "media_url": new.get_absolute_url()}
)
def form_invalid(self, form): def form_invalid(self, form):
data = {"success": False, "error": "%s" % repr(form.errors)} data = {"success": False, "error": "%s" % repr(form.errors)}

View file

@ -1,7 +1,7 @@
from django.urls import reverse
from django.conf import settings
from allauth.account.adapter import DefaultAccountAdapter from allauth.account.adapter import DefaultAccountAdapter
from django.conf import settings
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.urls import reverse
class MyAccountAdapter(DefaultAccountAdapter): class MyAccountAdapter(DefaultAccountAdapter):

View file

@ -1,5 +1,6 @@
from django import forms from django import forms
from .models import User, Channel
from .models import Channel, User
class SignupForm(forms.Form): class SignupForm(forms.Form):
@ -23,7 +24,7 @@ class UserForm(forms.ModelForm):
"advancedUser", "advancedUser",
"is_manager", "is_manager",
"is_editor", "is_editor",
#"allow_contact", # "allow_contact",
) )
def clean_logo(self): def clean_logo(self):

View file

@ -1,12 +1,12 @@
# Generated by Django 3.1.4 on 2020-12-01 07:12 # Generated by Django 3.1.4 on 2020-12-01 07:12
from django.conf import settings
import django.contrib.auth.models import django.contrib.auth.models
import django.contrib.auth.validators import django.contrib.auth.validators
from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import django.utils.timezone import django.utils.timezone
import imagekit.models.fields import imagekit.models.fields
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -33,9 +33,7 @@ class Migration(migrations.Migration):
("password", models.CharField(max_length=128, verbose_name="password")), ("password", models.CharField(max_length=128, verbose_name="password")),
( (
"last_login", "last_login",
models.DateTimeField( models.DateTimeField(blank=True, null=True, verbose_name="last login"),
blank=True, null=True, verbose_name="last login"
),
), ),
( (
"is_superuser", "is_superuser",
@ -48,35 +46,25 @@ class Migration(migrations.Migration):
( (
"username", "username",
models.CharField( models.CharField(
error_messages={ error_messages={"unique": "A user with that username already exists."},
"unique": "A user with that username already exists."
},
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
max_length=150, max_length=150,
unique=True, unique=True,
validators=[ validators=[django.contrib.auth.validators.UnicodeUsernameValidator()],
django.contrib.auth.validators.UnicodeUsernameValidator()
],
verbose_name="username", verbose_name="username",
), ),
), ),
( (
"first_name", "first_name",
models.CharField( models.CharField(blank=True, max_length=150, verbose_name="first name"),
blank=True, max_length=150, verbose_name="first name"
),
), ),
( (
"last_name", "last_name",
models.CharField( models.CharField(blank=True, max_length=150, verbose_name="last name"),
blank=True, max_length=150, verbose_name="last name"
),
), ),
( (
"email", "email",
models.EmailField( models.EmailField(blank=True, max_length=254, verbose_name="email address"),
blank=True, max_length=254, verbose_name="email address"
),
), ),
( (
"is_staff", "is_staff",
@ -96,9 +84,7 @@ class Migration(migrations.Migration):
), ),
( (
"date_joined", "date_joined",
models.DateTimeField( models.DateTimeField(default=django.utils.timezone.now, verbose_name="date joined"),
default=django.utils.timezone.now, verbose_name="date joined"
),
), ),
( (
"logo", "logo",
@ -111,9 +97,7 @@ class Migration(migrations.Migration):
("description", models.TextField(blank=True, verbose_name="About me")), ("description", models.TextField(blank=True, verbose_name="About me")),
( (
"name", "name",
models.CharField( models.CharField(db_index=True, max_length=250, verbose_name="full name"),
db_index=True, max_length=250, verbose_name="full name"
),
), ),
( (
"date_added", "date_added",
@ -125,9 +109,7 @@ class Migration(migrations.Migration):
), ),
( (
"is_featured", "is_featured",
models.BooleanField( models.BooleanField(db_index=True, default=False, verbose_name="Is featured"),
db_index=True, default=False, verbose_name="Is featured"
),
), ),
( (
"title", "title",
@ -135,9 +117,7 @@ class Migration(migrations.Migration):
), ),
( (
"advancedUser", "advancedUser",
models.BooleanField( models.BooleanField(db_index=True, default=False, verbose_name="advanced user"),
db_index=True, default=False, verbose_name="advanced user"
),
), ),
("media_count", models.IntegerField(default=0)), ("media_count", models.IntegerField(default=0)),
( (
@ -156,21 +136,15 @@ class Migration(migrations.Migration):
), ),
( (
"location", "location",
models.CharField( models.CharField(blank=True, max_length=250, verbose_name="Location"),
blank=True, max_length=250, verbose_name="Location"
),
), ),
( (
"is_editor", "is_editor",
models.BooleanField( models.BooleanField(db_index=True, default=False, verbose_name="MediaCMS Editor"),
db_index=True, default=False, verbose_name="MediaCMS Editor"
),
), ),
( (
"is_manager", "is_manager",
models.BooleanField( models.BooleanField(db_index=True, default=False, verbose_name="MediaCMS Manager"),
db_index=True, default=False, verbose_name="MediaCMS Manager"
),
), ),
( (
"groups", "groups",
@ -218,9 +192,7 @@ class Migration(migrations.Migration):
("notify", models.BooleanField(default=False)), ("notify", models.BooleanField(default=False)),
( (
"method", "method",
models.CharField( models.CharField(choices=[("email", "Email")], default="email", max_length=20),
choices=[("email", "Email")], default="email", max_length=20
),
), ),
( (
"user", "user",
@ -276,8 +248,6 @@ class Migration(migrations.Migration):
), ),
migrations.AddIndex( migrations.AddIndex(
model_name="user", model_name="user",
index=models.Index( index=models.Index(fields=["-date_added", "name"], name="users_user_date_ad_4eb0b8_idx"),
fields=["-date_added", "name"], name="users_user_date_ad_4eb0b8_idx"
),
), ),
] ]

View file

@ -1,18 +1,17 @@
from django.db import models
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.utils import timezone
from django.urls import reverse
from django.dispatch import receiver
from django.db.models.signals import post_save, post_delete
from django.utils.html import strip_tags
from django.core.mail import EmailMessage from django.core.mail import EmailMessage
from django.db import models
from imagekit.processors import ResizeToFill from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from django.urls import reverse
from django.utils import timezone
from django.utils.html import strip_tags
from imagekit.models import ProcessedImageField from imagekit.models import ProcessedImageField
from imagekit.processors import ResizeToFill
import files.helpers as helpers import files.helpers as helpers
from files.models import Media, Tag, Category from files.models import Category, Media, Tag
class User(AbstractUser): class User(AbstractUser):
@ -40,9 +39,7 @@ class User(AbstractUser):
location = models.CharField("Location", max_length=250, blank=True) location = models.CharField("Location", max_length=250, blank=True)
is_editor = models.BooleanField("MediaCMS Editor", default=False, db_index=True) is_editor = models.BooleanField("MediaCMS Editor", default=False, db_index=True)
is_manager = models.BooleanField("MediaCMS Manager", default=False, db_index=True) is_manager = models.BooleanField("MediaCMS Manager", default=False, db_index=True)
allow_contact = models.BooleanField( allow_contact = models.BooleanField("Whether allow contact will be shown on profile page", default=False)
"Whether allow contact will be shown on profile page", default=False
)
class Meta: class Meta:
ordering = ["-date_added", "name"] ordering = ["-date_added", "name"]
@ -117,9 +114,7 @@ class User(AbstractUser):
class Channel(models.Model): class Channel(models.Model):
title = models.CharField(max_length=90, db_index=True) title = models.CharField(max_length=90, db_index=True)
description = models.TextField(blank=True, help_text="description") description = models.TextField(blank=True, help_text="description")
user = models.ForeignKey( user = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True, related_name="channels")
User, on_delete=models.CASCADE, db_index=True, related_name="channels"
)
add_date = models.DateTimeField(auto_now_add=True, db_index=True) add_date = models.DateTimeField(auto_now_add=True, db_index=True)
subscribers = models.ManyToManyField(User, related_name="subscriptions", blank=True) subscribers = models.ManyToManyField(User, related_name="subscriptions", blank=True)
friendly_token = models.CharField(blank=True, max_length=12) friendly_token = models.CharField(blank=True, max_length=12)
@ -150,13 +145,9 @@ class Channel(models.Model):
def get_absolute_url(self, edit=False): def get_absolute_url(self, edit=False):
if edit: if edit:
return reverse( return reverse("edit_channel", kwargs={"friendly_token": self.friendly_token})
"edit_channel", kwargs={"friendly_token": self.friendly_token}
)
else: else:
return reverse( return reverse("view_channel", kwargs={"friendly_token": self.friendly_token})
"view_channel", kwargs={"friendly_token": self.friendly_token}
)
@property @property
def edit_url(self): def edit_url(self):
@ -178,9 +169,7 @@ Visit user profile page at %s
instance.email, instance.email,
settings.SSL_FRONTEND_HOST + instance.get_absolute_url(), settings.SSL_FRONTEND_HOST + instance.get_absolute_url(),
) )
email = EmailMessage( email = EmailMessage(title, msg, settings.DEFAULT_FROM_EMAIL, settings.ADMIN_EMAIL_LIST)
title, msg, settings.DEFAULT_FROM_EMAIL, settings.ADMIN_EMAIL_LIST
)
email.send(fail_silently=True) email.send(fail_silently=True)
@ -193,14 +182,10 @@ class Notification(models.Model):
Needs work Needs work
""" """
user = models.ForeignKey( user = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True, related_name="notifications")
User, on_delete=models.CASCADE, db_index=True, related_name="notifications"
)
action = models.CharField(max_length=30, blank=True) action = models.CharField(max_length=30, blank=True)
notify = models.BooleanField(default=False) notify = models.BooleanField(default=False)
method = models.CharField( method = models.CharField(max_length=20, choices=NOTIFICATION_METHODS, default="email")
max_length=20, choices=NOTIFICATION_METHODS, default="email"
)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
super(Notification, self).save(*args, **kwargs) super(Notification, self).save(*args, **kwargs)

View file

@ -1,4 +1,5 @@
from rest_framework import serializers from rest_framework import serializers
from .models import User from .models import User
@ -11,9 +12,7 @@ class UserSerializer(serializers.ModelSerializer):
return self.context["request"].build_absolute_uri(obj.get_absolute_url()) return self.context["request"].build_absolute_uri(obj.get_absolute_url())
def get_api_url(self, obj): def get_api_url(self, obj):
return self.context["request"].build_absolute_uri( return self.context["request"].build_absolute_uri(obj.get_absolute_url(api=True))
obj.get_absolute_url(api=True)
)
def get_thumbnail_url(self, obj): def get_thumbnail_url(self, obj):
return self.context["request"].build_absolute_uri(obj.thumbnail_url()) return self.context["request"].build_absolute_uri(obj.thumbnail_url())
@ -55,9 +54,7 @@ class UserDetailSerializer(serializers.ModelSerializer):
return self.context["request"].build_absolute_uri(obj.get_absolute_url()) return self.context["request"].build_absolute_uri(obj.get_absolute_url())
def get_api_url(self, obj): def get_api_url(self, obj):
return self.context["request"].build_absolute_uri( return self.context["request"].build_absolute_uri(obj.get_absolute_url(api=True))
obj.get_absolute_url(api=True)
)
def get_thumbnail_url(self, obj): def get_thumbnail_url(self, obj):
return self.context["request"].build_absolute_uri(obj.thumbnail_url()) return self.context["request"].build_absolute_uri(obj.thumbnail_url())

View file

@ -1,4 +1,5 @@
from django.conf.urls import url from django.conf.urls import url
from . import views from . import views
urlpatterns = [ urlpatterns = [
@ -20,9 +21,7 @@ urlpatterns = [
name="get_user_about", name="get_user_about",
), ),
url(r"^user/(?P<username>[\w@.]*)/edit$", views.edit_user, name="edit_user"), url(r"^user/(?P<username>[\w@.]*)/edit$", views.edit_user, name="edit_user"),
url( url(r"^channel/(?P<friendly_token>[\w]*)$", views.view_channel, name="view_channel"),
r"^channel/(?P<friendly_token>[\w]*)$", views.view_channel, name="view_channel"
),
url( url(
r"^channel/(?P<friendly_token>[\w]*)/edit$", r"^channel/(?P<friendly_token>[\w]*)/edit$",
views.edit_channel, views.edit_channel,

View file

@ -8,10 +8,7 @@ from django.utils.translation import gettext_lazy as _
@deconstructible @deconstructible
class ASCIIUsernameValidator(validators.RegexValidator): class ASCIIUsernameValidator(validators.RegexValidator):
regex = r"^[\w]+$" regex = r"^[\w]+$"
message = _( message = _("Enter a valid username. This value may contain only " "English letters and numbers")
"Enter a valid username. This value may contain only "
"English letters and numbers"
)
flags = re.ASCII flags = re.ASCII

View file

@ -1,27 +1,27 @@
from django.shortcuts import render from django.conf import settings
from django.http import HttpResponseRedirect
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.mail import EmailMessage from django.core.mail import EmailMessage
from django.conf import settings from django.http import HttpResponseRedirect
from django.shortcuts import render
from rest_framework import permissions from rest_framework import permissions, status
from rest_framework.views import APIView from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.exceptions import PermissionDenied from rest_framework.exceptions import PermissionDenied
from rest_framework import status
from rest_framework.parsers import ( from rest_framework.parsers import (
JSONParser,
MultiPartParser,
FileUploadParser, FileUploadParser,
FormParser, FormParser,
JSONParser,
MultiPartParser,
) )
from rest_framework.decorators import api_view from rest_framework.response import Response
from rest_framework.settings import api_settings
from rest_framework.views import APIView
from cms.permissions import IsUserOrManager from cms.permissions import IsUserOrManager
from files.methods import is_mediacms_manager, is_mediacms_editor from files.methods import is_mediacms_editor, is_mediacms_manager
from .models import User, Channel
from .forms import UserForm, ChannelForm from .forms import ChannelForm, UserForm
from .serializers import UserSerializer, UserDetailSerializer from .models import Channel, User
from .serializers import UserDetailSerializer, UserSerializer
def get_user(username): def get_user(username):
@ -38,15 +38,9 @@ def view_user(request, username):
if not user: if not user:
return HttpResponseRedirect("/members") return HttpResponseRedirect("/members")
context["user"] = user context["user"] = user
context["CAN_EDIT"] = ( context["CAN_EDIT"] = True if ((user and user == request.user) or is_mediacms_manager(request.user)) else False
True
if ((user and user == request.user) or is_mediacms_manager(request.user))
else False
)
context["CAN_DELETE"] = True if is_mediacms_manager(request.user) else False context["CAN_DELETE"] = True if is_mediacms_manager(request.user) else False
context["SHOW_CONTACT_FORM"] = ( context["SHOW_CONTACT_FORM"] = True if (user.allow_contact or is_mediacms_editor(request.user)) else False
True if (user.allow_contact or is_mediacms_editor(request.user)) else False
)
return render(request, "cms/user.html", context) return render(request, "cms/user.html", context)
@ -57,15 +51,9 @@ def view_user_media(request, username):
return HttpResponseRedirect("/members") return HttpResponseRedirect("/members")
context["user"] = user context["user"] = user
context["CAN_EDIT"] = ( context["CAN_EDIT"] = True if ((user and user == request.user) or is_mediacms_manager(request.user)) else False
True
if ((user and user == request.user) or is_mediacms_manager(request.user))
else False
)
context["CAN_DELETE"] = True if is_mediacms_manager(request.user) else False context["CAN_DELETE"] = True if is_mediacms_manager(request.user) else False
context["SHOW_CONTACT_FORM"] = ( context["SHOW_CONTACT_FORM"] = True if (user.allow_contact or is_mediacms_editor(request.user)) else False
True if (user.allow_contact or is_mediacms_editor(request.user)) else False
)
return render(request, "cms/user_media.html", context) return render(request, "cms/user_media.html", context)
@ -76,15 +64,9 @@ def view_user_playlists(request, username):
return HttpResponseRedirect("/members") return HttpResponseRedirect("/members")
context["user"] = user context["user"] = user
context["CAN_EDIT"] = ( context["CAN_EDIT"] = True if ((user and user == request.user) or is_mediacms_manager(request.user)) else False
True
if ((user and user == request.user) or is_mediacms_manager(request.user))
else False
)
context["CAN_DELETE"] = True if is_mediacms_manager(request.user) else False context["CAN_DELETE"] = True if is_mediacms_manager(request.user) else False
context["SHOW_CONTACT_FORM"] = ( context["SHOW_CONTACT_FORM"] = True if (user.allow_contact or is_mediacms_editor(request.user)) else False
True if (user.allow_contact or is_mediacms_editor(request.user)) else False
)
return render(request, "cms/user_playlists.html", context) return render(request, "cms/user_playlists.html", context)
@ -96,15 +78,9 @@ def view_user_about(request, username):
return HttpResponseRedirect("/members") return HttpResponseRedirect("/members")
context["user"] = user context["user"] = user
context["CAN_EDIT"] = ( context["CAN_EDIT"] = True if ((user and user == request.user) or is_mediacms_manager(request.user)) else False
True
if ((user and user == request.user) or is_mediacms_manager(request.user))
else False
)
context["CAN_DELETE"] = True if is_mediacms_manager(request.user) else False context["CAN_DELETE"] = True if is_mediacms_manager(request.user) else False
context["SHOW_CONTACT_FORM"] = ( context["SHOW_CONTACT_FORM"] = True if (user.allow_contact or is_mediacms_editor(request.user)) else False
True if (user.allow_contact or is_mediacms_editor(request.user)) else False
)
return render(request, "cms/user_about.html", context) return render(request, "cms/user_about.html", context)
@ -134,20 +110,14 @@ def view_channel(request, friendly_token):
else: else:
user = channel.user user = channel.user
context["user"] = user context["user"] = user
context["CAN_EDIT"] = ( context["CAN_EDIT"] = True if ((user and user == request.user) or is_mediacms_manager(request.user)) else False
True
if ((user and user == request.user) or is_mediacms_manager(request.user))
else False
)
return render(request, "cms/channel.html", context) return render(request, "cms/channel.html", context)
@login_required @login_required
def edit_channel(request, friendly_token): def edit_channel(request, friendly_token):
channel = Channel.objects.filter(friendly_token=friendly_token).first() channel = Channel.objects.filter(friendly_token=friendly_token).first()
if not ( if not (channel and request.user.is_authenticated and (request.user == channel.user)):
channel and request.user.is_authenticated and (request.user == channel.user)
):
return HttpResponseRedirect("/") return HttpResponseRedirect("/")
if request.method == "POST": if request.method == "POST":
@ -228,13 +198,9 @@ class UserDetail(APIView):
self.check_object_permissions(self.request, user) self.check_object_permissions(self.request, user)
return user return user
except PermissionDenied: except PermissionDenied:
return Response( return Response({"detail": "not enough permissions"}, status=status.HTTP_400_BAD_REQUEST)
{"detail": "not enough permissions"}, status=status.HTTP_400_BAD_REQUEST
)
except User.DoesNotExist: except User.DoesNotExist:
return Response( return Response({"detail": "user does not exist"}, status=status.HTTP_400_BAD_REQUEST)
{"detail": "user does not exist"}, status=status.HTTP_400_BAD_REQUEST
)
def get(self, request, username, format=None): def get(self, request, username, format=None):
# Get user details # Get user details
@ -251,9 +217,7 @@ class UserDetail(APIView):
if isinstance(user, Response): if isinstance(user, Response):
return user return user
serializer = UserDetailSerializer( serializer = UserDetailSerializer(user, data=request.data, context={"request": request})
user, data=request.data, context={"request": request}
)
if serializer.is_valid(): if serializer.is_valid():
logo = request.data.get("logo") logo = request.data.get("logo")
if logo: if logo:
@ -271,9 +235,7 @@ class UserDetail(APIView):
return user return user
if not request.user.is_superuser: if not request.user.is_superuser:
return Response( return Response({"detail": "not allowed"}, status=status.HTTP_400_BAD_REQUEST)
{"detail": "not allowed"}, status=status.HTTP_400_BAD_REQUEST
)
action = request.data.get("action") action = request.data.get("action")
if action == "feature": if action == "feature":