From c10ffc6d265bfdb55e60c23a3d26e071d7056133 Mon Sep 17 00:00:00 2001
From: ptitloup
Date: Thu, 19 Jul 2018 12:24:01 +0200
Subject: [PATCH 01/43] add celery integration
---
pod/main/celery.py | 18 ++++++++++++++++++
pod/main/tasks.py | 8 ++++++++
pod/main/templates/aside.html | 2 +-
pod/main/templates/base.html | 2 +-
pod/main/templates/navbar.html | 2 +-
pod/video/encode.py | 16 +++++++++++-----
6 files changed, 40 insertions(+), 8 deletions(-)
create mode 100644 pod/main/celery.py
create mode 100644 pod/main/tasks.py
diff --git a/pod/main/celery.py b/pod/main/celery.py
new file mode 100644
index 0000000000..2d22255517
--- /dev/null
+++ b/pod/main/celery.py
@@ -0,0 +1,18 @@
+import os
+from celery import Celery
+from django.conf import settings # noqa
+
+# set the default Django settings module for the 'celery' program.
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pod.settings")
+
+app = Celery(settings.CELERY_NAME, backend=settings.CELERY_BACKEND, broker=settings.CELERY_BROKER)
+
+# Using a string here means the worker will not have to
+# pickle the object when using Windows.
+app.config_from_object('django.conf:settings')
+app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
+
+
+@app.task(bind=True)
+def debug_task(self):
+ print('Request: {0!r}'.format(self.request))
\ No newline at end of file
diff --git a/pod/main/tasks.py b/pod/main/tasks.py
new file mode 100644
index 0000000000..60298eb20f
--- /dev/null
+++ b/pod/main/tasks.py
@@ -0,0 +1,8 @@
+from celery import shared_task
+
+
+@shared_task(bind=True)
+def task_start_encode(self, video_id):
+ print "START ENCODE VIDEO ID %s" % video_id
+ from pod.video.encode import encode_video
+ encode_video(video_id)
diff --git a/pod/main/templates/aside.html b/pod/main/templates/aside.html
index 04807ae5c9..edddc18ff0 100644
--- a/pod/main/templates/aside.html
+++ b/pod/main/templates/aside.html
@@ -9,7 +9,7 @@
- G+
+ G+
diff --git a/pod/main/templates/base.html b/pod/main/templates/base.html
index d7740739a2..b722a3a28a 100644
--- a/pod/main/templates/base.html
+++ b/pod/main/templates/base.html
@@ -11,7 +11,7 @@
-
+
diff --git a/pod/main/templates/navbar.html b/pod/main/templates/navbar.html
index d8bd570c1f..70acc85cc9 100644
--- a/pod/main/templates/navbar.html
+++ b/pod/main/templates/navbar.html
@@ -56,7 +56,7 @@
{% if user.is_authenticated %}
- {% if user.first_name != '' and user.last_name != '' %}{{ user.first_name|first|upper }}{{ user.last_name|first|upper }}{% else %}{{ user.username|slice:":2"}}{% endif %}
+ {% if user.first_name != '' and user.last_name != '' %}{{ user.first_name|first|upper }}{{ user.last_name|first|upper }}{% else %}{{ user.username|slice:":2"|upper}}{% endif %}
{% endif %}
\ No newline at end of file
From d860806eb62826cc260a1d949d5052b9cf148219 Mon Sep 17 00:00:00 2001
From: ptitloup
Date: Tue, 7 Aug 2018 15:06:49 +0200
Subject: [PATCH 14/43] add minrate and maxrate in video rendition, use it in
encode module, add initial fixture and test it. pep8 compliant
---
pod/video/encode.py | 18 +++---
pod/video/fixtures/initial_data.json | 8 ++-
.../commands/download_video_source_file.py | 49 ++++++++-------
pod/video/models.py | 63 +++++++++++++++----
pod/video/tests/test_models.py | 4 ++
5 files changed, 99 insertions(+), 43 deletions(-)
diff --git a/pod/video/encode.py b/pod/video/encode.py
index e7aeac6e7e..c0caccdf4d 100644
--- a/pod/video/encode.py
+++ b/pod/video/encode.py
@@ -49,7 +49,7 @@
MAX_BITRATE_RATIO = getattr(settings, 'MAX_BITRATE_RATIO', 1.07)
# maximum buffer size between bitrate conformance checks
RATE_MONITOR_BUFFER_RATIO = getattr(
- settings, 'RATE_MONITOR_BUFFER_RATIO', 1.5)
+ settings, 'RATE_MONITOR_BUFFER_RATIO', 2)
# maximum threads use by ffmpeg
FFMPEG_NB_THREADS = getattr(settings, 'FFMPEG_NB_THREADS', 0)
@@ -480,10 +480,12 @@ def get_video_command_mp4(video_id, video_data, output_dir):
bitrate = rendition.video_bitrate
audiorate = rendition.audio_bitrate
height = rendition.height
+ minrate = rendition.minrate
+ maxrate = rendition.maxrate
if in_height >= int(height) or rendition == renditions[0]:
int_bitrate = int(
re.search("(\d+)k", bitrate, re.I).groups()[0])
- maxrate = int_bitrate * MAX_BITRATE_RATIO
+ # maxrate = int_bitrate * MAX_BITRATE_RATIO
bufsize = int_bitrate * RATE_MONITOR_BUFFER_RATIO
name = "%sp" % height
@@ -491,8 +493,8 @@ def get_video_command_mp4(video_id, video_data, output_dir):
cmd += " %s -vf " % (static_params,)
cmd += "\"scale=-2:%s\"" % (height)
# cmd += "force_original_aspect_ratio=decrease"
- cmd += " -b:v %s -maxrate %sk -bufsize %sk -b:a %s" % (
- bitrate, int(maxrate), int(bufsize), audiorate)
+ cmd += " -minrate %s -b:v %s -maxrate %s -bufsize %sk -b:a %s" % (
+ minrate, bitrate, maxrate, int(bufsize), audiorate)
cmd += " -movflags faststart -write_tmcd 0 \"%s/%s.mp4\"" % (
output_dir, name)
@@ -697,12 +699,14 @@ def get_video_command_playlist(video_id, video_data, output_dir):
for rendition in renditions:
resolution = rendition.resolution
bitrate = rendition.video_bitrate
+ minrate = rendition.minrate
+ maxrate = rendition.maxrate
audiorate = rendition.audio_bitrate
height = rendition.height
if in_height >= int(height) or rendition == renditions[0]:
int_bitrate = int(
re.search("(\d+)k", bitrate, re.I).groups()[0])
- maxrate = int_bitrate * MAX_BITRATE_RATIO
+ # maxrate = int_bitrate * MAX_BITRATE_RATIO
bufsize = int_bitrate * RATE_MONITOR_BUFFER_RATIO
bandwidth = int_bitrate * 1000
@@ -712,8 +716,8 @@ def get_video_command_playlist(video_id, video_data, output_dir):
cmd += "\"scale=-2:%s\"" % (height)
# cmd += "scale=w=%s:h=%s:" % (width, height)
# cmd += "force_original_aspect_ratio=decrease"
- cmd += " -b:v %s -maxrate %sk -bufsize %sk -b:a %s" % (
- bitrate, int(maxrate), int(bufsize), audiorate)
+ cmd += " -minrate %s -b:v %s -maxrate %s -bufsize %sk -b:a %s" % (
+ minrate, bitrate, maxrate, int(bufsize), audiorate)
cmd += " -hls_playlist_type vod -hls_time %s \
-hls_flags single_file %s/%s.m3u8" % (
SEGMENT_TARGET_DURATION, output_dir, name)
diff --git a/pod/video/fixtures/initial_data.json b/pod/video/fixtures/initial_data.json
index 3d918871ca..12fa0242ef 100644
--- a/pod/video/fixtures/initial_data.json
+++ b/pod/video/fixtures/initial_data.json
@@ -14,7 +14,9 @@
"pk": 1,
"fields": {
"resolution": "640x360",
- "video_bitrate": "1000k",
+ "minrate": "500k",
+ "video_bitrate": "750k",
+ "maxrate": "1000k",
"audio_bitrate": "96k",
"encode_mp4": true
}
@@ -24,7 +26,9 @@
"pk": 2,
"fields": {
"resolution": "1280x720",
+ "minrate": "1000k",
"video_bitrate": "2000k",
+ "maxrate": "3000k",
"audio_bitrate": "128k",
"encode_mp4": true
}
@@ -34,7 +38,9 @@
"pk": 3,
"fields": {
"resolution": "1920x1080",
+ "minrate": "2000k",
"video_bitrate": "3000k",
+ "maxrate": "4500k",
"audio_bitrate": "192k",
"encode_mp4": false
}
diff --git a/pod/video/management/commands/download_video_source_file.py b/pod/video/management/commands/download_video_source_file.py
index a844f33502..76b9376f2f 100644
--- a/pod/video/management/commands/download_video_source_file.py
+++ b/pod/video/management/commands/download_video_source_file.py
@@ -14,6 +14,31 @@ class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument('video_id', nargs='+', type=int)
+ def download(self, vid, video_id, source_url, dest_file):
+ try:
+ self.stdout.write("\n - download %s : from %s to %s\n" % (
+ video_id,
+ source_url,
+ dest_file
+ ))
+ new_file = wget.download(source_url, dest_file)
+ self.stdout.write("\n")
+ vid.video = new_file.replace(
+ settings.MEDIA_ROOT + '/', ''
+ )
+ vid.save()
+ self.stdout.write(
+ self.style.SUCCESS(
+ 'Successfully download video "%s"' % video_id
+ )
+ )
+ except ValueError as e:
+ raise CommandError('ValueError "%s"' % e)
+ except FileNotFoundError as f:
+ raise CommandError('FileNotFoundError "%s"' % f)
+ except urllib.error.HTTPError as err:
+ raise CommandError('HTTPError "%s"' % err)
+
def handle(self, *args, **options):
for video_id in options['video_id']:
vid = None
@@ -30,28 +55,6 @@ def handle(self, *args, **options):
os.path.basename(vid.video.name)
)
os.makedirs(os.path.dirname(dest_file), exist_ok=True)
- try:
- self.stdout.write("\n - download %s : from %s to %s\n" % (
- video_id,
- source_url,
- dest_file
- ))
- new_file = wget.download(source_url, dest_file)
- self.stdout.write("\n")
- vid.video = new_file.replace(
- settings.MEDIA_ROOT + '/', ''
- )
- vid.save()
- self.stdout.write(
- self.style.SUCCESS(
- 'Successfully download video "%s"' % video_id
- )
- )
- except ValueError as e:
- raise CommandError('ValueError "%s"' % e)
- except FileNotFoundError as f:
- raise CommandError('FileNotFoundError "%s"' % f)
- except urllib.error.HTTPError as err:
- raise CommandError('HTTPError "%s"' % err)
+ self.download(self, vid, video_id, source_url, dest_file)
else:
raise CommandError('source url is empty')
diff --git a/pod/video/models.py b/pod/video/models.py
index c834d7bc31..ac310aed4a 100644
--- a/pod/video/models.py
+++ b/pod/video/models.py
@@ -745,18 +745,28 @@ class ViewCount(models.Model):
class VideoRendition(models.Model):
resolution = models.CharField(
_('resolution'),
- max_length=250,
+ max_length=50,
unique=True,
help_text="Please use the only format x. i.e.: "
+ "640x360 or 1280x720 or 1920x1080 .")
+ minrate = models.CharField(
+ _('minrate'),
+ max_length=50,
+ help_text="Please use the only format k. i.e.: "
+ + "300k or 600k or 1000k .")
video_bitrate = models.CharField(
_('bitrate video'),
- max_length=250,
+ max_length=50,
+ help_text="Please use the only format k. i.e.: "
+ + "300k or 600k or 1000k .")
+ maxrate = models.CharField(
+ _('maxrate'),
+ max_length=50,
help_text="Please use the only format k. i.e.: "
+ "300k or 600k or 1000k .")
audio_bitrate = models.CharField(
_('bitrate audio'),
- max_length=250,
+ max_length=50,
help_text="Please use the only format k. i.e.: "
+ "300k or 600k or 1000k .")
encode_mp4 = models.BooleanField(_('Make a MP4 version'), default=False)
@@ -778,15 +788,7 @@ def __str__(self):
return "VideoRendition num %s with resolution %s" % (
'%04d' % self.id, self.resolution)
- def clean(self):
- if self.resolution and 'x' not in self.resolution:
- raise ValidationError(
- VideoRendition._meta.get_field('resolution').help_text)
- else:
- res = self.resolution.replace('x', '')
- if not res.isdigit():
- raise ValidationError(
- VideoRendition._meta.get_field('resolution').help_text)
+ def clean_bitrate(self):
if self.video_bitrate and 'k' not in self.video_bitrate:
msg = "Error in %s : " % _('bitrate video')
raise ValidationError(
@@ -799,6 +801,43 @@ def clean(self):
raise ValidationError(
msg + VideoRendition._meta.get_field(
'video_bitrate').help_text)
+ if self.maxrate and 'k' not in self.maxrate:
+ msg = "Error in %s : " % _('maxrate')
+ raise ValidationError(
+ msg + VideoRendition._meta.get_field(
+ 'maxrate').help_text)
+ else:
+ vb = self.video_bitrate.replace('k', '')
+ if not vb.isdigit():
+ msg = "Error in %s : " % _('maxrate')
+ raise ValidationError(
+ msg + VideoRendition._meta.get_field(
+ 'maxrate').help_text)
+ if self.minrate and 'k' not in self.minrate:
+ msg = "Error in %s : " % _('minrate')
+ raise ValidationError(
+ msg + VideoRendition._meta.get_field(
+ 'minrate').help_text)
+ else:
+ vb = self.video_bitrate.replace('k', '')
+ if not vb.isdigit():
+ msg = "Error in %s : " % _('minrate')
+ raise ValidationError(
+ msg + VideoRendition._meta.get_field(
+ 'minrate').help_text)
+
+ def clean(self):
+ if self.resolution and 'x' not in self.resolution:
+ raise ValidationError(
+ VideoRendition._meta.get_field('resolution').help_text)
+ else:
+ res = self.resolution.replace('x', '')
+ if not res.isdigit():
+ raise ValidationError(
+ VideoRendition._meta.get_field('resolution').help_text)
+
+ self.clean_bitrate()
+
if self.audio_bitrate and 'k' not in self.audio_bitrate:
msg = "Error in %s : " % _('bitrate audio')
raise ValidationError(
diff --git a/pod/video/tests/test_models.py b/pod/video/tests/test_models.py
index 83f762461a..a919da1aff 100644
--- a/pod/video/tests/test_models.py
+++ b/pod/video/tests/test_models.py
@@ -466,12 +466,16 @@ class VideoRenditionTestCase(TestCase):
# fixtures = ['initial_data.json', ]
def create_video_rendition(self, resolution="640x360",
+ minrate="500k",
video_bitrate="1000k",
+ maxrate="2000k",
audio_bitrate="300k", encode_mp4=False):
# print("create_video_rendition : %s" % resolution)
return VideoRendition.objects.create(
resolution=resolution,
+ minrate=minrate,
video_bitrate=video_bitrate,
+ maxrate=maxrate,
audio_bitrate=audio_bitrate,
encode_mp4=encode_mp4)
From 9233716cb473e44b7c3032568efa23e9544be64a Mon Sep 17 00:00:00 2001
From: ptitloup
Date: Tue, 7 Aug 2018 16:41:32 +0200
Subject: [PATCH 15/43] fix bug in download command
---
pod/video/management/commands/download_video_source_file.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pod/video/management/commands/download_video_source_file.py b/pod/video/management/commands/download_video_source_file.py
index 76b9376f2f..64b826db7e 100644
--- a/pod/video/management/commands/download_video_source_file.py
+++ b/pod/video/management/commands/download_video_source_file.py
@@ -55,6 +55,6 @@ def handle(self, *args, **options):
os.path.basename(vid.video.name)
)
os.makedirs(os.path.dirname(dest_file), exist_ok=True)
- self.download(self, vid, video_id, source_url, dest_file)
+ self.download(vid, video_id, source_url, dest_file)
else:
raise CommandError('source url is empty')
From b4d24b1872f2645d6a715d888c1507eb506d97c2 Mon Sep 17 00:00:00 2001
From: ptitloup
Date: Wed, 8 Aug 2018 11:03:51 +0200
Subject: [PATCH 16/43] improve show tab theme in navbar
---
pod/main/static/js/main.js | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/pod/main/static/js/main.js b/pod/main/static/js/main.js
index ece04ed730..68af82df55 100644
--- a/pod/main/static/js/main.js
+++ b/pod/main/static/js/main.js
@@ -113,6 +113,7 @@ var get_list = function(tab, level=0, tab_selected=[], tag_type="option", li_cla
/*** CHANNELS IN NAVBAR ***/
$("#list-channels a.show-themes").click(function() {
+ /*
if($("#list-themes").children().length === 0) {
var str = get_list(listTheme["channel_"+$(this).data('id')], 0, [], tag_type="li", li_class="list-group-item", attrs='', add_link=true, current="", channel="");
$("#list-channels").removeClass("col-12");
@@ -125,8 +126,25 @@ $("#list-channels a.show-themes").click(function() {
$("#list-themes").html("").hide();
$(this).parents("li").removeClass('list-group-item-info');
}
+ */
+ $("#list-channels li").removeClass('list-group-item-info');
+ var str = get_list(listTheme["channel_"+$(this).data('id')], 0, [], tag_type="li", li_class="list-group-item", attrs='', add_link=true, current="", channel="");
+ $("#list-channels").removeClass("col-12");
+ $("#list-channels").addClass("col-8");
+ var closebutton = '';
+ closebutton += '× ';
+ closebutton += ' ';
+ closebutton += ' ';
+ $("#list-themes").html(closebutton+str).show();
+ $(this).parents("li").addClass('list-group-item-info');
return false;
});
+$(document).on('click','#close_tab_theme', function() {
+ $("#list-channels li").removeClass('list-group-item-info');
+ $("#list-channels").removeClass("col-8");
+ $("#list-channels").addClass("col-12");
+ $("#list-themes").html("").hide();
+});
$('#ownerboxnavbar').keyup(function() {
if($(this).val() && $(this).val().length > 2) {
From ee52c575b22002dc76107076b0d37f950ef58848 Mon Sep 17 00:00:00 2001
From: ptitloup
Date: Wed, 8 Aug 2018 11:05:41 +0200
Subject: [PATCH 17/43] remove comments
---
pod/main/static/js/main.js | 15 +--------------
1 file changed, 1 insertion(+), 14 deletions(-)
diff --git a/pod/main/static/js/main.js b/pod/main/static/js/main.js
index 68af82df55..c5b65d8a59 100644
--- a/pod/main/static/js/main.js
+++ b/pod/main/static/js/main.js
@@ -113,20 +113,6 @@ var get_list = function(tab, level=0, tab_selected=[], tag_type="option", li_cla
/*** CHANNELS IN NAVBAR ***/
$("#list-channels a.show-themes").click(function() {
- /*
- if($("#list-themes").children().length === 0) {
- var str = get_list(listTheme["channel_"+$(this).data('id')], 0, [], tag_type="li", li_class="list-group-item", attrs='', add_link=true, current="", channel="");
- $("#list-channels").removeClass("col-12");
- $("#list-channels").addClass("col-8");
- $("#list-themes").html(str).show();
- $(this).parents("li").addClass('list-group-item-info');
- } else {
- $("#list-channels").removeClass("col-8");
- $("#list-channels").addClass("col-12");
- $("#list-themes").html("").hide();
- $(this).parents("li").removeClass('list-group-item-info');
- }
- */
$("#list-channels li").removeClass('list-group-item-info');
var str = get_list(listTheme["channel_"+$(this).data('id')], 0, [], tag_type="li", li_class="list-group-item", attrs='', add_link=true, current="", channel="");
$("#list-channels").removeClass("col-12");
@@ -139,6 +125,7 @@ $("#list-channels a.show-themes").click(function() {
$(this).parents("li").addClass('list-group-item-info');
return false;
});
+
$(document).on('click','#close_tab_theme', function() {
$("#list-channels li").removeClass('list-group-item-info');
$("#list-channels").removeClass("col-8");
From 955be5ff1af3d6bd2cd9edbb461ead910f1bcef9 Mon Sep 17 00:00:00 2001
From: ptitloup
Date: Wed, 8 Aug 2018 14:04:31 +0200
Subject: [PATCH 18/43] add video count and total duration in footer
---
pod/main/context_processors.py | 15 +++++++++++++--
pod/main/templates/footer.html | 2 +-
2 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/pod/main/context_processors.py b/pod/main/context_processors.py
index b9b1672308..e73644e04e 100644
--- a/pod/main/context_processors.py
+++ b/pod/main/context_processors.py
@@ -1,8 +1,9 @@
from django.conf import settings as django_settings
from django.core.exceptions import ImproperlyConfigured
-from django.db.models import Count
+from django.db.models import Count, Sum
from django.db.models import Prefetch
from django.db.models.functions import Substr, Lower
+from datetime import timedelta
from pod.main.models import LinkFooter
@@ -123,10 +124,20 @@ def context_navbar(request):
LAST_VIDEOS = get_last_videos() if request.path == "/" else None
+ list_videos = Video.objects.filter(
+ encoding_in_progress=False,
+ is_draft=False)
+ VIDEOS_COUNT = list_videos.count()
+ VIDEOS_DURATION = str(timedelta(
+ seconds=list_videos.aggregate(Sum('duration'))['duration__sum']
+ ))
+
return {'ALL_CHANNELS': all_channels, 'CHANNELS': channels,
'TYPES': types, 'OWNERS': owners,
'DISCIPLINES': disciplines, 'LISTOWNER': json.dumps(listowner),
- 'LAST_VIDEOS': LAST_VIDEOS, 'LINK_FOOTER': linkFooter
+ 'LAST_VIDEOS': LAST_VIDEOS, 'LINK_FOOTER': linkFooter,
+ 'VIDEOS_COUNT': VIDEOS_COUNT,
+ 'VIDEOS_DURATION': VIDEOS_DURATION
}
diff --git a/pod/main/templates/footer.html b/pod/main/templates/footer.html
index 87cd5b4bbd..89c2dff1fd 100644
--- a/pod/main/templates/footer.html
+++ b/pod/main/templates/footer.html
@@ -27,6 +27,6 @@
> {% trans "Pod Project" %}
-{{TITLE_SITE}} {% trans "video platform of" %} {{TITLE_ETB}} - Release {{VERSION}}
+{{TITLE_SITE}} {% trans "video platform of" %} {{TITLE_ETB}} - Release {{VERSION}} - {{VIDEOS_COUNT}} {% trans "videos availables" %} [ {{VIDEOS_DURATION}} ]
{% endspaceless %}
\ No newline at end of file
From 02308e538382951e613e694d9c0953c8a0b96216 Mon Sep 17 00:00:00 2001
From: ptitloup
Date: Wed, 8 Aug 2018 16:56:27 +0200
Subject: [PATCH 19/43] create video_caption_maker view, template and link it
from completion
---
.../templates/video_caption_maker.html | 616 ++++++++++++++++++
.../templates/video_completion.html | 7 +-
pod/completion/urls.py | 14 +-
pod/completion/views.py | 28 +-
4 files changed, 649 insertions(+), 16 deletions(-)
create mode 100644 pod/completion/templates/video_caption_maker.html
diff --git a/pod/completion/templates/video_caption_maker.html b/pod/completion/templates/video_caption_maker.html
new file mode 100644
index 0000000000..021980c2b6
--- /dev/null
+++ b/pod/completion/templates/video_caption_maker.html
@@ -0,0 +1,616 @@
+{% extends 'base.html' %}
+{% load i18n %}
+{% load staticfiles %}
+
+{% block page_extra_head %}
+
+{% endblock page_extra_head %}
+
+
+{% block breadcrumbs %}
+ {{block.super}}
+ {% trans 'My videos' %}
+
+
+
+ {{video.title|title|truncatechars:45}}
+
+
+
+ {% trans 'Video Caption Maker' %}
+
+{% endblock %}
+
+{% block page_title %}
+{% trans "Video Caption Maker" %}
+{% endblock %}
+
+
+{% block page_content %}
+{% trans "Video Caption Maker" %}
+
+
+Load Existing Caption File [optional] :
+
+
+
+
Video:
+
+ HTML5 video is not supported
+ {% for vid in video.get_video_mp4 %}
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+ Play
+ Pause
+ Save
+ Save & Continue
+
+
+
+
+
Caption list:
+
+
+
+
+ Clear All
+
+
+
+
+ Select and copy the caption file contents below and paste into a .vtt file:
+
+
+
+
+
+
+ Save to File
+
+ Copy to Clipboard
+
+
+
+
+
+
+
+{% endblock page_content %}
+
+{% block collapse_page_aside %}
+{% endblock collapse_page_aside %}
+
+{% block page_aside %}
+{% endblock page_aside %}
+
+{% block more_script %}
+
+{% endblock more_script %}
\ No newline at end of file
diff --git a/pod/completion/templates/video_completion.html b/pod/completion/templates/video_completion.html
index d9624a6bec..1582f070a6 100644
--- a/pod/completion/templates/video_completion.html
+++ b/pod/completion/templates/video_completion.html
@@ -139,16 +139,17 @@ {% trans "Manag
{% trans 'Subtitle(s) and/or captions(s) related to this video.' %}
+
{% trans 'Video Caption Maker' %}
{% trans 'Several web sites allows you to subtitle or caption videos (for example: Amara)' %}
{% trans 'You can add several subtitle or caption files to a signle video (for example, in order to subtitle or caption this video in several languages' %}
{% trans 'Subtitles and/or caption(s) files must be in ".vtt" format.' %}
{% trans 'You will need the URL of this video to make subtitles and/or captions. This URL is a direct access to this video. Please do not communicate it outside of this site to avoid any misuse.' %}
-
diff --git a/pod/completion/urls.py b/pod/completion/urls.py
index 9521e21f4f..fa8ca0df61 100644
--- a/pod/completion/urls.py
+++ b/pod/completion/urls.py
@@ -1,14 +1,18 @@
from django.conf.urls import url
-from pod.completion.views import video_completion
-from pod.completion.views import video_completion_contributor
-from pod.completion.views import video_completion_document
-from pod.completion.views import video_completion_track
-from pod.completion.views import video_completion_overlay
+from .views import video_completion
+from .views import video_caption_maker
+from .views import video_completion_contributor
+from .views import video_completion_document
+from .views import video_completion_track
+from .views import video_completion_overlay
urlpatterns = [
url(r'^video_completion/(?P[\-\d\w]+)/$',
video_completion,
name='video_completion'),
+ url(r'^video_caption_maker/(?P[\-\d\w]+)/$',
+ video_caption_maker,
+ name='video_caption_maker'),
url(r'^video_completion_contributor/(?P[\-\d\w]+)/$',
video_completion_contributor,
name='video_completion_contributor'),
diff --git a/pod/completion/views.py b/pod/completion/views.py
index 895acec77b..0a80ec5deb 100644
--- a/pod/completion/views.py
+++ b/pod/completion/views.py
@@ -9,19 +9,31 @@
from django.contrib.admin.views.decorators import staff_member_required
from django.core.exceptions import PermissionDenied
from pod.video.models import Video
-from pod.completion.models import Contributor
-from pod.completion.forms import ContributorForm
-from pod.completion.models import Document
-from pod.completion.forms import DocumentForm
-from pod.completion.models import Track
-from pod.completion.forms import TrackForm
-from pod.completion.models import Overlay
-from pod.completion.forms import OverlayForm
+from .models import Contributor
+from .forms import ContributorForm
+from .models import Document
+from .forms import DocumentForm
+from .models import Track
+from .forms import TrackForm
+from .models import Overlay
+from .forms import OverlayForm
import json
ACTION = ['new', 'save', 'modify', 'delete']
+@csrf_protect
+@staff_member_required(redirect_field_name='referrer')
+def video_caption_maker(request, slug):
+ video = get_object_or_404(Video, slug=slug)
+ if request.user != video.owner and not request.user.is_superuser:
+ messages.add_message(
+ request, messages.ERROR, _(u'You cannot complement this video.'))
+ raise PermissionDenied
+ return render(
+ request,
+ 'video_caption_maker.html',
+ {'video': video})
@csrf_protect
@login_required(redirect_field_name='referrer')
From 41c62181fd4283a11192ef2190cb4b886c9984c2 Mon Sep 17 00:00:00 2001
From: ptitloup
Date: Thu, 9 Aug 2018 10:30:20 +0200
Subject: [PATCH 20/43] add copy to clipboard button
---
pod/completion/templates/video_caption_maker.html | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/pod/completion/templates/video_caption_maker.html b/pod/completion/templates/video_caption_maker.html
index 021980c2b6..315e7c7e65 100644
--- a/pod/completion/templates/video_caption_maker.html
+++ b/pod/completion/templates/video_caption_maker.html
@@ -167,7 +167,7 @@ {% trans "Video Caption Maker" %}
Save to File
-
+
Copy to Clipboard
@@ -606,11 +606,14 @@ {% trans "Video Caption Maker" %}
}
}
}
-
appendCurrentCaption();
-
SortAndDisplayCaptionList();
}
-
+$("#copyToClipboard").on('click', function() {
+ var copyText = document.getElementById("captionFile");
+ copyText.select();
+ document.execCommand("copy");
+ alert("text copied, paste it in vtt file");
+});
{% endblock more_script %}
\ No newline at end of file
From f644e66482c9f503f536f2af01fb7d412413577f Mon Sep 17 00:00:00 2001
From: ptitloup
Date: Thu, 9 Aug 2018 11:20:12 +0200
Subject: [PATCH 21/43] add some style and fix bug with start is 0
---
.../templates/video_caption_maker.html | 64 +++++++------------
1 file changed, 24 insertions(+), 40 deletions(-)
diff --git a/pod/completion/templates/video_caption_maker.html b/pod/completion/templates/video_caption_maker.html
index 315e7c7e65..c891844048 100644
--- a/pod/completion/templates/video_caption_maker.html
+++ b/pod/completion/templates/video_caption_maker.html
@@ -7,16 +7,12 @@
#videoElm {
display: block;
border: solid 1px #999;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
width: 100%;
min-height: 297px;
}
.videoError {
display: block;
background-color: #999;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
width: 100%;
min-height: 297px;
color: white;
@@ -33,8 +29,6 @@
}
#textCaptionEntry {
- -moz-box-sizing: border-box;
- box-sizing: border-box;
width: 100%;
height: auto;
line-height: 19px;
@@ -61,8 +55,6 @@
#display {
border: solid 1px #999;
height: 374px;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
overflow: auto;
}
@@ -94,7 +86,6 @@
#display div span:nth-child(3) {
font-style: italic;
}
-
{% endblock page_extra_head %}
@@ -122,12 +113,12 @@
{% trans "Video Caption Maker" %}
-Load Existing Caption File [optional] :
+{% trans "Load Existing Caption File [optional]" %} :
-
Video:
+
{% trans "Video" %}:
HTML5 video is not supported
{% for vid in video.get_video_mp4 %}
@@ -140,41 +131,35 @@ {% trans "Video Caption Maker" %}
-
- Play
- Pause
- Save
- Save & Continue
-
+
{% trans "Play" %}
+
{% trans "Pause" %}
+
{% trans "Save" %}
+
{% trans "Save & Continue" %}
-
Caption list:
+
{% trans "Caption list" %}:
-
-
Clear All
+
+ {% trans "Clear All" %}
- Select and copy the caption file contents below and paste into a .vtt file:
+ {% trans "Select and copy the caption file contents below and paste into a .vtt file" %}:
-
- Save to File
-
- Copy to Clipboard
+
+ {% trans "Copy to Clipboard" %}
+
-
-
-
-
-
+
{% endblock page_content %}
@@ -213,12 +198,14 @@ {% trans "Video Caption Maker" %}
});
$("#clearAllCaptions").on('click', function () {
- captionsArray.length = 0;
- autoPauseAtTime = -1;
- $("#display div").remove();
- $("#captionTitle").html(" ");
- $("#textCaptionEntry").val("");
- $("#captionFormatNone").click();
+ var deleteConfirm = confirm(gettext("Are you sure you want to delete all caption ?"));
+ if (deleteConfirm){
+ captionsArray.length = 0;
+ autoPauseAtTime = -1;
+ $("#display div").remove();
+ $("#captionTitle").html(" ");
+ $("#textCaptionEntry").val("");
+ }
});
// index into captionsArray of the caption being displayed. -1 if none.
@@ -464,16 +451,13 @@ {% trans "Video Caption Maker" %}
function DisplayVTTSource() {
var s = "WEBVTT\r\n\r\n";
-
for (var i = 0; i < captionsArray.length; ++i) {
if (captionsArray[i].caption != "") {
s += (FormatTime(captionsArray[i].start) + " --> " + FormatTime(captionsArray[i].end) + "\r\n");
s += captionsArray[i].caption + "\r\n\r\n";
}
}
-
$("#captionFile").val(s);
- $("#captionFileKind").text(".vtt");
}
var captionFileSourceUpdateTimer = null;
@@ -521,7 +505,6 @@ {% trans "Video Caption Maker" %}
}
$("#ttFile").get(0).addEventListener("change", function() {
- alert('coucou');
var filesObject = $("#ttFile").get(0).files;
if (filesObject && filesObject.length > 0) {
LoadCaptionFile(filesObject[0]);
@@ -591,6 +574,7 @@ {% trans "Video Caption Maker" %}
if (timeMatch) {
appendCurrentCaption();
cueStart = ParseTime(timeMatch[1]);
+ if(cueStart==0) cueStart = "0.0";
cueEnd = ParseTime(timeMatch[2]);
continue;
}
From 99316e39f902e885fdd9962fa396a0bb3aabfa5a Mon Sep 17 00:00:00 2001
From: ptitloup
Date: Thu, 9 Aug 2018 11:21:53 +0200
Subject: [PATCH 22/43] pep8
---
pod/completion/views.py | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/pod/completion/views.py b/pod/completion/views.py
index 0a80ec5deb..60488b6f1d 100644
--- a/pod/completion/views.py
+++ b/pod/completion/views.py
@@ -22,6 +22,7 @@
ACTION = ['new', 'save', 'modify', 'delete']
+
@csrf_protect
@staff_member_required(redirect_field_name='referrer')
def video_caption_maker(request, slug):
@@ -31,9 +32,10 @@ def video_caption_maker(request, slug):
request, messages.ERROR, _(u'You cannot complement this video.'))
raise PermissionDenied
return render(
- request,
- 'video_caption_maker.html',
- {'video': video})
+ request,
+ 'video_caption_maker.html',
+ {'video': video})
+
@csrf_protect
@login_required(redirect_field_name='referrer')
From d540a8581b6bb2685648fd260c6f984c1ec83f42 Mon Sep 17 00:00:00 2001
From: ptitloup
Date: Thu, 9 Aug 2018 14:55:18 +0200
Subject: [PATCH 23/43] improve encoding module to get better log, database
info and send email only if debug is false
---
pod/video/encode.py | 111 +++++++++++++++++++++++++-------------------
1 file changed, 64 insertions(+), 47 deletions(-)
diff --git a/pod/video/encode.py b/pod/video/encode.py
index c0caccdf4d..508326eb20 100644
--- a/pod/video/encode.py
+++ b/pod/video/encode.py
@@ -138,12 +138,9 @@ def change_encoding_step(video_id, num_step, desc):
def add_encoding_log(video_id, log):
- encoding_log, created = EncodingLog.objects.get_or_create(
+ encoding_log = EncodingLog.objects.get(
video=Video.objects.get(id=video_id))
- if encoding_log.log:
- encoding_log.log += "\n\n%s" % log
- else:
- encoding_log.log = "\n\n%s" % log
+ encoding_log.log += "\n\n%s" % (log)
encoding_log.save()
if DEBUG:
print(log)
@@ -162,23 +159,36 @@ def check_file(path_file):
def encode_video(video_id):
start = "Start at : %s" % time.ctime()
- change_encoding_step(video_id, 0, "start")
- add_encoding_log(video_id, start)
-
video_to_encode = Video.objects.get(id=video_id)
video_to_encode.encoding_in_progress = True
video_to_encode.save()
+ change_encoding_step(video_id, 0, "start")
- if check_file(video_to_encode.video.path):
+ encoding_log, created = EncodingLog.objects.get_or_create(
+ video=Video.objects.get(id=video_id))
+ encoding_log.log = "%s" % start
+ encoding_log.save()
+
+ if check_file(video_to_encode.video.path):
change_encoding_step(video_id, 1, "remove old data")
remove_msg = remove_old_data(video_id)
add_encoding_log(video_id, "remove old data : %s" % remove_msg)
- change_encoding_step(video_id, 2, "get video data")
+ # create video dir
+ change_encoding_step(video_id, 2, "create output dir")
+ output_dir = create_outputdir(video_id, video_to_encode.video.path)
+ add_encoding_log(video_id, "output_dir : %s" % output_dir)
+
+ # clear log file
+ open(output_dir + "/encoding.log", 'w').close()
+ with open(output_dir + "/encoding.log", "a") as f:
+ f.write("%s\n" % start)
+
+ change_encoding_step(video_id, 3, "get video data")
video_data = {}
try:
- video_data = get_video_data(video_id)
+ video_data = get_video_data(video_id, output_dir)
add_encoding_log(video_id, "get video data : %s" %
video_data["msg"])
except ValueError:
@@ -192,11 +202,6 @@ def encode_video(video_id):
video_to_encode.duration = video_data["duration"]
video_to_encode.save()
- # create video dir
- change_encoding_step(video_id, 3, "create output dir")
- output_dir = create_outputdir(video_id, video_to_encode.video.path)
- add_encoding_log(video_id, "output_dir : %s" % output_dir)
-
if video_data["is_video"]:
# encodage_video
# create encoding video command
@@ -357,6 +362,11 @@ def encode_video(video_id):
video_to_encode.encoding_in_progress = False
video_to_encode.save()
+ # End
+ add_encoding_log(video_id, "End : %s" % time.ctime())
+ with open(output_dir + "/encoding.log", "a") as f:
+ f.write("\n\nEnd : %s" % time.ctime())
+
# envois mail fin encodage
if EMAIL_ON_ENCODING_COMPLETION:
send_email_encoding(video_to_encode)
@@ -374,7 +384,7 @@ def encode_video(video_id):
# ##########################################################################
-def get_video_data(video_id):
+def get_video_data(video_id, output_dir):
video_to_encode = Video.objects.get(id=video_id)
msg = ""
source = "%s" % video_to_encode.video.path
@@ -383,15 +393,18 @@ def get_video_data(video_id):
ffproberesult = subprocess.run(
command, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
- msg += "\nffprobe command : %s" % command
+ msg += "\nffprobe command : \n- %s\n" % command
"""
add_encoding_log(
video_id,
"command : %s \n ffproberesult : %s" % (command, ffproberesult))
"""
info = json.loads(ffproberesult.stdout.decode('utf-8'))
- msg += "%s" % json.dumps(
- info, sort_keys=True, indent=4, separators=(',', ': '))
+ with open(output_dir + "/encoding.log", "a") as f:
+ f.write('\n\ffprobe commande video result :\n\n')
+ f.write('%s\n' % json.dumps(
+ info, sort_keys=True, indent=4, separators=(',', ': ')))
+
is_video = False
contain_audio = False
in_height = 0
@@ -426,8 +439,12 @@ def get_video_data(video_id):
"command : %s \n ffproberesult : %s" % (command, ffproberesult))
"""
info = json.loads(ffproberesult.stdout.decode('utf-8'))
- msg += "%s" % json.dumps(
- info, sort_keys=True, indent=4, separators=(',', ': '))
+ # msg += "%s" % json.dumps(
+ # info, sort_keys=True, indent=4, separators=(',', ': '))
+ with open(output_dir + "/encoding.log", "a") as f:
+ f.write('\n\ffprobe commande audio result :\n\n')
+ f.write('%s\n' % json.dumps(
+ info, sort_keys=True, indent=4, separators=(',', ': ')))
if len(info["streams"]) > 0:
contain_audio = True
@@ -510,15 +527,15 @@ def encode_video_mp4(source, cmd, output_dir):
ffmpegMp4Command = "%s %s -i %s %s" % (
FFMPEG, FFMPEG_MISC_PARAMS, source, cmd)
- msg = "ffmpegMp4Command :\n%s" % ffmpegMp4Command
- msg += "Encoding Mp4 : %s" % time.ctime()
+ msg = "\nffmpegMp4Command :\n%s" % ffmpegMp4Command
+ msg += "\n- Encoding Mp4 : %s" % time.ctime()
# ffmpegvideo = subprocess.getoutput(ffmpegMp4Command)
ffmpegvideo = subprocess.run(
ffmpegMp4Command, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
- msg += "End Encoding Mp4 : %s" % time.ctime()
+ msg += "\n- End Encoding Mp4 : %s" % time.ctime()
with open(output_dir + "/encoding.log", "ab") as f:
f.write(b'\n\nffmpegvideoMP4:\n\n')
@@ -646,7 +663,7 @@ def encode_video_mp3(video_id, source, output_dir):
'output_dir': output_dir,
'audio_bitrate': AUDIO_BITRATE
}
- msg = "ffmpegMP3Command :\n%s" % command
+ msg = "\nffmpegMP3Command :\n%s" % command
msg += "\n- Encoding MP3 : %s" % time.ctime()
# ffmpegaudio = subprocess.getoutput(command)
ffmpegaudio = subprocess.run(
@@ -738,15 +755,15 @@ def encode_video_playlist(source, cmd, output_dir):
ffmpegPlaylistCommand = "%s %s -i %s %s" % (
FFMPEG, FFMPEG_MISC_PARAMS, source, cmd)
- msg = "ffmpegPlaylistCommand :\n%s" % ffmpegPlaylistCommand
- msg += "Encoding Playlist : %s" % time.ctime()
+ msg = "\nffmpegPlaylistCommand :\n%s" % ffmpegPlaylistCommand
+ msg += "\n- Encoding Playlist : %s" % time.ctime()
# ffmpegvideo = subprocess.getoutput(ffmpegPlaylistCommand)
ffmpegvideo = subprocess.run(
ffmpegPlaylistCommand, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
- msg += "End Encoding Playlist : %s" % time.ctime()
+ msg += "\n- End Encoding Playlist : %s" % time.ctime()
with open(output_dir + "/encoding.log", "ab") as f:
f.write(b'\n\nffmpegvideoPlaylist:\n\n')
@@ -1026,11 +1043,6 @@ def remove_old_data(video_id):
video_to_encode.overview = None
video_to_encode.save()
- encoding_log, created = EncodingLog.objects.get_or_create(
- video=Video.objects.get(id=video_id))
- encoding_log.log = ""
- encoding_log.save()
-
encoding_log_msg = ""
encoding_log_msg += remove_previous_encoding_video(
video_to_encode)
@@ -1115,22 +1127,25 @@ def send_email_encoding(video_to_encode):
'content_id': video_to_encode.id
}
)
- message = "%s\n\n%s\n%s\n" % (
+ message = "%s\n%s\n\n%s\n%s\n%s\n" % (
+ _("Hello"),
_(u"The content “%(content_title)s” has been encoded to Web "
+ "formats, and is now available on %(site_title)s.") % {
'content_title': video_to_encode.title,
'site_title': TITLE_SITE
},
_(u"You will find it here:"),
- content_url
+ content_url,
+ _("Regards")
)
from_email = HELP_MAIL
to_email = []
to_email.append(video_to_encode.owner.email)
html_message = ""
- html_message = '%s
%s%s \
-
' % (
+ html_message = '%s
%s
%s%s \
+
%s
' % (
+ _("Hello"),
_(u"The content “%(content_title)s” has been encoded to Web "
+ "formats, and is now available on %(site_title)s.") % {
'content_title': '%s ' % video_to_encode.title,
@@ -1138,16 +1153,18 @@ def send_email_encoding(video_to_encode):
},
_(u"You will find it here:"),
content_url,
- content_url
- )
- send_mail(
- subject,
- message,
- from_email,
- to_email,
- fail_silently=False,
- html_message=html_message,
+ content_url,
+ _("Regards")
)
+ if not DEBUG:
+ send_mail(
+ subject,
+ message,
+ from_email,
+ to_email,
+ fail_silently=False,
+ html_message=html_message,
+ )
mail_managers(
subject, message, fail_silently=False,
html_message=html_message)
From 31e198bcff7871a5a696795a077d9f533c65df27 Mon Sep 17 00:00:00 2001
From: ptitloup
Date: Fri, 10 Aug 2018 10:13:41 +0200
Subject: [PATCH 24/43] add chapter in import data
---
pod/video/encode.py | 1 -
pod/video/management/commands/import_data.py | 68 ++++++++++++++------
2 files changed, 50 insertions(+), 19 deletions(-)
mode change 100644 => 100755 pod/video/encode.py
diff --git a/pod/video/encode.py b/pod/video/encode.py
old mode 100644
new mode 100755
index 508326eb20..ae832f8b55
--- a/pod/video/encode.py
+++ b/pod/video/encode.py
@@ -169,7 +169,6 @@ def encode_video(video_id):
encoding_log.log = "%s" % start
encoding_log.save()
-
if check_file(video_to_encode.video.path):
change_encoding_step(video_id, 1, "remove old data")
remove_msg = remove_old_data(video_id)
diff --git a/pod/video/management/commands/import_data.py b/pod/video/management/commands/import_data.py
index 2544b62232..656b47e2f8 100755
--- a/pod/video/management/commands/import_data.py
+++ b/pod/video/management/commands/import_data.py
@@ -4,6 +4,7 @@
from django.conf import settings
from django.apps import apps
from pod.video.models import Video
+from django.core.exceptions import ObjectDoesNotExist
try:
from pod.authentication.models import Owner
except ImportError:
@@ -17,12 +18,15 @@
BASE_DIR = getattr(
settings, 'BASE_DIR', '/home/pod/django_projects/podv2/pod')
+VIDEO_ID_TO_EXCLUDE = getattr(
+ settings, 'VIDEO_ID_TO_EXCLUDE', [])
+
class Command(BaseCommand):
- args = 'Channel Theme Type User Discipline'
+ args = 'Channel Theme Type User Discipline Pod tags Chapter'
help = 'Import from V1'
valid_args = ['Channel', 'Theme', 'Type', 'User', 'Discipline', 'FlatPage',
- 'UserProfile', 'Pod', 'tags']
+ 'UserProfile', 'Pod', 'tags', 'Chapter']
def add_arguments(self, parser):
parser.add_argument('import')
@@ -39,21 +43,10 @@ def handle(self, *args, **options):
with open(filepath, "r") as infile:
if type_to_import in ('Channel', 'Theme', 'Type', 'User',
'Discipline', 'FlatPage', 'UserProfile',
- 'Pod'):
+ 'Pod', 'Chapter'):
data = serializers.deserialize("json", infile)
for obj in data:
- if AUTHENTICATION and type_to_import == 'UserProfile':
- owner = Owner.objects.get(
- user_id=obj.object.user_id)
- owner.auth_type = obj.object.auth_type
- owner.affiliation = obj.object.affiliation
- owner.commentaire = obj.object.commentaire
- # todo image
- owner.save()
- else:
- print(obj, obj.object.id)
- obj.object.headband = None
- obj.save()
+ self.save_object(type_to_import, obj)
if type_to_import in ('tags'):
data = json.load(infile)
for obj in data:
@@ -63,6 +56,27 @@ def handle(self, *args, **options):
"******* Warning: you must give some arguments: %s *******"
% self.valid_args)
+ def save_object(self, type_to_import, obj):
+ if AUTHENTICATION and type_to_import == 'UserProfile':
+ owner = Owner.objects.get(
+ user_id=obj.object.user_id)
+ owner.auth_type = obj.object.auth_type
+ owner.affiliation = obj.object.affiliation
+ owner.commentaire = obj.object.commentaire
+ # todo image
+ owner.save()
+ else:
+ print(obj, obj.object.id, obj.object.video.id)
+ if (type_to_import == 'Pod'
+ and obj.object.id in VIDEO_ID_TO_EXCLUDE):
+ print(obj.object.id, " are exclude")
+ else:
+ if obj.object.video.id not in VIDEO_ID_TO_EXCLUDE:
+ obj.object.headband = None
+ obj.save()
+ else:
+ print("video ", obj.object.video.id, " are exclude")
+
def migrate_to_v2(self, filepath, type_to_import):
f = open(filepath, 'r')
filedata = f.read()
@@ -92,12 +106,23 @@ def get_new_data(self, type_to_import, filedata):
"pods.pod",
"video.video"
)
+ if type_to_import == 'Chapter':
+ newdata = filedata.replace(
+ "pods.chapterpods",
+ "chapter.chapter"
+ ).replace(
+ "\"time\"",
+ "\"time_start\""
+ )
return newdata
def add_tag_to_video(self, video_id, list_tag):
- video = Video.objects.get(id=video_id)
- video.tags = ', '.join(list_tag)
- video.save()
+ try:
+ video = Video.objects.get(id=video_id)
+ video.tags = ', '.join(list_tag)
+ video.save()
+ except ObjectDoesNotExist:
+ print(video_id, " does not exist")
"""
@@ -113,6 +138,8 @@ def add_tag_to_video(self, video_id, list_tag):
from django.contrib.auth.models import User
from django.contrib.flatpages.models import FlatPage
+from pods.models import ChapterPods
+
from django.core import serializers
jsonserializer = serializers.get_serializer("json")
json_serializer = jsonserializer()
@@ -167,6 +194,11 @@ def add_tag_to_video(self, video_id, list_tag):
with open("tags.json", "w") as out:
out.write(json.dumps(list_tag, indent=2))
+
+with open("Chapter.json", "w") as out:
+ json_serializer.serialize(ChapterPods.objects.all().order_by('video'),
+ indent=2, stream=out)
+
# todo
'chapter',
'completion',
From f0f31481cd0178398a07f110bda9201a8b37e08e Mon Sep 17 00:00:00 2001
From: ptitloup
Date: Fri, 10 Aug 2018 14:54:02 +0200
Subject: [PATCH 25/43] improve import data and add completion contributor and
document
---
pod/video/management/commands/import_data.py | 184 ++++++++++++++++---
1 file changed, 155 insertions(+), 29 deletions(-)
diff --git a/pod/video/management/commands/import_data.py b/pod/video/management/commands/import_data.py
index 656b47e2f8..4c415d01bf 100755
--- a/pod/video/management/commands/import_data.py
+++ b/pod/video/management/commands/import_data.py
@@ -4,7 +4,10 @@
from django.conf import settings
from django.apps import apps
from pod.video.models import Video
+from pod.completion.models import Document
from django.core.exceptions import ObjectDoesNotExist
+from django.core.files import File
+
try:
from pod.authentication.models import Owner
except ImportError:
@@ -12,6 +15,15 @@
# from django.contrib.auth.models import User as Owner
import os
import json
+import wget
+
+if getattr(settings, 'USE_PODFILE', False):
+ FILEPICKER = True
+ from pod.podfile.models import CustomFileModel
+ from pod.podfile.models import UserFolder
+else:
+ FILEPICKER = False
+ from pod.main.models import CustomFileModel
AUTHENTICATION = True if apps.is_installed('pod.authentication') else False
@@ -21,12 +33,15 @@
VIDEO_ID_TO_EXCLUDE = getattr(
settings, 'VIDEO_ID_TO_EXCLUDE', [])
+FROM_URL = getattr(settings, 'FROM_URL', "https://pod.univ-lille1.fr/media/")
+
class Command(BaseCommand):
- args = 'Channel Theme Type User Discipline Pod tags Chapter'
+ args = 'Channel Theme Type User Discipline Pod tags Chapter Contributor...'
help = 'Import from V1'
valid_args = ['Channel', 'Theme', 'Type', 'User', 'Discipline', 'FlatPage',
- 'UserProfile', 'Pod', 'tags', 'Chapter']
+ 'UserProfile', 'Pod', 'tags', 'Chapter', 'Contributor',
+ 'Overlay', 'docpods']
def add_arguments(self, parser):
parser.add_argument('import')
@@ -43,14 +58,15 @@ def handle(self, *args, **options):
with open(filepath, "r") as infile:
if type_to_import in ('Channel', 'Theme', 'Type', 'User',
'Discipline', 'FlatPage', 'UserProfile',
- 'Pod', 'Chapter'):
+ 'Pod', 'Chapter', 'Contributor',
+ 'Overlay'):
data = serializers.deserialize("json", infile)
for obj in data:
self.save_object(type_to_import, obj)
- if type_to_import in ('tags'):
+ if type_to_import in ('tags', 'docpods'):
data = json.load(infile)
for obj in data:
- self.add_tag_to_video(obj, data[obj])
+ self.add_data_to_video(type_to_import, obj, data[obj])
else:
print(
"******* Warning: you must give some arguments: %s *******"
@@ -86,36 +102,88 @@ def migrate_to_v2(self, filepath, type_to_import):
f.write(newdata)
f.close()
+ def Channel(self, filedata):
+ return filedata.replace(
+ "pods.channel",
+ "video.channel"
+ )
+
+ def Theme(self, filedata):
+ return filedata.replace(
+ "pods.theme",
+ "video.theme"
+ )
+
+ def Type(self, filedata):
+ return filedata.replace(
+ "pods.type",
+ "video.type"
+ ).replace("headband", "icon")
+
+ def Discipline(self, filedata):
+ return filedata.replace(
+ "pods.discipline",
+ "video.discipline"
+ ).replace("headband", "icon")
+
+ def Pod(self, filedata):
+ return filedata.replace(
+ "pods.pod",
+ "video.video"
+ )
+
+ def Chapter(self, filedata):
+ return filedata.replace(
+ "pods.chapterpods",
+ "chapter.chapter"
+ ).replace(
+ "\"time\"",
+ "\"time_start\""
+ )
+
+ def Contributor(self, filedata):
+ return filedata.replace(
+ "pods.contributorpods",
+ "completion.contributor"
+ )
+
+ def Overlay(self, filedata):
+ return filedata.replace(
+ "pods.overlaypods",
+ "completion.overlay"
+ )
+
def get_new_data(self, type_to_import, filedata):
newdata = ""
- if type_to_import in ('Channel', 'Theme', 'Type', 'Discipline'):
- newdata = filedata.replace(
- "pods." + type_to_import.lower(),
- "video." + type_to_import.lower()
- )
- if type_to_import in ('Type', 'Discipline'):
- newdata = newdata.replace("headband", "icon")
- if type_to_import in ('User', 'FlatPage', 'tags'):
+ type_import = {
+ "Channel": self.Channel,
+ "Theme": self.Theme,
+ "Type": self.Type,
+ "Discipline": self.Discipline,
+ "Pod": self.Pod,
+ "Chapter": self.Chapter,
+ "Contributor": self.Contributor,
+ "Overlay": self.Overlay,
+ }
+ if type_import.get(type_to_import):
+ func = type_import.get(type_to_import)
+ return func(filedata)
+
+ if type_to_import in ('User', 'FlatPage', 'tags', 'docpods'):
newdata = filedata
if type_to_import == 'UserProfile':
newdata = filedata.replace(
"core.userprofile", "authentication.owner"
).replace("\"image\":", "\"userpicture\":")
- if type_to_import == 'Pod':
- newdata = filedata.replace(
- "pods.pod",
- "video.video"
- )
- if type_to_import == 'Chapter':
- newdata = filedata.replace(
- "pods.chapterpods",
- "chapter.chapter"
- ).replace(
- "\"time\"",
- "\"time_start\""
- )
+
return newdata
+ def add_data_to_video(self, type_to_import, obj, data):
+ if type_to_import in ('docpods',):
+ self.add_doc_to_video(obj, data)
+ if type_to_import in ('tags',):
+ self.add_tag_to_video(obj, data)
+
def add_tag_to_video(self, video_id, list_tag):
try:
video = Video.objects.get(id=video_id)
@@ -124,6 +192,55 @@ def add_tag_to_video(self, video_id, list_tag):
except ObjectDoesNotExist:
print(video_id, " does not exist")
+ def add_doc_to_video(self, video_id, list_doc):
+ print(video_id, list_doc)
+ try:
+ video = Video.objects.get(id=video_id)
+ for doc in list_doc:
+ new_file = self.download_doc(doc)
+ print("\n", new_file)
+ document = self.create_and_save_doc(new_file, video)
+ Document.objects.create(video=video, document=document)
+ except ObjectDoesNotExist:
+ print(video_id, " does not exist")
+
+ def download_doc(self, doc):
+ source_url = FROM_URL + doc
+ dest_file = os.path.join(
+ settings.MEDIA_ROOT,
+ 'tempfile',
+ os.path.basename(doc)
+ )
+ os.makedirs(os.path.dirname(dest_file), exist_ok=True)
+ new_file = wget.download(source_url, dest_file)
+ return new_file
+
+ def create_and_save_doc(self, new_file, video):
+ if FILEPICKER:
+ homedir, created = UserFolder.objects.get_or_create(
+ name='home',
+ owner=video.owner)
+ videodir, created = UserFolder.objects.get_or_create(
+ name='%s' % video.slug,
+ owner=video.owner)
+ document = CustomFileModel(
+ folder=videodir,
+ created_by=video.owner
+ )
+ document.file.save(
+ os.path.basename(new_file),
+ File(open(new_file, "rb")),
+ save=True)
+ document.save()
+ else:
+ document = CustomFileModel()
+ document.file.save(
+ os.path.basename(new_file),
+ File(open(new_file, "rb")),
+ save=True)
+ document.save()
+ return document
+
"""
SAVE FROM PODV1
@@ -139,6 +256,7 @@ def add_tag_to_video(self, video_id, list_tag):
from django.contrib.flatpages.models import FlatPage
from pods.models import ChapterPods
+from pods.models import ContributorPods
from django.core import serializers
jsonserializer = serializers.get_serializer("json")
@@ -199,8 +317,16 @@ def add_tag_to_video(self, video_id, list_tag):
json_serializer.serialize(ChapterPods.objects.all().order_by('video'),
indent=2, stream=out)
-# todo
-'chapter',
-'completion',
+with open("Contributor.json", "w") as out:
+ json_serializer.serialize(ContributorPods.objects.all().order_by('video'),
+ indent=2, stream=out)
+
+list_doc = {}
+for p in Pod.objects.all():
+ list_doc["%s" %p.id] = []
+ for d in p.docpods_set.all():
+ list_doc["%s" %p.id].append(d.document.file.name)
+with open("docpods.json", "w") as out:
+ out.write(json.dumps(list_doc, indent=2))
"""
From c294053b61bbce0b487a57ee0eed76029b8459da Mon Sep 17 00:00:00 2001
From: ptitloup
Date: Fri, 10 Aug 2018 16:47:59 +0200
Subject: [PATCH 26/43] add import data for tracks, update webvtt, fix bug in
template
---
pod/main/templates/navbar.html | 2 +-
pod/video/management/commands/import_data.py | 79 +++++++++++++++++--
pod/video/templates/videos/video-element.html | 2 +-
pod/video/templates/videos/video-info.html | 2 +-
requirements.txt | 4 +-
5 files changed, 77 insertions(+), 12 deletions(-)
diff --git a/pod/main/templates/navbar.html b/pod/main/templates/navbar.html
index 70acc85cc9..5b94ce6c94 100644
--- a/pod/main/templates/navbar.html
+++ b/pod/main/templates/navbar.html
@@ -43,7 +43,7 @@
-
{% for track in video.track_set.all%}
-
+
{%endfor%}