From 6df942ac4ee82b4825900f3f1c8e5e88da088af5 Mon Sep 17 00:00:00 2001 From: Markos Gogoulos Date: Wed, 26 May 2021 18:35:21 +0300 Subject: [PATCH] format content (#198) --- actions/migrations/0002_mediaaction_media.py | 2 +- actions/migrations/0003_auto_20201201_0712.py | 6 +- actions/models.py | 11 +- cms/__init__.py | 1 + cms/celery.py | 2 + cms/custom_pagination.py | 5 +- cms/permissions.py | 1 + cms/settings.py | 7 +- cms/urls.py | 4 +- deploy/docker/local_settings.py | 4 +- files/admin.py | 8 +- files/backends.py | 4 +- files/context_processors.py | 13 +- files/feeds.py | 16 +- files/forms.py | 17 +- files/helpers.py | 42 +-- files/management_views.py | 20 +- files/methods.py | 91 ++---- files/migrations/0001_initial.py | 38 +-- files/migrations/0002_auto_20201201_0712.py | 48 +-- files/models.py | 290 +++++------------ files/permissions.py | 1 + files/serializers.py | 28 +- files/tasks.py | 148 +++------ files/urls.py | 7 +- files/views.py | 298 ++++++------------ manage.py | 6 +- setup.cfg | 2 +- uploader/fineuploader.py | 5 +- uploader/utils.py | 7 +- uploader/views.py | 15 +- users/adapter.py | 4 +- users/forms.py | 5 +- users/migrations/0001_initial.py | 64 +--- users/models.py | 45 +-- users/serializers.py | 9 +- users/urls.py | 5 +- users/validators.py | 5 +- users/views.py | 98 ++---- 39 files changed, 420 insertions(+), 962 deletions(-) diff --git a/actions/migrations/0002_mediaaction_media.py b/actions/migrations/0002_mediaaction_media.py index 0a0372a..7e12bc7 100644 --- a/actions/migrations/0002_mediaaction_media.py +++ b/actions/migrations/0002_mediaaction_media.py @@ -1,7 +1,7 @@ # Generated by Django 3.1.4 on 2020-12-01 07:12 -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/actions/migrations/0003_auto_20201201_0712.py b/actions/migrations/0003_auto_20201201_0712.py index 80178ef..596a010 100644 --- a/actions/migrations/0003_auto_20201201_0712.py +++ b/actions/migrations/0003_auto_20201201_0712.py @@ -1,8 +1,8 @@ # Generated by Django 3.1.4 on 2020-12-01 07:12 +import django.db.models.deletion from django.conf import settings from django.db import migrations, models -import django.db.models.deletion class Migration(migrations.Migration): @@ -35,8 +35,6 @@ class Migration(migrations.Migration): ), migrations.AddIndex( model_name="mediaaction", - index=models.Index( - fields=["session_key", "action"], name="actions_med_session_fac55a_idx" - ), + index=models.Index(fields=["session_key", "action"], name="actions_med_session_fac55a_idx"), ), ] diff --git a/actions/models.py b/actions/models.py index 64a6d4d..7f5d93b 100644 --- a/actions/models.py +++ b/actions/models.py @@ -1,6 +1,7 @@ from django.db import models -from users.models import User + from files.models import Media +from users.models import User USER_MEDIA_ACTIONS = ( ("like", "Like"), @@ -30,15 +31,11 @@ class MediaAction(models.Model): help_text="for not logged in users", ) - action = models.CharField( - max_length=20, choices=USER_MEDIA_ACTIONS, default="watch" - ) + action = models.CharField(max_length=20, choices=USER_MEDIA_ACTIONS, default="watch") # keeps extra info, eg on report action, why it is reported extra_info = models.TextField(blank=True, null=True) - media = models.ForeignKey( - Media, on_delete=models.CASCADE, related_name="mediaactions" - ) + media = models.ForeignKey(Media, on_delete=models.CASCADE, related_name="mediaactions") action_date = models.DateTimeField(auto_now_add=True) remote_ip = models.CharField(max_length=40, blank=True, null=True) diff --git a/cms/__init__.py b/cms/__init__.py index 1afbeb5..faa86a6 100644 --- a/cms/__init__.py +++ b/cms/__init__.py @@ -1,4 +1,5 @@ from __future__ import absolute_import + from .celery import app as celery_app __all__ = ["celery_app"] diff --git a/cms/celery.py b/cms/celery.py index 8b21f5b..a622658 100644 --- a/cms/celery.py +++ b/cms/celery.py @@ -1,5 +1,7 @@ from __future__ import absolute_import + import os + from celery import Celery os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cms.settings") diff --git a/cms/custom_pagination.py b/cms/custom_pagination.py index 170ed61..792ca40 100644 --- a/cms/custom_pagination.py +++ b/cms/custom_pagination.py @@ -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 django.core.paginator import Paginator from django.utils.functional import cached_property +from rest_framework.pagination import PageNumberPagination +from rest_framework.response import Response class FasterDjangoPaginator(Paginator): diff --git a/cms/permissions.py b/cms/permissions.py index ad5741e..8c372e8 100644 --- a/cms/permissions.py +++ b/cms/permissions.py @@ -1,5 +1,6 @@ from django.conf import settings from rest_framework import permissions + from files.methods import is_mediacms_editor, is_mediacms_manager diff --git a/cms/settings.py b/cms/settings.py index 559433c..7a4898f 100644 --- a/cms/settings.py +++ b/cms/settings.py @@ -1,4 +1,5 @@ import os + from celery.schedules import crontab DEBUG = False @@ -17,7 +18,7 @@ CAN_ADD_MEDIA = "all" PORTAL_WORKFLOW = "public" # valid values: 'light', 'dark'. -DEFAULT_THEME = "light" +DEFAULT_THEME = "light" # These are passed on every request @@ -213,9 +214,7 @@ POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY = "" CANNOT_ADD_MEDIA_MESSAGE = "" # mp4hls command, part of Bendo4 -MP4HLS_COMMAND = ( - "/home/mediacms.io/mediacms/Bento4-SDK-1-6-0-637.x86_64-unknown-linux/bin/mp4hls" -) +MP4HLS_COMMAND = "/home/mediacms.io/mediacms/Bento4-SDK-1-6-0-637.x86_64-unknown-linux/bin/mp4hls" # highly experimental, related with remote workers ADMIN_TOKEN = "c2b8e1838b6128asd333ddc5e24" diff --git a/cms/urls.py b/cms/urls.py index ce6692a..d152981 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -1,7 +1,7 @@ +import debug_toolbar +from django.conf.urls import include, url from django.contrib import admin from django.urls import path -from django.conf.urls import url, include -import debug_toolbar urlpatterns = [ url(r"^__debug__/", include(debug_toolbar.urls)), diff --git a/deploy/docker/local_settings.py b/deploy/docker/local_settings.py index b0254ef..7dbad9a 100644 --- a/deploy/docker/local_settings.py +++ b/deploy/docker/local_settings.py @@ -29,8 +29,6 @@ CACHES = { BROKER_URL = REDIS_LOCATION CELERY_RESULT_BACKEND = BROKER_URL -MP4HLS_COMMAND = ( - "/home/mediacms.io/bento4/bin/mp4hls" -) +MP4HLS_COMMAND = "/home/mediacms.io/bento4/bin/mp4hls" DEBUG = False diff --git a/files/admin.py b/files/admin.py index 65ce937..78cff2a 100644 --- a/files/admin.py +++ b/files/admin.py @@ -1,14 +1,14 @@ from django.contrib import admin from .models import ( - Media, - Encoding, - EncodeProfile, Category, Comment, - Tag, + EncodeProfile, + Encoding, Language, + Media, Subtitle, + Tag, ) diff --git a/files/backends.py b/files/backends.py index 87cdadc..ac130ef 100644 --- a/files/backends.py +++ b/files/backends.py @@ -1,9 +1,9 @@ # ffmpeg only backend -from subprocess import PIPE, Popen import locale -import re import logging +import re +from subprocess import PIPE, Popen logger = logging.getLogger(__name__) diff --git a/files/context_processors.py b/files/context_processors.py index f9297f5..997f979 100644 --- a/files/context_processors.py +++ b/files/context_processors.py @@ -1,4 +1,5 @@ from django.conf import settings + 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_FILES_NUMBER"] = settings.UPLOAD_MAX_FILES_NUMBER ret["PRE_UPLOAD_MEDIA_MESSAGE"] = settings.PRE_UPLOAD_MEDIA_MESSAGE - ret[ - "POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY" - ] = settings.POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY + ret["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_EDITOR"] = is_mediacms_editor(request.user) ret["IS_MEDIACMS_MANAGER"] = is_mediacms_manager(request.user) ret["ALLOW_RATINGS"] = settings.ALLOW_RATINGS - ret[ - "ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY" - ] = settings.ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY - ret[ - "VIDEO_PLAYER_FEATURED_VIDEO_ON_INDEX_PAGE" - ] = settings.VIDEO_PLAYER_FEATURED_VIDEO_ON_INDEX_PAGE + ret["ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY"] = 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" return ret diff --git a/files/feeds.py b/files/feeds.py index 6c3c7eb..170e8b3 100644 --- a/files/feeds.py +++ b/files/feeds.py @@ -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.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 .models import Category, Media from .stop_words import STOP_WORDS @@ -119,11 +119,7 @@ class SearchRSSFeed(Feed): elif query: # same as on files.views.MediaSearch: move this processing to a prepare_query function query = helpers.clean_query(query) - q_parts = [ - q_part.rstrip("y") - for q_part in query.split() - if q_part not in STOP_WORDS - ] + q_parts = [q_part.rstrip("y") for q_part in query.split() if q_part not in STOP_WORDS] if q_parts: query = SearchQuery(q_parts[0] + ":*", search_type="raw") for part in q_parts[1:]: diff --git a/files/forms.py b/files/forms.py index 5846df4..a36c0ca 100644 --- a/files/forms.py +++ b/files/forms.py @@ -1,6 +1,7 @@ from django import forms + +from .methods import get_next_state, is_mediacms_editor from .models import Media, Subtitle -from .methods import is_mediacms_editor, get_next_state class MultipleSelect(forms.CheckboxSelectMultiple): @@ -8,9 +9,7 @@ class MultipleSelect(forms.CheckboxSelectMultiple): class MediaForm(forms.ModelForm): - new_tags = forms.CharField( - label="Tags", help_text="a comma separated list of new tags.", required=False - ) + new_tags = forms.CharField(label="Tags", help_text="a comma separated list of new tags.", required=False) class Meta: model = Media @@ -27,7 +26,7 @@ class MediaForm(forms.ModelForm): "thumbnail_time", "reported_times", "is_reviewed", - "allow_download" + "allow_download", ) widgets = { "tags": MultipleSelect(), @@ -42,9 +41,7 @@ class MediaForm(forms.ModelForm): self.fields.pop("featured") self.fields.pop("reported_times") self.fields.pop("is_reviewed") - self.fields["new_tags"].initial = ", ".join( - [tag.title for tag in self.instance.tags.all()] - ) + self.fields["new_tags"].initial = ", ".join([tag.title for tag in self.instance.tags.all()]) def clean_uploaded_poster(self): image = self.cleaned_data.get("uploaded_poster", False) @@ -57,9 +54,7 @@ class MediaForm(forms.ModelForm): data = self.cleaned_data state = data.get("state") if state != self.initial["state"]: - self.instance.state = get_next_state( - self.user, self.initial["state"], self.instance.state - ) + self.instance.state = get_next_state(self.user, self.initial["state"], self.instance.state) media = super(MediaForm, self).save(*args, **kwargs) return media diff --git a/files/helpers.py b/files/helpers.py index e4c056c..bb050e6 100644 --- a/files/helpers.py +++ b/files/helpers.py @@ -1,19 +1,19 @@ # Kudos to Werner Robitza, AVEQ GmbH, for helping with ffmpeg # related content -import os -import math -import shutil -import tempfile -import random import hashlib -import subprocess import json +import math +import os +import random +import shutil +import subprocess +import tempfile from fractions import Fraction + import filetype from django.conf import settings - CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 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): # TODO: find a way to preserver http - https ... - return "{0}{1}".format( - settings.MEDIA_URL, filename.replace(settings.MEDIA_ROOT, "") - ) + return "{0}{1}".format(settings.MEDIA_URL, filename.replace(settings.MEDIA_ROOT, "")) def create_temp_file(suffix=None, dir=settings.TEMP_DIRECTORY): @@ -210,9 +208,7 @@ def run_command(cmd, cwd=None): cmd = cmd.split() ret = {} if cwd: - process = subprocess.Popen( - cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd - ) + process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd) else: process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = process.communicate() @@ -331,9 +327,7 @@ def media_file_info(input_file): except ValueError: hms, msec = duration_str.split(",") - total_dur = sum( - int(x) * 60 ** i for i, x in enumerate(reversed(hms.split(":"))) - ) + total_dur = sum(int(x) * 60 ** i for i, x in enumerate(reversed(hms.split(":")))) video_duration = total_dur + float("0." + msec) else: # fallback to format, eg for webm @@ -370,7 +364,7 @@ def media_file_info(input_file): input_file, ] 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) ret = { @@ -396,9 +390,7 @@ def media_file_info(input_file): hms, msec = duration_str.split(".") except ValueError: hms, msec = duration_str.split(",") - total_dur = sum( - int(x) * 60 ** i for i, x in enumerate(reversed(hms.split(":"))) - ) + total_dur = sum(int(x) * 60 ** i for i, x in enumerate(reversed(hms.split(":")))) audio_duration = total_dur + float("0." + msec) else: # fallback to format, eg for webm @@ -432,7 +424,7 @@ def media_file_info(input_file): input_file, ] 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) ret.update( @@ -660,9 +652,7 @@ def get_base_ffmpeg_command( return cmd -def produce_ffmpeg_commands( - media_file, media_info, resolution, codec, output_filename, pass_file, chunk=False -): +def produce_ffmpeg_commands(media_file, media_info, resolution, codec, output_filename, pass_file, chunk=False): try: media_info = json.loads(media_info) except BaseException: @@ -699,9 +689,7 @@ def produce_ffmpeg_commands( # else: # adjust the target frame rate if the input is fractional - target_fps = ( - src_framerate if isinstance(src_framerate, int) else math.ceil(src_framerate) - ) + target_fps = src_framerate if isinstance(src_framerate, int) else math.ceil(src_framerate) if media_info.get("video_duration") > CRF_ENCODING_NUM_SECONDS: enc_type = "crf" diff --git a/files/management_views.py b/files/management_views.py index e2dae07..3eb6f8a 100644 --- a/files/management_views.py +++ b/files/management_views.py @@ -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.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.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): @@ -189,9 +189,7 @@ class UserList(APIView): def delete(self, request, format=None): if not is_mediacms_manager(request.user): - return Response( - {"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST - ) + return Response({"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST) tokens = request.GET.get("tokens") if tokens: diff --git a/files/methods.py b/files/methods.py index 6d9220f..1cf796c 100644 --- a/files/methods.py +++ b/files/methods.py @@ -1,15 +1,17 @@ # Kudos to Werner Robitza, AVEQ GmbH, for helping with ffmpeg # related content +import itertools import logging import random -import itertools from datetime import datetime -from cms import celery_app + from django.conf import settings from django.core.cache import cache -from django.db.models import Q from django.core.mail import EmailMessage +from django.db.models import Q + +from cms import celery_app from . import models from .helpers import mask_ip @@ -48,9 +50,7 @@ def pre_save_action(media, user, session_key, action, remote_ip): if user: query = MediaAction.objects.filter(media=media, action=action, user=user) else: - query = MediaAction.objects.filter( - media=media, action=action, session_key=session_key - ) + query = MediaAction.objects.filter(media=media, action=action, session_key=session_key) query = query.order_by("-action_date") 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 # id is specified (and user is anonymous) to avoid spam # eg allow for the same remote_ip for a specific number of actions - query = ( - MediaAction.objects.filter(media=media, action=action, remote_ip=remote_ip) - .filter(user=None) - .order_by("-action_date") - ) + query = MediaAction.objects.filter(media=media, action=action, remote_ip=remote_ip).filter(user=None).order_by("-action_date") if query: query = query.first() now = datetime.now(query.action_date.tzinfo) @@ -204,11 +200,8 @@ URL: %s d["to"] = [media.user.email] notify_items.append(d) - for item in notify_items: - email = EmailMessage( - item["title"], item["msg"], settings.DEFAULT_FROM_EMAIL, item["to"] - ) + email = EmailMessage(item["title"], item["msg"], settings.DEFAULT_FROM_EMAIL, item["to"]) email.send(fail_silently=True) return True @@ -222,17 +215,9 @@ def show_recommended_media(request, limit=100): pmi = cache.get("popular_media_ids") # produced by task get_list_of_popular_media and cached if pmi: - media = list( - models.Media.objects.filter(friendly_token__in=pmi) - .filter(basic_query) - .prefetch_related("user")[:limit] - ) + media = list(models.Media.objects.filter(friendly_token__in=pmi).filter(basic_query).prefetch_related("user")[:limit]) else: - media = list( - models.Media.objects.filter(basic_query) - .order_by("-views", "-likes") - .prefetch_related("user")[:limit] - ) + media = list(models.Media.objects.filter(basic_query).order_by("-views", "-likes").prefetch_related("user")[:limit]) random.shuffle(media) return media @@ -257,11 +242,7 @@ def show_related_media_content(media, request, limit): # and include author videos in any case q_author = Q(listable=True, user=media.user) - m = list( - models.Media.objects.filter(q_author) - .order_by() - .prefetch_related("user")[:limit] - ) + m = list(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 # 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() if category: q_category = Q(listable=True, category=category) - 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] - ) + 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] m = list(itertools.chain(m, q_res)) if len(m) < limit: q_generic = Q(listable=True) - 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] - ) + 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] m = list(itertools.chain(m, q_res)) 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""" q_author = Q(listable=True, user=media.user) - m = list( - models.Media.objects.filter(q_author) - .order_by() - .prefetch_related("user")[:limit] - ) + m = list(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 # 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""" for rating in user_ratings: - user_rating = ( - models.Rating.objects.filter( - user=user, media_id=media, rating_category_id=rating.get("category_id") - ) - .only("score") - .first() - ) + user_rating = models.Rating.objects.filter(user=user, media_id=media, rating_category_id=rating.get("category_id")).only("score").first() if user_rating: rating["score"] = user_rating.score return user_ratings @@ -379,9 +342,7 @@ View it on %s media.title, media_url, ) - email = EmailMessage( - title, msg, settings.DEFAULT_FROM_EMAIL, [media.user.email] - ) + email = EmailMessage(title, msg, settings.DEFAULT_FROM_EMAIL, [media.user.email]) email.send(fail_silently=True) return True @@ -420,27 +381,17 @@ def list_tasks(): friendly_token = task_args.split()[0] profile_id = task_args.split()[1] - media = models.Media.objects.filter( - friendly_token=friendly_token - ).first() + media = models.Media.objects.filter(friendly_token=friendly_token).first() if media: - profile = models.EncodeProfile.objects.filter( - id=profile_id - ).first() + profile = models.EncodeProfile.objects.filter(id=profile_id).first() if profile: - media_profile_pairs.append( - (media.friendly_token, profile.id) - ) + media_profile_pairs.append((media.friendly_token, profile.id)) task_dict["info"] = {} task_dict["info"]["profile name"] = profile.name task_dict["info"]["media title"] = media.title - encoding = models.Encoding.objects.filter( - task_id=task.get("id") - ).first() + encoding = models.Encoding.objects.filter(task_id=task.get("id")).first() if encoding: - task_dict["info"][ - "encoding progress" - ] = encoding.progress + task_dict["info"]["encoding progress"] = encoding.progress ret[state]["tasks"].append(task_dict) ret["task_ids"] = task_ids diff --git a/files/migrations/0001_initial.py b/files/migrations/0001_initial.py index 306d9f4..3e9f82a 100644 --- a/files/migrations/0001_initial.py +++ b/files/migrations/0001_initial.py @@ -1,11 +1,13 @@ # 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 django.contrib.postgres.search +import imagekit.models.fields +from django.db import migrations, models + +import files.models + class Migration(migrations.Migration): @@ -32,9 +34,7 @@ class Migration(migrations.Migration): ("description", models.TextField(blank=True)), ( "is_global", - models.BooleanField( - default=False, help_text="global categories or user specific" - ), + models.BooleanField(default=False, help_text="global categories or user specific"), ), ( "media_count", @@ -42,9 +42,7 @@ class Migration(migrations.Migration): ), ( "thumbnail", - imagekit.models.fields.ProcessedImageField( - blank=True, upload_to=files.models.category_thumb_path - ), + imagekit.models.fields.ProcessedImageField(blank=True, upload_to=files.models.category_thumb_path), ), ( "listings_thumbnail", @@ -153,9 +151,7 @@ class Migration(migrations.Migration): ("commands", models.TextField(blank=True, help_text="commands run")), ( "chunk", - models.BooleanField( - db_index=True, default=False, help_text="is chunk?" - ), + models.BooleanField(db_index=True, default=False, help_text="is chunk?"), ), ("chunk_file_path", models.CharField(blank=True, max_length=400)), ("chunks_info", models.TextField(blank=True)), @@ -317,9 +313,7 @@ class Migration(migrations.Migration): ("likes", models.IntegerField(db_index=True, default=1)), ( "listable", - models.BooleanField( - default=False, help_text="Whether it will appear on listings" - ), + models.BooleanField(default=False, help_text="Whether it will appear on listings"), ), ( "md5sum", @@ -341,9 +335,7 @@ class Migration(migrations.Migration): ), ( "media_info", - models.TextField( - blank=True, help_text="extracted media metadata info" - ), + models.TextField(blank=True, help_text="extracted media metadata info"), ), ( "media_type", @@ -387,9 +379,7 @@ class Migration(migrations.Migration): ), ( "reported_times", - models.IntegerField( - default=0, help_text="how many time a Medis is reported" - ), + models.IntegerField(default=0, help_text="how many time a Medis is reported"), ), ( "search", @@ -485,9 +475,7 @@ class Migration(migrations.Migration): ), ( "user_featured", - models.BooleanField( - default=False, help_text="Featured by the user" - ), + models.BooleanField(default=False, help_text="Featured by the user"), ), ("video_height", models.IntegerField(default=1)), ("views", models.IntegerField(db_index=True, default=1)), diff --git a/files/migrations/0002_auto_20201201_0712.py b/files/migrations/0002_auto_20201201_0712.py index 0b8dc03..1a2310a 100644 --- a/files/migrations/0002_auto_20201201_0712.py +++ b/files/migrations/0002_auto_20201201_0712.py @@ -1,10 +1,10 @@ # Generated by Django 3.1.4 on 2020-12-01 07:12 -from django.conf import settings import django.contrib.postgres.indexes -from django.db import migrations, models import django.db.models.deletion import mptt.fields +from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): @@ -31,9 +31,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name="subtitle", name="language", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="files.language" - ), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="files.language"), ), migrations.AddField( model_name="subtitle", @@ -47,9 +45,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name="subtitle", name="user", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL - ), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), ), migrations.AddField( model_name="rating", @@ -63,37 +59,27 @@ class Migration(migrations.Migration): migrations.AddField( model_name="rating", name="rating_category", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="files.ratingcategory" - ), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="files.ratingcategory"), ), migrations.AddField( model_name="rating", name="user", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL - ), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), ), migrations.AddField( model_name="playlistmedia", name="media", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="files.media" - ), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="files.media"), ), migrations.AddField( model_name="playlistmedia", name="playlist", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="files.playlist" - ), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="files.playlist"), ), migrations.AddField( model_name="playlist", name="media", - field=models.ManyToManyField( - blank=True, through="files.PlaylistMedia", to="files.Media" - ), + field=models.ManyToManyField(blank=True, through="files.PlaylistMedia", to="files.Media"), ), migrations.AddField( model_name="playlist", @@ -173,9 +159,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name="encoding", name="profile", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="files.encodeprofile" - ), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="files.encodeprofile"), ), migrations.AddField( model_name="comment", @@ -200,9 +184,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name="comment", name="user", - field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL - ), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), ), migrations.AddField( model_name="category", @@ -216,9 +198,7 @@ class Migration(migrations.Migration): ), migrations.AddIndex( model_name="rating", - index=models.Index( - fields=["user", "media"], name="files_ratin_user_id_72ca6a_idx" - ), + index=models.Index(fields=["user", "media"], name="files_ratin_user_id_72ca6a_idx"), ), migrations.AlterUniqueTogether( name="rating", @@ -226,8 +206,6 @@ class Migration(migrations.Migration): ), migrations.AddIndex( model_name="media", - index=django.contrib.postgres.indexes.GinIndex( - fields=["search"], name="files_media_search_7194c6_gin" - ), + index=django.contrib.postgres.indexes.GinIndex(fields=["search"], name="files_media_search_7194c6_gin"), ), ] diff --git a/files/models.py b/files/models.py index 41b3bf6..554c972 100644 --- a/files/models.py +++ b/files/models.py @@ -1,28 +1,27 @@ +import json import logging -import uuid import os +import random import re import tempfile -import random -import json +import uuid + 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.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 mptt.models import MPTTModel, TreeForeignKey - -from imagekit.processors import ResizeToFit +from django.core.exceptions import ValidationError +from django.core.files import File +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.processors import ResizeToFit +from mptt.models import MPTTModel, TreeForeignKey from . import helpers 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): """Helper function to place original media file""" file_name = "{0}.{1}".format(instance.uid.hex, helpers.get_file_name(filename)) - return settings.MEDIA_UPLOAD_DIR + "user/{0}/{1}".format( - instance.user.username, file_name - ) + return settings.MEDIA_UPLOAD_DIR + "user/{0}/{1}".format(instance.user.username, file_name) def encoding_media_file_path(instance, filename): """Helper function to place encoded media file""" - file_name = "{0}.{1}".format( - 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 - ) + file_name = "{0}.{1}".format(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) def original_thumbnail_file_path(instance, filename): """Helper function to place original media thumbnail file""" - return settings.THUMBNAIL_UPLOAD_DIR + "user/{0}/{1}".format( - instance.user.username, filename - ) + return settings.THUMBNAIL_UPLOAD_DIR + "user/{0}/{1}".format(instance.user.username, filename) def subtitles_file_path(instance, filename): """Helper function to place subtitle file""" - return settings.SUBTITLES_UPLOAD_DIR + "user/{0}/{1}".format( - instance.media.user.username, filename - ) + return settings.SUBTITLES_UPLOAD_DIR + "user/{0}/{1}".format(instance.media.user.username, filename) def category_thumb_path(instance, filename): @@ -130,17 +119,11 @@ def category_thumb_path(instance, filename): class Media(models.Model): """The most important model for MediaCMS""" - add_date = models.DateTimeField( - "Date produced", blank=True, null=True, db_index=True - ) + add_date = models.DateTimeField("Date produced", blank=True, null=True, db_index=True) - allow_download = models.BooleanField( - default=True, help_text="Whether option to download media is shown" - ) + allow_download = models.BooleanField(default=True, help_text="Whether option to download media is shown") - category = models.ManyToManyField( - "Category", blank=True, help_text="Media can be part of one or more categories" - ) + category = models.ManyToManyField("Category", blank=True, help_text="Media can be part of one or more categories") channel = models.ForeignKey( "users.Channel", @@ -158,13 +141,9 @@ class Media(models.Model): edit_date = models.DateTimeField(auto_now=True) - enable_comments = models.BooleanField( - default=True, help_text="Whether comments will be allowed for this media" - ) + enable_comments = models.BooleanField(default=True, help_text="Whether comments will be allowed for this media") - encoding_status = models.CharField( - max_length=20, choices=MEDIA_ENCODING_STATUS, default="pending", db_index=True - ) + encoding_status = models.CharField(max_length=20, choices=MEDIA_ENCODING_STATUS, default="pending", db_index=True) featured = models.BooleanField( default=False, @@ -172,13 +151,9 @@ class Media(models.Model): help_text="Whether media is globally featured by a MediaCMS editor", ) - friendly_token = models.CharField( - blank=True, max_length=12, db_index=True, help_text="Identifier for the Media" - ) + friendly_token = models.CharField(blank=True, max_length=12, db_index=True, help_text="Identifier for the Media") - hls_file = models.CharField( - max_length=1000, blank=True, help_text="Path to HLS file for videos" - ) + hls_file = models.CharField(max_length=1000, blank=True, help_text="Path to HLS file for videos") is_reviewed = models.BooleanField( 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", ) - license = models.ForeignKey( - "License", on_delete=models.CASCADE, db_index=True, blank=True, null=True - ) + license = models.ForeignKey("License", on_delete=models.CASCADE, db_index=True, blank=True, null=True) likes = models.IntegerField(db_index=True, default=1) - listable = models.BooleanField( - default=False, help_text="Whether it will appear on listings" - ) + listable = models.BooleanField(default=False, help_text="Whether it will appear on listings") - md5sum = models.CharField( - max_length=50, blank=True, null=True, help_text="Not exposed, used internally" - ) + md5sum = models.CharField(max_length=50, blank=True, null=True, help_text="Not exposed, used internally") media_file = models.FileField( "media file", @@ -217,9 +186,7 @@ class Media(models.Model): default="video", ) - password = models.CharField( - max_length=100, blank=True, help_text="password for private media" - ) + password = models.CharField(max_length=100, blank=True, help_text="password for private media") preview_file_path = models.CharField( max_length=500, @@ -243,9 +210,7 @@ class Media(models.Model): help_text="Rating category, if media Rating is allowed", ) - reported_times = models.IntegerField( - default=0, help_text="how many time a Medis is reported" - ) + reported_times = models.IntegerField(default=0, help_text="how many time a Medis is reported") search = SearchVectorField( null=True, @@ -274,13 +239,9 @@ class Media(models.Model): help_text="state of Media", ) - tags = models.ManyToManyField( - "Tag", blank=True, help_text="select one or more out of the existing tags" - ) + tags = models.ManyToManyField("Tag", blank=True, help_text="select one or more out of the existing tags") - title = models.CharField( - max_length=100, help_text="media title", blank=True, db_index=True - ) + title = models.CharField(max_length=100, help_text="media title", blank=True, db_index=True) thumbnail = ProcessedImageField( upload_to=original_thumbnail_file_path, @@ -292,13 +253,9 @@ class Media(models.Model): help_text="media extracted small thumbnail, shown on listings", ) - thumbnail_time = models.FloatField( - blank=True, null=True, help_text="Time on video that a thumbnail will be taken" - ) + thumbnail_time = models.FloatField(blank=True, null=True, help_text="Time on video that a thumbnail will be taken") - uid = models.UUIDField( - unique=True, default=uuid.uuid4, help_text="A unique identifier for the Media" - ) + uid = models.UUIDField(unique=True, default=uuid.uuid4, help_text="A unique identifier for the Media") uploaded_thumbnail = ProcessedImageField( upload_to=original_thumbnail_file_path, @@ -321,9 +278,7 @@ class Media(models.Model): max_length=500, ) - user = models.ForeignKey( - "users.User", on_delete=models.CASCADE, help_text="user that uploads the media" - ) + user = models.ForeignKey("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") @@ -406,11 +361,7 @@ class Media(models.Model): self.state = helpers.get_default_state(user=self.user) # condition to appear on listings - if ( - self.state == "public" - and self.encoding_status == "success" - and self.is_reviewed == True - ): + if self.state == "public" and self.encoding_status == "success" and self.is_reviewed is True: self.listable = True else: self.listable = False @@ -419,10 +370,7 @@ class Media(models.Model): # produce a thumbnail out of an uploaded poster # will run only when a poster is uploaded for the first time - if ( - self.uploaded_poster - and self.uploaded_poster != self.__original_uploaded_poster - ): + if self.uploaded_poster and self.uploaded_poster != self.__original_uploaded_poster: with open(self.uploaded_poster.path, "rb") as f: # set this otherwise gets to infinite loop @@ -458,9 +406,7 @@ class Media(models.Model): ] items = [item for item in items if item] text = " ".join(items) - text = " ".join( - [token for token in text.lower().split(" ") if token not in STOP_WORDS] - ) + text = " ".join([token for token in text.lower().split(" ") if token not in STOP_WORDS]) sql_code = """ UPDATE {db_table} SET search = to_tsvector( @@ -561,9 +507,7 @@ class Media(models.Model): if self.media_type == "image": with open(self.media_file.path, "rb") as f: myfile = File(f) - thumbnail_name = ( - helpers.get_file_name(self.media_file.path) + ".jpg" - ) + thumbnail_name = helpers.get_file_name(self.media_file.path) + ".jpg" self.thumbnail.save(content=myfile, name=thumbnail_name) self.poster.save(content=myfile, name=thumbnail_name) return True @@ -585,9 +529,7 @@ class Media(models.Model): command = [ settings.FFMPEG_COMMAND, "-ss", - str( - thumbnail_time - ), # -ss need to be firt here otherwise time taken is huge + str(thumbnail_time), # -ss need to be firt here otherwise time taken is huge "-i", self.media_file.path, "-vframes", @@ -650,10 +592,7 @@ class Media(models.Model): for profile in profiles: if profile.extension != "gif": if self.video_height and self.video_height < profile.resolution: - if ( - profile.resolution - not in settings.MINIMUM_RESOLUTIONS_TO_ENCODE - ): + if profile.resolution not in settings.MINIMUM_RESOLUTIONS_TO_ENCODE: continue encoding = Encoding(media=self, profile=profile) encoding.save() @@ -688,12 +627,7 @@ class Media(models.Model): self.save(update_fields=["encoding_status", "listable"]) - if ( - encoding - and encoding.status == "success" - and encoding.profile.codec == "h264" - and action == "add" - ): + if encoding and encoding.status == "success" and encoding.profile.codec == "h264" and action == "add": from . import tasks tasks.create_hls(self.friendly_token) @@ -704,10 +638,7 @@ class Media(models.Model): """Set encoding_status for videos Set success if at least one mp4 exists """ - mp4_statuses = set( - encoding.status - for encoding in self.encodings.filter(profile__extension="mp4", chunk=False) - ) + mp4_statuses = set(encoding.status for encoding in self.encodings.filter(profile__extension="mp4", chunk=False)) if not mp4_statuses: encoding_status = "pending" @@ -752,12 +683,8 @@ class Media(models.Model): extra.append(encoding.profile.codec) for codec in extra: ret[resolution][codec] = {} - v = self.encodings.filter(chunk=True, profile__codec=codec).values( - "progress" - ) - ret[resolution][codec]["progress"] = ( - sum([p["progress"] for p in v]) / v.count() - ) + v = self.encodings.filter(chunk=True, profile__codec=codec).values("progress") + ret[resolution][codec]["progress"] = sum([p["progress"] for p in v]) / v.count() # TODO; status/logs/errors return ret @@ -897,19 +824,13 @@ class Media(models.Model): for iframe_playlist in m3u8_obj.iframe_playlists: uri = os.path.join(p, iframe_playlist.uri) if os.path.exists(uri): - resolution = iframe_playlist.iframe_stream_info.resolution[ - 1 - ] - res["{}_iframe".format(resolution)] = helpers.url_from_path( - uri - ) + resolution = iframe_playlist.iframe_stream_info.resolution[1] + res["{}_iframe".format(resolution)] = helpers.url_from_path(uri) for playlist in m3u8_obj.playlists: uri = os.path.join(p, playlist.uri) if os.path.exists(uri): resolution = playlist.stream_info.resolution[1] - res[ - "{}_playlist".format(resolution) - ] = helpers.url_from_path(uri) + res["{}_playlist".format(resolution)] = helpers.url_from_path(uri) return res @property @@ -930,9 +851,7 @@ class Media(models.Model): if edit: return reverse("edit_media") + "?m={0}".format(self.friendly_token) if api: - return reverse( - "api_get_media", kwargs={"friendly_token": self.friendly_token} - ) + return reverse("api_get_media", kwargs={"friendly_token": self.friendly_token}) else: return reverse("get_media") + "?m={0}".format(self.friendly_token) @@ -988,13 +907,9 @@ class Category(models.Model): description = models.TextField(blank=True) - user = models.ForeignKey( - "users.User", on_delete=models.CASCADE, blank=True, null=True - ) + user = models.ForeignKey("users.User", on_delete=models.CASCADE, blank=True, null=True) - is_global = models.BooleanField( - default=False, help_text="global categories or user specific" - ) + is_global = models.BooleanField(default=False, help_text="global categories or user specific") media_count = models.IntegerField(default=0, help_text="number of media") @@ -1006,9 +921,7 @@ class Category(models.Model): blank=True, ) - listings_thumbnail = models.CharField( - max_length=400, blank=True, null=True, help_text="Thumbnail to show on listings" - ) + listings_thumbnail = models.CharField(max_length=400, blank=True, null=True, help_text="Thumbnail to show on listings") def __str__(self): return self.title @@ -1039,11 +952,7 @@ class Category(models.Model): if self.thumbnail: return helpers.url_from_path(self.thumbnail.path) - media = ( - Media.objects.filter(category=self, state="public") - .order_by("-views") - .first() - ) + media = Media.objects.filter(category=self, state="public").order_by("-views").first() if media: return media.thumbnail_url @@ -1061,9 +970,7 @@ class Tag(models.Model): title = models.CharField(max_length=100, unique=True, db_index=True) - user = models.ForeignKey( - "users.User", on_delete=models.CASCADE, blank=True, null=True - ) + user = models.ForeignKey("users.User", on_delete=models.CASCADE, blank=True, null=True) 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) def update_tag_media(self): - self.media_count = Media.objects.filter( - state="public", is_reviewed=True, tags=self - ).count() + self.media_count = Media.objects.filter(state="public", is_reviewed=True, tags=self).count() self.save(update_fields=["media_count"]) return True @@ -1102,9 +1007,7 @@ class Tag(models.Model): def thumbnail_url(self): if self.listings_thumbnail: return self.listings_thumbnail - media = ( - Media.objects.filter(tags=self, state="public").order_by("-views").first() - ) + media = Media.objects.filter(tags=self, state="public").order_by("-views").first() if media: return media.thumbnail_url @@ -1154,9 +1057,7 @@ class Encoding(models.Model): media = models.ForeignKey(Media, on_delete=models.CASCADE, related_name="encodings") - media_file = models.FileField( - "encoding file", upload_to=encoding_media_file_path, blank=True, max_length=500 - ) + media_file = models.FileField("encoding file", upload_to=encoding_media_file_path, blank=True, max_length=500) profile = models.ForeignKey(EncodeProfile, on_delete=models.CASCADE) @@ -1168,9 +1069,7 @@ class Encoding(models.Model): size = models.CharField(max_length=20, blank=True) - status = models.CharField( - max_length=20, choices=MEDIA_ENCODING_STATUS, default="pending" - ) + status = models.CharField(max_length=20, choices=MEDIA_ENCODING_STATUS, default="pending") temp_file = models.CharField(max_length=400, blank=True) @@ -1305,9 +1204,7 @@ class Rating(models.Model): unique_together = ("user", "media", "rating_category") def __str__(self): - return "{0}, rate for {1} for category {2}".format( - self.user.username, self.media.title, self.rating_category.title - ) + return "{0}, rate for {1} for category {2}".format(self.user.username, self.media.title, self.rating_category.title) class Playlist(models.Model): @@ -1325,9 +1222,7 @@ class Playlist(models.Model): uid = models.UUIDField(unique=True, default=uuid.uuid4) - user = models.ForeignKey( - "users.User", on_delete=models.CASCADE, db_index=True, related_name="playlists" - ) + user = models.ForeignKey("users.User", on_delete=models.CASCADE, db_index=True, related_name="playlists") def __str__(self): return self.title @@ -1338,13 +1233,9 @@ class Playlist(models.Model): def get_absolute_url(self, api=False): if api: - return reverse( - "api_get_playlist", kwargs={"friendly_token": self.friendly_token} - ) + return reverse("api_get_playlist", kwargs={"friendly_token": self.friendly_token}) else: - return reverse( - "get_playlist", kwargs={"friendly_token": self.friendly_token} - ) + return reverse("get_playlist", kwargs={"friendly_token": self.friendly_token}) @property def url(self): @@ -1411,13 +1302,9 @@ class Comment(MPTTModel): add_date = models.DateTimeField(auto_now_add=True) - media = models.ForeignKey( - Media, on_delete=models.CASCADE, db_index=True, related_name="comments" - ) + media = models.ForeignKey(Media, on_delete=models.CASCADE, db_index=True, related_name="comments") - parent = TreeForeignKey( - "self", on_delete=models.CASCADE, null=True, blank=True, related_name="children" - ) + parent = TreeForeignKey("self", on_delete=models.CASCADE, null=True, blank=True, related_name="children") 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 chunks_paths = [f.media_file.path for f in chunks] - with tempfile.TemporaryDirectory( - dir=settings.TEMP_DIRECTORY - ) as temp_dir: + with tempfile.TemporaryDirectory(dir=settings.TEMP_DIRECTORY) as temp_dir: seg_file = helpers.create_temp_file(suffix=".txt", dir=temp_dir) - tf = helpers.create_temp_file( - suffix=".{0}".format(instance.profile.extension), dir=temp_dir - ) + tf = helpers.create_temp_file(suffix=".{0}".format(instance.profile.extension), dir=temp_dir) with open(seg_file, "w") as ff: for f in chunks_paths: ff.write("file {}\n".format(f)) @@ -1602,9 +1485,7 @@ def encoding_file_save(sender, instance, created, **kwargs): progress=100, ) all_logs = "\n".join([st.logs for st in chunks]) - encoding.logs = "{0}\n{1}\n{2}".format( - chunks_paths, stdout, all_logs - ) + encoding.logs = "{0}\n{1}\n{2}".format(chunks_paths, stdout, all_logs) workers = list(set([st.worker for st in chunks])) 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 # will be changed - who = Encoding.objects.filter( - media=encoding.media, profile=encoding.profile - ).exclude(id=encoding.id) + who = Encoding.objects.filter(media=encoding.media, profile=encoding.profile).exclude(id=encoding.id) who.delete() else: encoding.delete() @@ -1652,13 +1531,9 @@ def encoding_file_save(sender, instance, created, **kwargs): instance.media.post_encode_actions(encoding=instance, action="add") elif instance.chunk and instance.status == "fail": - encoding = Encoding( - media=instance.media, profile=instance.profile, status="fail", progress=100 - ) + encoding = Encoding(media=instance.media, profile=instance.profile, status="fail", progress=100) - chunks = Encoding.objects.filter( - media=instance.media, chunks_info=instance.chunks_info, chunk=True - ).order_by("add_date") + chunks = Encoding.objects.filter(media=instance.media, chunks_info=instance.chunks_info, chunk=True).order_by("add_date") 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.save() - who = Encoding.objects.filter( - media=encoding.media, profile=encoding.profile - ).exclude(id=encoding.id) + who = Encoding.objects.filter(media=encoding.media, profile=encoding.profile).exclude(id=encoding.id) who.delete() 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"]: instance.media.post_encode_actions(encoding=instance, action="add") - encodings = set( - [ - encoding.status - for encoding in Encoding.objects.filter(media=instance.media) - ] - ) + encodings = set([encoding.status for encoding in Encoding.objects.filter(media=instance.media)]) if ("running" in encodings) or ("pending" in encodings): return - workers = list( - set( - [ - encoding.worker - for encoding in Encoding.objects.filter(media=instance.media) - ] - ) - ) + workers = list(set([encoding.worker for encoding in Encoding.objects.filter(media=instance.media)])) @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") # delete local chunks, and remote chunks + media file. Only when the # last encoding of a media is complete - diff --git a/files/permissions.py b/files/permissions.py index 5601166..480e91f 100644 --- a/files/permissions.py +++ b/files/permissions.py @@ -1,4 +1,5 @@ from rest_framework import permissions + from .methods import is_mediacms_editor diff --git a/files/serializers.py b/files/serializers.py index 28da7be..2752d88 100644 --- a/files/serializers.py +++ b/files/serializers.py @@ -1,6 +1,6 @@ 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 @@ -18,9 +18,7 @@ class MediaSerializer(serializers.ModelSerializer): return self.context["request"].build_absolute_uri(obj.get_absolute_url()) def get_api_url(self, obj): - return self.context["request"].build_absolute_uri( - obj.get_absolute_url(api=True) - ) + return self.context["request"].build_absolute_uri(obj.get_absolute_url(api=True)) def get_thumbnail_url(self, obj): if obj.thumbnail_url: @@ -210,16 +208,7 @@ class PlaylistSerializer(serializers.ModelSerializer): class Meta: model = Playlist read_only_fields = ("add_date", "user") - fields = ( - "add_date", - "title", - "description", - "user", - "media_count", - "url", - "api_url", - "thumbnail_url" - ) + fields = ("add_date", "title", "description", "user", "media_count", "url", "api_url", "thumbnail_url") class PlaylistDetailSerializer(serializers.ModelSerializer): @@ -228,16 +217,7 @@ class PlaylistDetailSerializer(serializers.ModelSerializer): class Meta: model = Playlist read_only_fields = ("add_date", "user") - fields = ( - "title", - "add_date", - "user_thumbnail_url", - "description", - "user", - "media_count", - "url", - "thumbnail_url" - ) + fields = ("title", "add_date", "user_thumbnail_url", "description", "user", "media_count", "url", "thumbnail_url") class CommentSerializer(serializers.ModelSerializer): diff --git a/files/tasks.py b/files/tasks.py index 0c23c1c..8985dbd 100644 --- a/files/tasks.py +++ b/files/tasks.py @@ -1,41 +1,40 @@ -import re -import os import json -import subprocess -from datetime import datetime, timedelta -import tempfile +import os +import re import shutil -from django.core.cache import cache -from django.conf import settings -from django.core.files import File -from django.db.models import Q +import subprocess +import tempfile +from datetime import datetime, timedelta from celery import Task from celery.decorators import task -from celery.utils.log import get_task_logger from celery.exceptions import SoftTimeLimitExceeded - -from celery.task.control import revoke 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 .exceptions import VideoEncodingError from .helpers import ( calculate_seconds, - rm_file, create_temp_file, get_file_name, get_file_type, media_file_info, - run_command, produce_ffmpeg_commands, produce_friendly_token, + rm_file, + run_command, ) - -from actions.models import MediaAction, USER_MEDIA_ACTIONS -from users.models import User -from .models import Encoding, EncodeProfile, Media, Category, Rating, Tag -from .methods import list_tasks, pre_save_action, notify_users +from .methods import list_tasks, notify_users, pre_save_action +from .models import Category, EncodeProfile, Encoding, Media, Rating, Tag logger = get_task_logger(__name__) @@ -83,10 +82,7 @@ def chunkize_media(self, friendly_token, profiles, force=True): chunks.append(ch[0]) if not chunks: # command completely failed to segment file.putting to normal encode - logger.info( - "Failed to break file {0} in chunks." - " Putting to normal encode queue".format(friendly_token) - ) + logger.info("Failed to break file {0} in chunks." " Putting to normal encode queue".format(friendly_token)) for profile in profiles: if media.video_height and media.video_height < profile.resolution: 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.save() enc_url = settings.SSL_FRONTEND_HOST + encoding.get_absolute_url() - encode_media.delay( - friendly_token, profile.id, encoding.id, enc_url, force=force - ) + encode_media.delay(friendly_token, profile.id, encoding.id, enc_url, force=force) return False 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, ) - logger.info( - "got {0} chunks and will encode to {1} profiles".format( - len(chunks), to_profiles - ) - ) + logger.info("got {0} chunks and will encode to {1} profiles".format(len(chunks), to_profiles)) return True @@ -180,11 +170,7 @@ def encode_media( ): """Encode a media to given profile, using ffmpeg, storing progress""" - logger.info( - "Encode Media started, friendly token {0}, profile id {1}, force {2}".format( - friendly_token, profile_id, force - ) - ) + logger.info("Encode Media started, friendly token {0}, profile id {1}, force {2}".format(friendly_token, profile_id, force)) if 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 # it will always run since chunk_file_path is always different # thus find a better way for this check - if ( - Encoding.objects.filter( - media=media, profile=profile, chunk_file_path=chunk_file_path - ).count() - > 1 - and force == False - ): + if Encoding.objects.filter(media=media, profile=profile, chunk_file_path=chunk_file_path).count() > 1 and force is False: Encoding.objects.filter(id=encoding_id).delete() return False else: @@ -230,19 +210,14 @@ def encode_media( chunk_file_path=chunk_file_path, ) else: - if ( - Encoding.objects.filter(media=media, profile=profile).count() > 1 - and force is False - ): + if Encoding.objects.filter(media=media, profile=profile).count() > 1 and force is False: Encoding.objects.filter(id=encoding_id).delete() return False else: try: encoding = Encoding.objects.get(id=encoding_id) encoding.status = "running" - Encoding.objects.filter(media=media, profile=profile).exclude( - id=encoding_id - ).delete() + Encoding.objects.filter(media=media, profile=profile).exclude(id=encoding_id).delete() except BaseException: encoding = Encoding(media=media, profile=profile, status="running") @@ -287,7 +262,7 @@ def encode_media( else: original_media_path = media.media_file.path - #if not media.duration: + # if not media.duration: # encoding.status = "fail" # encoding.save(update_fields=["status"]) # return False @@ -337,9 +312,7 @@ def encode_media( if n_times % 60 == 0: encoding.progress = percent try: - encoding.save( - update_fields=["progress", "update_date"] - ) + encoding.save(update_fields=["progress", "update_date"]) logger.info("Saved {0}".format(round(percent, 2))) except BaseException: pass @@ -383,18 +356,12 @@ def encode_media( with open(tf, "rb") as f: myfile = File(f) - output_name = "{0}.{1}".format( - get_file_name(original_media_path), profile.extension - ) + output_name = "{0}.{1}".format(get_file_name(original_media_path), profile.extension) encoding.media_file.save(content=myfile, name=output_name) - encoding.total_run_time = ( - encoding.update_date - encoding.add_date - ).seconds + encoding.total_run_time = (encoding.update_date - encoding.add_date).seconds try: - encoding.save( - update_fields=["status", "logs", "progress", "total_run_time"] - ) + encoding.save(update_fields=["status", "logs", "progress", "total_run_time"]) # this will raise a django.db.utils.DatabaseError error when task is revoked, # since we delete the encoding at that stage except BaseException: @@ -457,18 +424,14 @@ def create_hls(friendly_token): p = media.uid.hex output_dir = os.path.join(settings.HLS_DIR, p) - encodings = media.encodings.filter( - profile__extension="mp4", status="success", chunk=False, profile__codec="h264" - ) + encodings = media.encodings.filter(profile__extension="mp4", status="success", chunk=False, profile__codec="h264") if encodings: existing_output_dir = None if os.path.exists(output_dir): existing_output_dir = output_dir 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]) - cmd = "{0} --segment-duration=4 --output-dir={1} {2}".format( - settings.MP4HLS_COMMAND, output_dir, files - ) + cmd = "{0} --segment-duration=4 --output-dir={1} {2}".format(settings.MP4HLS_COMMAND, output_dir, files) ret = subprocess.run(cmd, stdout=subprocess.PIPE, shell=True) if existing_output_dir: # override content with -T ! @@ -515,11 +478,7 @@ def check_running_states(): def check_media_states(): # Experimental - unused # check encoding status of not success media - media = Media.objects.filter( - Q(encoding_status="running") - | Q(encoding_status="fail") - | Q(encoding_status="pending") - ) + media = Media.objects.filter(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())) @@ -564,11 +523,7 @@ def check_pending_states(): media.encode(profiles=[profile], force=False) changed += 1 if changed: - logger.info( - "set to the encode queue {0} encodings that were on pending state".format( - changed - ) - ) + logger.info("set to the encode queue {0} encodings that were on pending state".format(changed)) return True @@ -602,6 +557,7 @@ def clear_sessions(): try: from importlib import import_module + from django.conf import settings engine = import_module(settings.SESSION_ENGINE) @@ -612,9 +568,7 @@ def clear_sessions(): @task(name="save_user_action", queue="short_tasks") -def save_user_action( - user_or_session, friendly_token=None, action="watch", extra_info=None -): +def save_user_action(user_or_session, friendly_token=None, action="watch", extra_info=None): """Short task that saves a user action""" if action not in VALID_USER_ACTIONS: @@ -652,9 +606,7 @@ def save_user_action( if user: MediaAction.objects.filter(user=user, media=media, action="watch").delete() else: - MediaAction.objects.filter( - session_key=session_key, media=media, action="watch" - ).delete() + MediaAction.objects.filter(session_key=session_key, media=media, action="watch").delete() if action == "rate": try: score = extra_info.get("score") @@ -663,9 +615,7 @@ def save_user_action( # TODO: better error handling? return False try: - rating = Rating.objects.filter( - user=user, media=media, rating_category_id=rating_category - ).first() + rating = Rating.objects.filter(user=user, media=media, rating_category_id=rating_category).first() if rating: rating.score = score rating.save(update_fields=["score"]) @@ -735,14 +685,10 @@ def get_list_of_popular_media(): for media in media_x: ft = media["friendly_token"] - num = MediaAction.objects.filter( - action_date__gte=period_x, action="watch", media__friendly_token=ft - ).count() + num = MediaAction.objects.filter(action_date__gte=period_x, action="watch", media__friendly_token=ft).count() if num: valid_media_x[ft] = num - num = MediaAction.objects.filter( - action_date__gte=period_y, action="like", media__friendly_token=ft - ).count() + num = MediaAction.objects.filter(action_date__gte=period_y, action="like", media__friendly_token=ft).count() if num: valid_media_y[ft] = num @@ -767,12 +713,7 @@ def update_listings_thumbnails(): saved = 0 qs = Category.objects.filter().order_by("-media_count") for object in qs: - media = ( - Media.objects.exclude(friendly_token__in=used_media) - .filter(category=object, state="public", is_reviewed=True) - .order_by("-views") - .first() - ) + media = Media.objects.exclude(friendly_token__in=used_media).filter(category=object, state="public", is_reviewed=True).order_by("-views").first() if media: object.listings_thumbnail = media.thumbnail_url object.save(update_fields=["listings_thumbnail"]) @@ -785,12 +726,7 @@ def update_listings_thumbnails(): saved = 0 qs = Tag.objects.filter().order_by("-media_count") for object in qs: - media = ( - Media.objects.exclude(friendly_token__in=used_media) - .filter(tags=object, state="public", is_reviewed=True) - .order_by("-views") - .first() - ) + media = Media.objects.exclude(friendly_token__in=used_media).filter(tags=object, state="public", is_reviewed=True).order_by("-views").first() if media: object.listings_thumbnail = media.thumbnail_url object.save(update_fields=["listings_thumbnail"]) diff --git a/files/urls.py b/files/urls.py index 4470c31..55c22b6 100644 --- a/files/urls.py +++ b/files/urls.py @@ -1,10 +1,9 @@ -from django.conf.urls.static import static 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 . import views -from . import management_views +from . import management_views, views from .feeds import IndexRSSFeed, SearchRSSFeed urlpatterns = [ diff --git a/files/views.py b/files/views.py index d693d91..41dcd6c 100644 --- a/files/views.py +++ b/files/views.py @@ -1,69 +1,67 @@ from datetime import datetime, timedelta -from django.shortcuts import render -from django.http import HttpResponseRedirect -from django.conf import settings -from django.shortcuts import get_object_or_404 -from django.db.models import Q -from django.contrib.auth.decorators import login_required -from django.contrib import messages -from django.template.defaultfilters import slugify -from django.core.mail import EmailMessage -from django.contrib.postgres.search import SearchQuery - -from rest_framework import permissions -from rest_framework.views import APIView -from rest_framework.response import Response -from rest_framework.settings import api_settings -from rest_framework.exceptions import PermissionDenied -from rest_framework import status -from rest_framework.parsers import ( - JSONParser, - MultiPartParser, - FileUploadParser, - FormParser, -) from celery.task.control import revoke -from cms.permissions import IsAuthorizedToAdd, IsUserOrEditor -from cms.permissions import user_allowed_to_upload -from cms.custom_pagination import FastPaginationWithoutCount -from actions.models import MediaAction, USER_MEDIA_ACTIONS -from users.models import User -from .helpers import produce_ffmpeg_commands, clean_query -from .models import ( - Media, - EncodeProfile, - Encoding, - Playlist, - PlaylistMedia, - Comment, - Category, - Tag, +from django.conf import settings +from django.contrib import messages +from django.contrib.auth.decorators import login_required +from django.contrib.postgres.search import SearchQuery +from django.core.mail import EmailMessage +from django.db.models import Q +from django.http import HttpResponseRedirect +from django.shortcuts import get_object_or_404, render +from django.template.defaultfilters import slugify +from rest_framework import permissions, status +from rest_framework.exceptions import PermissionDenied +from rest_framework.parsers import ( + FileUploadParser, + FormParser, + JSONParser, + MultiPartParser, ) -from .forms import MediaForm, ContactForm, SubtitleForm -from .tasks import save_user_action +from rest_framework.response import Response +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 ( - list_tasks, get_user_or_session, - show_recommended_media, - show_related_media, is_mediacms_editor, is_mediacms_manager, - update_user_ratings, + list_tasks, 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 ( - MediaSerializer, CategorySerializer, - TagSerializer, - SingleMediaSerializer, + CommentSerializer, EncodeProfileSerializer, MediaSearchSerializer, - PlaylistSerializer, + MediaSerializer, PlaylistDetailSerializer, - CommentSerializer, + PlaylistSerializer, + SingleMediaSerializer, + TagSerializer, ) from .stop_words import STOP_WORDS +from .tasks import save_user_action VALID_USER_ACTIONS = [action for action, name in USER_MEDIA_ACTIONS] @@ -86,11 +84,7 @@ def add_subtitle(request): if not media: return HttpResponseRedirect("/") - if not ( - request.user == media.user - or is_mediacms_editor(request.user) - or is_mediacms_manager(request.user) - ): + if not (request.user == media.user or is_mediacms_editor(request.user) or is_mediacms_manager(request.user)): return HttpResponseRedirect("/") if request.method == "POST": @@ -175,11 +169,7 @@ def edit_media(request): if not media: return HttpResponseRedirect("/") - if not ( - request.user == media.user - or is_mediacms_editor(request.user) - or is_mediacms_manager(request.user) - ): + if not (request.user == media.user or is_mediacms_editor(request.user) or is_mediacms_manager(request.user)): return HttpResponseRedirect("/") if request.method == "POST": form = MediaForm(request.user, request.POST, request.FILES, instance=media) @@ -342,9 +332,7 @@ def view_media(request): return render(request, "cms/media.html", context) user_or_session = get_user_or_session(request) - save_user_action.delay( - user_or_session, friendly_token=friendly_token, action="watch" - ) + save_user_action.delay(user_or_session, friendly_token=friendly_token, action="watch") context = {} context["media"] = friendly_token context["media_object"] = media @@ -354,11 +342,7 @@ def view_media(request): context["CAN_DELETE_COMMENTS"] = False if request.user.is_authenticated: - if ( - (media.user.id == request.user.id) - or is_mediacms_editor(request.user) - or is_mediacms_manager(request.user) - ): + if (media.user.id == request.user.id) or is_mediacms_editor(request.user) or is_mediacms_manager(request.user): context["CAN_DELETE_MEDIA"] = True context["CAN_EDIT_MEDIA"] = True context["CAN_DELETE_COMMENTS"] = True @@ -443,33 +427,21 @@ class MediaDetail(APIView): def get_object(self, friendly_token, password=None): try: - media = ( - Media.objects.select_related("user") - .prefetch_related("encodings__profile") - .get(friendly_token=friendly_token) - ) + media = Media.objects.select_related("user").prefetch_related("encodings__profile").get(friendly_token=friendly_token) # this need be explicitly called, and will call # has_object_permission() after has_permission has succeeded self.check_object_permissions(self.request, media) - if media.state == "private" and not ( - self.request.user == media.user or is_mediacms_editor(self.request.user) - ): - if ( - (not password) - or (not media.password) - or (password != media.password) - ): + if media.state == "private" and not (self.request.user == media.user or is_mediacms_editor(self.request.user)): + if (not password) or (not media.password) or (password != media.password): return Response( {"detail": "media is private"}, status=status.HTTP_401_UNAUTHORIZED, ) return media except PermissionDenied: - return Response( - {"detail": "bad permissions"}, status=status.HTTP_401_UNAUTHORIZED - ) + return Response({"detail": "bad permissions"}, status=status.HTTP_401_UNAUTHORIZED) except BaseException: return Response( {"detail": "media file does not exist"}, @@ -488,23 +460,15 @@ class MediaDetail(APIView): related_media = [] else: related_media = show_related_media(media, request=request, limit=100) - related_media_serializer = MediaSerializer( - related_media, many=True, context={"request": request} - ) + related_media_serializer = MediaSerializer(related_media, many=True, context={"request": request}) related_media = related_media_serializer.data ret = serializer.data # update rattings info with user specific ratings # eg user has already rated for this media # this only affects user rating and only if enabled - if ( - settings.ALLOW_RATINGS - and ret.get("ratings_info") - and not request.user.is_anonymous - ): - ret["ratings_info"] = update_user_ratings( - request.user, media, ret.get("ratings_info") - ) + if settings.ALLOW_RATINGS and ret.get("ratings_info") and not request.user.is_anonymous: + ret["ratings_info"] = update_user_ratings(request.user, media, ret.get("ratings_info")) ret["related_media"] = related_media return Response(ret) @@ -521,9 +485,7 @@ class MediaDetail(APIView): return media if not (is_mediacms_editor(request.user) or is_mediacms_manager(request.user)): - return Response( - {"detail": "not allowed"}, status=status.HTTP_400_BAD_REQUEST - ) + return Response({"detail": "not allowed"}, status=status.HTTP_400_BAD_REQUEST) action = request.data.get("type") profiles_list = request.data.get("encoding_profiles") @@ -544,24 +506,18 @@ class MediaDetail(APIView): valid_profiles.append(p) except ValueError: 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, ) media.encode(profiles=valid_profiles) - return Response( - {"detail": "media will be encoded"}, status=status.HTTP_201_CREATED - ) + return Response({"detail": "media will be encoded"}, status=status.HTTP_201_CREATED) elif action == "review": if result: media.is_reviewed = True - elif result == False: + elif result is False: media.is_reviewed = False media.save(update_fields=["is_reviewed"]) - return Response( - {"detail": "media reviewed set"}, status=status.HTTP_201_CREATED - ) + return Response({"detail": "media reviewed set"}, status=status.HTTP_201_CREATED) return Response( {"detail": "not valid action or no action specified"}, status=status.HTTP_400_BAD_REQUEST, @@ -573,9 +529,7 @@ class MediaDetail(APIView): if isinstance(media, Response): return media - serializer = MediaSerializer( - media, data=request.data, context={"request": request} - ) + serializer = MediaSerializer(media, data=request.data, context={"request": request}) if serializer.is_valid(): media_file = request.data["media_file"] serializer.save(user=request.user, media_file=media_file) @@ -601,20 +555,12 @@ class MediaActions(APIView): def get_object(self, friendly_token): try: - media = ( - Media.objects.select_related("user") - .prefetch_related("encodings__profile") - .get(friendly_token=friendly_token) - ) + media = Media.objects.select_related("user").prefetch_related("encodings__profile").get(friendly_token=friendly_token) if media.state == "private" and self.request.user != media.user: - return Response( - {"detail": "media is private"}, status=status.HTTP_400_BAD_REQUEST - ) + return Response({"detail": "media is private"}, status=status.HTTP_400_BAD_REQUEST) return media except PermissionDenied: - return Response( - {"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST - ) + return Response({"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST) except BaseException: return Response( {"detail": "media file does not exist"}, @@ -661,13 +607,9 @@ class MediaActions(APIView): extra_info=extra, ) - return Response( - {"detail": "action received"}, status=status.HTTP_201_CREATED - ) + return Response({"detail": "action received"}, status=status.HTTP_201_CREATED) else: - return Response( - {"detail": "no action specified"}, status=status.HTTP_400_BAD_REQUEST - ) + return Response({"detail": "no action specified"}, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, friendly_token, format=None): media = self.get_object(friendly_token) @@ -675,9 +617,7 @@ class MediaActions(APIView): return media if not request.user.is_superuser: - return Response( - {"detail": "not allowed"}, status=status.HTTP_400_BAD_REQUEST - ) + return Response({"detail": "not allowed"}, status=status.HTTP_400_BAD_REQUEST) action = request.data.get("type") if action: @@ -690,9 +630,7 @@ class MediaActions(APIView): status=status.HTTP_201_CREATED, ) else: - return Response( - {"detail": "no action specified"}, status=status.HTTP_400_BAD_REQUEST - ) + return Response({"detail": "no action specified"}, status=status.HTTP_400_BAD_REQUEST) class MediaSearch(APIView): @@ -736,11 +674,7 @@ class MediaSearch(APIView): if query: # move this processing to a prepare_query function query = clean_query(query) - q_parts = [ - q_part.rstrip("y") - for q_part in query.split() - if q_part not in STOP_WORDS - ] + q_parts = [q_part.rstrip("y") for q_part in query.split() if q_part not in STOP_WORDS] if q_parts: query = SearchQuery(q_parts[0] + ":*", search_type="raw") for part in q_parts[1:]: @@ -771,10 +705,10 @@ class MediaSearch(APIView): if upload_date == 'this_month': year = datetime.now().date().year month = datetime.now().date().month - gte = datetime(year,month,1) + gte = datetime(year, month, 1) if upload_date == 'this_year': year = datetime.now().date().year - gte = datetime(year,1,1) + gte = datetime(year, 1, 1) if lte: media = media.filter(add_date__lte=lte) if gte: @@ -794,9 +728,7 @@ class MediaSearch(APIView): pagination_class = api_settings.DEFAULT_PAGINATION_CLASS paginator = pagination_class() page = paginator.paginate_queryset(media, request) - serializer = MediaSearchSerializer( - page, many=True, context={"request": request} - ) + serializer = MediaSearchSerializer(page, many=True, context={"request": request}) return paginator.get_paginated_response(serializer.data) @@ -840,9 +772,7 @@ class PlaylistDetail(APIView): self.check_object_permissions(self.request, playlist) return playlist except PermissionDenied: - return Response( - {"detail": "not enough permissions"}, status=status.HTTP_400_BAD_REQUEST - ) + return Response({"detail": "not enough permissions"}, status=status.HTTP_400_BAD_REQUEST) except BaseException: return Response( {"detail": "Playlist does not exist"}, @@ -856,14 +786,10 @@ class PlaylistDetail(APIView): serializer = PlaylistDetailSerializer(playlist, context={"request": request}) - playlist_media = PlaylistMedia.objects.filter( - playlist=playlist - ).prefetch_related("media__user") + playlist_media = PlaylistMedia.objects.filter(playlist=playlist).prefetch_related("media__user") playlist_media = [c.media for c in playlist_media] - playlist_media_serializer = MediaSerializer( - playlist_media, many=True, context={"request": request} - ) + playlist_media_serializer = MediaSerializer(playlist_media, many=True, context={"request": request}) ret = serializer.data ret["playlist_media"] = playlist_media_serializer.data @@ -873,9 +799,7 @@ class PlaylistDetail(APIView): playlist = self.get_playlist(friendly_token) if isinstance(playlist, Response): return playlist - serializer = PlaylistDetailSerializer( - playlist, data=request.data, context={"request": request} - ) + serializer = PlaylistDetailSerializer(playlist, data=request.data, context={"request": request}) if serializer.is_valid(): serializer.save(user=request.user) return Response(serializer.data, status=status.HTTP_201_CREATED) @@ -895,14 +819,10 @@ class PlaylistDetail(APIView): pass if action in ["add", "remove", "ordering"]: - media = Media.objects.filter( - friendly_token=media_friendly_token - ).first() + media = Media.objects.filter(friendly_token=media_friendly_token).first() if media: if action == "add": - media_in_playlist = PlaylistMedia.objects.filter( - playlist=playlist - ).count() + media_in_playlist = PlaylistMedia.objects.filter(playlist=playlist).count() if media_in_playlist >= settings.MAX_MEDIA_PER_PLAYLIST: return Response( {"detail": "max number of media for a Playlist reached"}, @@ -920,9 +840,7 @@ class PlaylistDetail(APIView): status=status.HTTP_201_CREATED, ) elif action == "remove": - PlaylistMedia.objects.filter( - playlist=playlist, media=media - ).delete() + PlaylistMedia.objects.filter(playlist=playlist, media=media).delete() return Response( {"detail": "media removed from Playlist"}, status=status.HTTP_201_CREATED, @@ -935,9 +853,7 @@ class PlaylistDetail(APIView): status=status.HTTP_201_CREATED, ) else: - return Response( - {"detail": "media is not valid"}, status=status.HTTP_400_BAD_REQUEST - ) + return Response({"detail": "media is not valid"}, status=status.HTTP_400_BAD_REQUEST) return Response( {"detail": "invalid or not specified action"}, status=status.HTTP_400_BAD_REQUEST, @@ -993,7 +909,7 @@ class EncodingDetail(APIView): chunk_file_path=chunk_file_path, ).count() > 1 - and force == False + and force is False ): Encoding.objects.filter(id=encoding_id).delete() return Response({"status": "fail"}, status=status.HTTP_400_BAD_REQUEST) @@ -1013,15 +929,11 @@ class EncodingDetail(APIView): if chunk: original_media_path = chunk_file_path original_media_md5sum = encoding.md5sum - original_media_url = ( - settings.SSL_FRONTEND_HOST + encoding.media_chunk_url - ) + original_media_url = settings.SSL_FRONTEND_HOST + encoding.media_chunk_url else: original_media_path = media.media_file.path original_media_md5sum = media.md5sum - original_media_url = ( - settings.SSL_FRONTEND_HOST + media.original_media_url - ) + original_media_url = settings.SSL_FRONTEND_HOST + media.original_media_url ret["original_media_url"] = original_media_url ret["original_media_path"] = original_media_path @@ -1137,19 +1049,13 @@ class CommentDetail(APIView): def get_object(self, friendly_token): try: - media = Media.objects.select_related("user").get( - friendly_token=friendly_token - ) + media = Media.objects.select_related("user").get(friendly_token=friendly_token) self.check_object_permissions(self.request, media) if media.state == "private" and self.request.user != media.user: - return Response( - {"detail": "media is private"}, status=status.HTTP_400_BAD_REQUEST - ) + return Response({"detail": "media is private"}, status=status.HTTP_400_BAD_REQUEST) return media except PermissionDenied: - return Response( - {"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST - ) + return Response({"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST) except BaseException: return Response( {"detail": "media file does not exist"}, @@ -1181,16 +1087,10 @@ class CommentDetail(APIView): {"detail": "comment does not exist"}, status=status.HTTP_400_BAD_REQUEST, ) - if ( - (comment.user == self.request.user) - or comment.media.user == self.request.user - or is_mediacms_editor(self.request.user) - ): + if (comment.user == self.request.user) or comment.media.user == self.request.user or is_mediacms_editor(self.request.user): comment.delete() else: - return Response( - {"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST - ) + return Response({"detail": "bad permissions"}, status=status.HTTP_400_BAD_REQUEST) return Response(status=status.HTTP_204_NO_CONTENT) def post(self, request, friendly_token): @@ -1221,13 +1121,7 @@ class UserActions(APIView): media = [] if action in VALID_USER_ACTIONS: if request.user.is_authenticated: - media = ( - Media.objects.select_related("user") - .filter( - mediaactions__user=request.user, mediaactions__action=action - ) - .order_by("-mediaactions__action_date") - ) + media = Media.objects.select_related("user").filter(mediaactions__user=request.user, mediaactions__action=action).order_by("-mediaactions__action_date") elif request.session.session_key: media = ( Media.objects.select_related("user") @@ -1250,9 +1144,7 @@ class CategoryList(APIView): def get(self, request, format=None): categories = Category.objects.filter().order_by("title") - serializer = CategorySerializer( - categories, many=True, context={"request": request} - ) + serializer = CategorySerializer(categories, many=True, context={"request": request}) ret = serializer.data return Response(ret) @@ -1274,9 +1166,7 @@ class EncodeProfileList(APIView): def get(self, request, format=None): profiles = EncodeProfile.objects.all() - serializer = EncodeProfileSerializer( - profiles, many=True, context={"request": request} - ) + serializer = EncodeProfileSerializer(profiles, many=True, context={"request": request}) return Response(serializer.data) diff --git a/manage.py b/manage.py index cf1a63a..3fb6ad6 100755 --- a/manage.py +++ b/manage.py @@ -7,9 +7,5 @@ if __name__ == "__main__": try: from django.core.management import execute_from_command_line except ImportError as exc: - 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 + 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 execute_from_command_line(sys.argv) diff --git a/setup.cfg b/setup.cfg index 30335b4..e02d407 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,4 +1,4 @@ [flake8] exclude = .git,*migrations* 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 diff --git a/uploader/fineuploader.py b/uploader/fineuploader.py index fd1bfae..5556e2e 100644 --- a/uploader/fineuploader.py +++ b/uploader/fineuploader.py @@ -1,6 +1,7 @@ -from os.path import join -from io import StringIO import shutil +from io import StringIO +from os.path import join + from django.conf import settings from . import utils diff --git a/uploader/utils.py b/uploader/utils.py index f752c96..852d98d 100644 --- a/uploader/utils.py +++ b/uploader/utils.py @@ -1,6 +1,7 @@ -from django.core.exceptions import ImproperlyConfigured from importlib import import_module +from django.core.exceptions import ImproperlyConfigured + def import_class(path): path_bits = path.split(".") @@ -14,9 +15,7 @@ def import_class(path): module_itself = import_module(module_path) if not hasattr(module_itself, class_name): - message = "The Python module '{}' has no '{}' class.".format( - module_path, class_name - ) + message = "The Python module '{}' has no '{}' class.".format(module_path, class_name) raise ImportError(message) return getattr(module_itself, class_name) diff --git a/uploader/views.py b/uploader/views.py index 93b2da1..ce977ea 100644 --- a/uploader/views.py +++ b/uploader/views.py @@ -2,17 +2,18 @@ import os 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.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 files.models import Media from files.helpers import rm_file -from .forms import FineUploaderUploadForm, FineUploaderUploadSuccessForm +from files.models import Media + from .fineuploader import ChunkedFineUploader +from .forms import FineUploaderUploadForm, FineUploaderUploadSuccessForm class FineUploaderView(generic.FormView): @@ -67,9 +68,7 @@ class FineUploaderView(generic.FormView): new = Media.objects.create(media_file=myfile, user=self.request.user) rm_file(media_file) shutil.rmtree(os.path.join(settings.MEDIA_ROOT, self.upload.file_path)) - return self.make_response( - {"success": True, "media_url": new.get_absolute_url()} - ) + return self.make_response({"success": True, "media_url": new.get_absolute_url()}) def form_invalid(self, form): data = {"success": False, "error": "%s" % repr(form.errors)} diff --git a/users/adapter.py b/users/adapter.py index 0e35a99..8305feb 100644 --- a/users/adapter.py +++ b/users/adapter.py @@ -1,7 +1,7 @@ -from django.urls import reverse -from django.conf import settings from allauth.account.adapter import DefaultAccountAdapter +from django.conf import settings from django.core.exceptions import ValidationError +from django.urls import reverse class MyAccountAdapter(DefaultAccountAdapter): diff --git a/users/forms.py b/users/forms.py index 1113ba4..f7cb3b0 100644 --- a/users/forms.py +++ b/users/forms.py @@ -1,5 +1,6 @@ from django import forms -from .models import User, Channel + +from .models import Channel, User class SignupForm(forms.Form): @@ -23,7 +24,7 @@ class UserForm(forms.ModelForm): "advancedUser", "is_manager", "is_editor", - #"allow_contact", + # "allow_contact", ) def clean_logo(self): diff --git a/users/migrations/0001_initial.py b/users/migrations/0001_initial.py index e577d9f..e5f5238 100644 --- a/users/migrations/0001_initial.py +++ b/users/migrations/0001_initial.py @@ -1,12 +1,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.validators -from django.db import migrations, models import django.db.models.deletion import django.utils.timezone import imagekit.models.fields +from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): @@ -33,9 +33,7 @@ class Migration(migrations.Migration): ("password", models.CharField(max_length=128, verbose_name="password")), ( "last_login", - models.DateTimeField( - blank=True, null=True, verbose_name="last login" - ), + models.DateTimeField(blank=True, null=True, verbose_name="last login"), ), ( "is_superuser", @@ -48,35 +46,25 @@ class Migration(migrations.Migration): ( "username", models.CharField( - error_messages={ - "unique": "A user with that username already exists." - }, + error_messages={"unique": "A user with that username already exists."}, help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", max_length=150, unique=True, - validators=[ - django.contrib.auth.validators.UnicodeUsernameValidator() - ], + validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name="username", ), ), ( "first_name", - models.CharField( - blank=True, max_length=150, verbose_name="first name" - ), + models.CharField(blank=True, max_length=150, verbose_name="first name"), ), ( "last_name", - models.CharField( - blank=True, max_length=150, verbose_name="last name" - ), + models.CharField(blank=True, max_length=150, verbose_name="last name"), ), ( "email", - models.EmailField( - blank=True, max_length=254, verbose_name="email address" - ), + models.EmailField(blank=True, max_length=254, verbose_name="email address"), ), ( "is_staff", @@ -96,9 +84,7 @@ class Migration(migrations.Migration): ), ( "date_joined", - models.DateTimeField( - default=django.utils.timezone.now, verbose_name="date joined" - ), + models.DateTimeField(default=django.utils.timezone.now, verbose_name="date joined"), ), ( "logo", @@ -111,9 +97,7 @@ class Migration(migrations.Migration): ("description", models.TextField(blank=True, verbose_name="About me")), ( "name", - models.CharField( - db_index=True, max_length=250, verbose_name="full name" - ), + models.CharField(db_index=True, max_length=250, verbose_name="full name"), ), ( "date_added", @@ -125,9 +109,7 @@ class Migration(migrations.Migration): ), ( "is_featured", - models.BooleanField( - db_index=True, default=False, verbose_name="Is featured" - ), + models.BooleanField(db_index=True, default=False, verbose_name="Is featured"), ), ( "title", @@ -135,9 +117,7 @@ class Migration(migrations.Migration): ), ( "advancedUser", - models.BooleanField( - db_index=True, default=False, verbose_name="advanced user" - ), + models.BooleanField(db_index=True, default=False, verbose_name="advanced user"), ), ("media_count", models.IntegerField(default=0)), ( @@ -156,21 +136,15 @@ class Migration(migrations.Migration): ), ( "location", - models.CharField( - blank=True, max_length=250, verbose_name="Location" - ), + models.CharField(blank=True, max_length=250, verbose_name="Location"), ), ( "is_editor", - models.BooleanField( - db_index=True, default=False, verbose_name="MediaCMS Editor" - ), + models.BooleanField(db_index=True, default=False, verbose_name="MediaCMS Editor"), ), ( "is_manager", - models.BooleanField( - db_index=True, default=False, verbose_name="MediaCMS Manager" - ), + models.BooleanField(db_index=True, default=False, verbose_name="MediaCMS Manager"), ), ( "groups", @@ -218,9 +192,7 @@ class Migration(migrations.Migration): ("notify", models.BooleanField(default=False)), ( "method", - models.CharField( - choices=[("email", "Email")], default="email", max_length=20 - ), + models.CharField(choices=[("email", "Email")], default="email", max_length=20), ), ( "user", @@ -276,8 +248,6 @@ class Migration(migrations.Migration): ), migrations.AddIndex( model_name="user", - index=models.Index( - fields=["-date_added", "name"], name="users_user_date_ad_4eb0b8_idx" - ), + index=models.Index(fields=["-date_added", "name"], name="users_user_date_ad_4eb0b8_idx"), ), ] diff --git a/users/models.py b/users/models.py index dd087ad..40e15ad 100644 --- a/users/models.py +++ b/users/models.py @@ -1,18 +1,17 @@ -from django.db import models from django.conf import settings 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 imagekit.processors import ResizeToFill +from django.db import models +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.processors import ResizeToFill import files.helpers as helpers -from files.models import Media, Tag, Category +from files.models import Category, Media, Tag class User(AbstractUser): @@ -40,9 +39,7 @@ class User(AbstractUser): location = models.CharField("Location", max_length=250, blank=True) is_editor = models.BooleanField("MediaCMS Editor", default=False, db_index=True) is_manager = models.BooleanField("MediaCMS Manager", default=False, db_index=True) - allow_contact = models.BooleanField( - "Whether allow contact will be shown on profile page", default=False - ) + allow_contact = models.BooleanField("Whether allow contact will be shown on profile page", default=False) class Meta: ordering = ["-date_added", "name"] @@ -117,9 +114,7 @@ class User(AbstractUser): class Channel(models.Model): title = models.CharField(max_length=90, db_index=True) description = models.TextField(blank=True, help_text="description") - user = models.ForeignKey( - User, on_delete=models.CASCADE, db_index=True, related_name="channels" - ) + user = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True, related_name="channels") add_date = models.DateTimeField(auto_now_add=True, db_index=True) subscribers = models.ManyToManyField(User, related_name="subscriptions", blank=True) friendly_token = models.CharField(blank=True, max_length=12) @@ -150,13 +145,9 @@ class Channel(models.Model): def get_absolute_url(self, edit=False): if edit: - return reverse( - "edit_channel", kwargs={"friendly_token": self.friendly_token} - ) + return reverse("edit_channel", kwargs={"friendly_token": self.friendly_token}) else: - return reverse( - "view_channel", kwargs={"friendly_token": self.friendly_token} - ) + return reverse("view_channel", kwargs={"friendly_token": self.friendly_token}) @property def edit_url(self): @@ -178,9 +169,7 @@ Visit user profile page at %s instance.email, settings.SSL_FRONTEND_HOST + instance.get_absolute_url(), ) - email = EmailMessage( - title, msg, settings.DEFAULT_FROM_EMAIL, settings.ADMIN_EMAIL_LIST - ) + email = EmailMessage(title, msg, settings.DEFAULT_FROM_EMAIL, settings.ADMIN_EMAIL_LIST) email.send(fail_silently=True) @@ -193,14 +182,10 @@ class Notification(models.Model): Needs work """ - user = models.ForeignKey( - User, on_delete=models.CASCADE, db_index=True, related_name="notifications" - ) + user = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True, related_name="notifications") action = models.CharField(max_length=30, blank=True) notify = models.BooleanField(default=False) - method = models.CharField( - max_length=20, choices=NOTIFICATION_METHODS, default="email" - ) + method = models.CharField(max_length=20, choices=NOTIFICATION_METHODS, default="email") def save(self, *args, **kwargs): super(Notification, self).save(*args, **kwargs) diff --git a/users/serializers.py b/users/serializers.py index a26d77b..259fb72 100644 --- a/users/serializers.py +++ b/users/serializers.py @@ -1,4 +1,5 @@ from rest_framework import serializers + from .models import User @@ -11,9 +12,7 @@ class UserSerializer(serializers.ModelSerializer): return self.context["request"].build_absolute_uri(obj.get_absolute_url()) def get_api_url(self, obj): - return self.context["request"].build_absolute_uri( - obj.get_absolute_url(api=True) - ) + return self.context["request"].build_absolute_uri(obj.get_absolute_url(api=True)) def get_thumbnail_url(self, obj): 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()) def get_api_url(self, obj): - return self.context["request"].build_absolute_uri( - obj.get_absolute_url(api=True) - ) + return self.context["request"].build_absolute_uri(obj.get_absolute_url(api=True)) def get_thumbnail_url(self, obj): return self.context["request"].build_absolute_uri(obj.thumbnail_url()) diff --git a/users/urls.py b/users/urls.py index d6b5ca8..82311d4 100644 --- a/users/urls.py +++ b/users/urls.py @@ -1,4 +1,5 @@ from django.conf.urls import url + from . import views urlpatterns = [ @@ -20,9 +21,7 @@ urlpatterns = [ name="get_user_about", ), url(r"^user/(?P[\w@.]*)/edit$", views.edit_user, name="edit_user"), - url( - r"^channel/(?P[\w]*)$", views.view_channel, name="view_channel" - ), + url(r"^channel/(?P[\w]*)$", views.view_channel, name="view_channel"), url( r"^channel/(?P[\w]*)/edit$", views.edit_channel, diff --git a/users/validators.py b/users/validators.py index 734e8f2..d5f21b9 100644 --- a/users/validators.py +++ b/users/validators.py @@ -8,10 +8,7 @@ from django.utils.translation import gettext_lazy as _ @deconstructible class ASCIIUsernameValidator(validators.RegexValidator): regex = r"^[\w]+$" - message = _( - "Enter a valid username. This value may contain only " - "English letters and numbers" - ) + message = _("Enter a valid username. This value may contain only " "English letters and numbers") flags = re.ASCII diff --git a/users/views.py b/users/views.py index 1c840f1..a413615 100644 --- a/users/views.py +++ b/users/views.py @@ -1,27 +1,27 @@ -from django.shortcuts import render -from django.http import HttpResponseRedirect +from django.conf import settings from django.contrib.auth.decorators import login_required from django.core.mail import EmailMessage -from django.conf import settings - -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 django.http import HttpResponseRedirect +from django.shortcuts import render +from rest_framework import permissions, status +from rest_framework.decorators import api_view from rest_framework.exceptions import PermissionDenied -from rest_framework import status from rest_framework.parsers import ( - JSONParser, - MultiPartParser, FileUploadParser, 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 files.methods import is_mediacms_manager, is_mediacms_editor -from .models import User, Channel -from .forms import UserForm, ChannelForm -from .serializers import UserSerializer, UserDetailSerializer +from files.methods import is_mediacms_editor, is_mediacms_manager + +from .forms import ChannelForm, UserForm +from .models import Channel, User +from .serializers import UserDetailSerializer, UserSerializer def get_user(username): @@ -38,15 +38,9 @@ def view_user(request, username): if not user: return HttpResponseRedirect("/members") context["user"] = user - context["CAN_EDIT"] = ( - True - if ((user and user == request.user) or is_mediacms_manager(request.user)) - else False - ) + context["CAN_EDIT"] = 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["SHOW_CONTACT_FORM"] = ( - True if (user.allow_contact or is_mediacms_editor(request.user)) else False - ) + context["SHOW_CONTACT_FORM"] = True if (user.allow_contact or is_mediacms_editor(request.user)) else False return render(request, "cms/user.html", context) @@ -57,15 +51,9 @@ def view_user_media(request, username): return HttpResponseRedirect("/members") context["user"] = user - context["CAN_EDIT"] = ( - True - if ((user and user == request.user) or is_mediacms_manager(request.user)) - else False - ) + context["CAN_EDIT"] = 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["SHOW_CONTACT_FORM"] = ( - True if (user.allow_contact or is_mediacms_editor(request.user)) else False - ) + context["SHOW_CONTACT_FORM"] = True if (user.allow_contact or is_mediacms_editor(request.user)) else False return render(request, "cms/user_media.html", context) @@ -76,15 +64,9 @@ def view_user_playlists(request, username): return HttpResponseRedirect("/members") context["user"] = user - context["CAN_EDIT"] = ( - True - if ((user and user == request.user) or is_mediacms_manager(request.user)) - else False - ) + context["CAN_EDIT"] = 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["SHOW_CONTACT_FORM"] = ( - True if (user.allow_contact or is_mediacms_editor(request.user)) else False - ) + context["SHOW_CONTACT_FORM"] = True if (user.allow_contact or is_mediacms_editor(request.user)) else False return render(request, "cms/user_playlists.html", context) @@ -96,15 +78,9 @@ def view_user_about(request, username): return HttpResponseRedirect("/members") context["user"] = user - context["CAN_EDIT"] = ( - True - if ((user and user == request.user) or is_mediacms_manager(request.user)) - else False - ) + context["CAN_EDIT"] = 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["SHOW_CONTACT_FORM"] = ( - True if (user.allow_contact or is_mediacms_editor(request.user)) else False - ) + context["SHOW_CONTACT_FORM"] = True if (user.allow_contact or is_mediacms_editor(request.user)) else False return render(request, "cms/user_about.html", context) @@ -134,20 +110,14 @@ def view_channel(request, friendly_token): else: user = channel.user context["user"] = user - context["CAN_EDIT"] = ( - True - if ((user and user == request.user) or is_mediacms_manager(request.user)) - else False - ) + context["CAN_EDIT"] = True if ((user and user == request.user) or is_mediacms_manager(request.user)) else False return render(request, "cms/channel.html", context) @login_required def edit_channel(request, friendly_token): channel = Channel.objects.filter(friendly_token=friendly_token).first() - if not ( - channel and request.user.is_authenticated and (request.user == channel.user) - ): + if not (channel and request.user.is_authenticated and (request.user == channel.user)): return HttpResponseRedirect("/") if request.method == "POST": @@ -228,13 +198,9 @@ class UserDetail(APIView): self.check_object_permissions(self.request, user) return user except PermissionDenied: - return Response( - {"detail": "not enough permissions"}, status=status.HTTP_400_BAD_REQUEST - ) + return Response({"detail": "not enough permissions"}, status=status.HTTP_400_BAD_REQUEST) except User.DoesNotExist: - return Response( - {"detail": "user does not exist"}, status=status.HTTP_400_BAD_REQUEST - ) + return Response({"detail": "user does not exist"}, status=status.HTTP_400_BAD_REQUEST) def get(self, request, username, format=None): # Get user details @@ -251,9 +217,7 @@ class UserDetail(APIView): if isinstance(user, Response): return user - serializer = UserDetailSerializer( - user, data=request.data, context={"request": request} - ) + serializer = UserDetailSerializer(user, data=request.data, context={"request": request}) if serializer.is_valid(): logo = request.data.get("logo") if logo: @@ -271,9 +235,7 @@ class UserDetail(APIView): return user if not request.user.is_superuser: - return Response( - {"detail": "not allowed"}, status=status.HTTP_400_BAD_REQUEST - ) + return Response({"detail": "not allowed"}, status=status.HTTP_400_BAD_REQUEST) action = request.data.get("action") if action == "feature":