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