def clean_html_view(self, request, object_id, extra_context=None):
        model = self.model
        opts = model._meta
        obj = self.get_object(request, unquote(object_id))

        if obj is None:
            raise Http404()

        fields = self._get_fields(obj, model)

        if request.method == 'POST':
            kwargs = dict([
                (name, after) for (name, _before, after) in fields
            ])

            model.objects.filter(pk=obj.pk).update(**kwargs)

            msg = 'HTML fields for {} {} were cleaned up'.format(
                opts.verbose_name, object_id)
            self.message_user(request, msg)

            add_admin_history(obj, 'Cleaned HTML fields', request.user)

            return redirect('{}:{}_{}_change'.format(
                self.admin_site.name, opts.app_label, opts.model_name),
                object_id)

        context = {
            'opts': opts,
            'obj': obj,
            'fields': fields,
        }

        return TemplateResponse(request, 'admin/cleanhtml_form.html', context)
Beispiel #2
0
def generate_thumbnail(app_label,
                       model_name,
                       pk,
                       sendtask_callback=None,
                       sendtask_tasksetid=None):
    '''
    Generate a thumbnail if no original format is available
    '''
    cls = apps.get_model(app_label, model_name)

    try:
        # Load video
        v = cls.objects.get(pk=pk)
    except cls.DoesNotExist:
        logger.warning('Could not find video %s.%s %s.', app_label, model_name,
                       pk)
        return

    if not v.resource_original:
        # Identify the largest useable format
        path = ''
        for fmt in cls.UPLOAD_FORMATS:
            try:
                r = getattr(v, 'resource_' + fmt)
                if not r:
                    continue
                logger.info('Will use format: %s' % fmt)
                path = r.path
                break
            except AttributeError:
                continue
        else:
            logger.warning('Couldn\'t find valid format to upload for "%s"',
                           pk)
            return

        output = os.path.join(settings.MEDIA_ROOT, cls.Archive.Meta.root,
                              'original', pk + '.tif')

        # Takes the screenshot at 5s
        position = 5

        cmd = 'ffmpeg -ss {position} -i {input} -vframes 1 -q:v 2 {output}'.format(
            position=position, input=path, output=output)

        logger.info(cmd)
        call(cmd, shell=True)
        add_admin_history(v, 'Generated thumbnail from %s at %d seconds' %
                          (fmt, position))  # pylint: disable=undefined-loop-variable

    # Send_task callback
    if sendtask_callback:
        args, kwargs = sendtask_callback
        current_app.send_task(*args, **str_keys(kwargs))
Beispiel #3
0
    def embargo_date_action(self):
        '''
        Called at the time defined by embargo_date
        '''
        super(Video, self).embargo_date_action()

        # If the video is featured and has the same embargo and release date
        # then we leave it for release_date_action
        if self.featured and self.release_date == self.embargo_date:
            return

        self.update_youtube_privacy('unlisted')
        add_admin_history(self, 'Setting YouTube video privacy to "unlisted" at embargo time')
Beispiel #4
0
    def release_date_action(self):
        '''
        Called at the time defined by release_date
        '''
        super(Video, self).release_date_action()

        if self.published and self.featured:
            if self.release_date > datetime.datetime.now():
                # The release date is in the future, which shouldn't happen
                # We mark the video as unlisted and send a notification
                send_mail('WARNING: Video ' + self.pk + ' should be marked as public by '
                    'release_date_action but release date is in the future!',
                    '', '*****@*****.**', ['*****@*****.**'])
                self.update_youtube_privacy('unlisted')
                return

            self.update_youtube_privacy('public')
            self.update_youtube_playlists()
            add_admin_history(self, 'Setting YouTube video privacy to "public" at release time')
    def run( self, conf, model_identifier=None, pk=None ):
        """
        """
        if model_identifier and pk:
            obj, emails = self._get_emails( model_identifier, pk )
            mlist = self._get_list( conf['list_name'] )

            # Convert from ValuesListQuerySet to list:
            emails = list(emails)

            mailman_emails = mlist.get_mailman_emails()
            for email in set(emails) - mailman_emails:
                # Remove contact from the group
                try:
                    c = obj.contact_set.get(email=email)
                except ObjectDoesNotExist:
                    continue

                obj.contact_set.remove(c)
                emails.remove(email)
                self.get_logger().info(u'Removed %s from %s' % (c, obj))
                add_admin_history(c,
                    'Mailman Sync (%s): Removed from %s' %
                    (conf['list_name'], obj)
                )

                # Add contact to 'unsub' group (create group if it doesn't exist)
                cls = apps.get_model(*model_identifier.split( "." ))
                l, _created = cls.objects.get_or_create(name='unsub_%s' % obj.name)

                c.groups.add(l)
                self.get_logger().info(u'Added %s to %s' % (c, l))
                add_admin_history(c,
                    'Mailman Sync (%s): Added to group %s' %
                    (conf['list_name'], l)
                )

            mlist.update_subscribers( emails )
            mlist.push( remove_existing=conf['remove_existing'] )
Beispiel #6
0
def image_extras(image_id, sendtask_callback=None, sendtask_tasksetid=None):
    """
    Celery task to determine extra information about an image after
    saving. This allows speeding up the save queries.
    """
    from djangoplicity.media.models import Image

    # We sleep for 30s to give enough time for NFS to catch in case the
    # sync task will run on a different server, otherwise we have the risk
    # of getting the information from the placeholder file
    time.sleep(30)

    try:
        # Load image
        im = Image.objects.get(id=image_id)
        logger.debug("Found image %s " % (im.id))

        try:
            original_file = im.resource_original.path
            update_fields = []
            fields = {}

            if original_file:
                # File size/type
                fields['file_size'] = long(
                    os.path.getsize(original_file) / 1024)

                fields['file_type'] = get_file_type(original_file)

                w, h = identify_image(im.resource_original.path)
                fields['width'] = w
                fields['height'] = h

                # Set spatial_reference_dimension if empty
                if not im.spatial_reference_dimension:
                    fields['spatial_reference_dimension'] = '%s;%s' % \
                            (float(w), float(h))

                fields['n_pixels'] = fields['width'] * fields['height']

                for key, value in fields.items():
                    if value != getattr(im, key):
                        setattr(im, key, value)
                        update_fields.append(key)

                # We specify which fields to save to prevent overwriting
                # any changes that might have happen since we read from
                # the DB
                if update_fields:
                    im.save(run_tasks=False, update_fields=update_fields)
                    add_admin_history(
                        im, 'media.image_extras updated: %s' %
                        ', '.join(update_fields))
        except AttributeError:
            pass
    except Image.DoesNotExist:
        logger.warning("Could not find image %s." % image_id)

    # send_task callback
    if sendtask_callback:
        # pylint: disable=W0633
        args, kwargs = sendtask_callback
        current_app.send_task(*args, **str_keys(kwargs))
Beispiel #7
0
def upload_youtube(video_id, user_id=None):
    from djangoplicity.media.models import Video
    from djangoplicity.media.youtube import youtube_configured, \
    youtube_thumbnails_set, youtube_videos_insert, youtube_videos_resumable_upload

    user = None
    if user_id:
        try:
            user = User.objects.get(pk=user_id)
        except User.DoesNotExist:
            pass

    def mail_user(subject, body=''):
        if user and user.email:
            send_mail(subject, body, '*****@*****.**', [user.email])

    if not youtube_configured:
        logger.warning('YouTube not configured, won\'t upload "%s"', video_id)

    try:
        # Load video
        v = Video.objects.get(id=video_id)
    except Video.DoesNotExist:
        logger.warning("Could not find video %s." % video_id)
        return

    # Check if the video already has a YouTube ID
    if v.youtube_video_id:
        mail_user(
            'Video already on YouTube: %s' % video_id,
            'Video "%s" was already uploaded on YouTube as: https://www.youtube.com/watch?v=%s'
            % (video_id, v.youtube_video_id))
        return

    # Identify the format with the largest resolution to upload
    path = ''
    for fmt in Video.UPLOAD_FORMATS:
        try:
            r = getattr(v, 'resource_' + fmt)
            if not r:
                continue
            logger.info('Will use format: %s' % fmt)
            path = r.path
            break
        except AttributeError:
            continue
    else:
        logger.warning('Couldn\'t find valid format to upload for "%s"' %
                       video_id)

        mail_user(
            'Couldn\'t find valid format to upload "%s" to YouTube' % video_id,
            'Expected one of: %s' % ', '.join(Video.UPLOAD_FORMATS))
        return

    # Prepare the body of the insert request
    body = dict(
        snippet=dict(
            title=v.get_youtube_title(),
            description=v.get_youtube_description(),
            tags=v.get_youtube_tags(),
            categoryId='28',  # Science & Technology
        ),
        status=dict(
            privacyStatus='unlisted',
            license='creativeCommon',
        ))

    # Some formats such as hd_and_apple use .m4v extensions which is not
    # support by YouTube, so we cheat by creating a symbolic link with .mp4
    symlink = False
    if path.endswith('.m4v'):
        target = path.replace('.m4v', '.mp4')
        os.symlink(path, target)
        symlink = True
        path = target
        logger.info('Creating symlink to bypass .m4v restriction: "%s"',
                    target)

    # Call the API's videos.insert method to create and upload the video.
    logger.info('Found resource to upload: %s', path)
    insert_request = youtube_videos_insert(body, path)

    if symlink:
        # Remove the temporary symlink
        logger.info('Removing temporary symlink"%s"', target)
        os.remove(path)

    v.youtube_video_id = youtube_videos_resumable_upload(
        insert_request, logger)
    v.use_youtube = True
    v.save()

    # Set thumbnail
    if v.resource_videoframe:
        logger.info('Uploading thumbnail')
        try:
            youtube_thumbnails_set(v.youtube_video_id,
                                   v.resource_videoframe.path)
            logger.info('Thumbnail successfully uploaded')
        except HttpError as e:
            # Account doesn't have permissions to upload thumbnails, only
            # verified accounts can do so
            if e.resp['status'] == '403':
                logger.warning('No permissions to upload thumbnail')
            else:
                raise e

    add_admin_history(
        v, 'Uploaded to YouTube: http://youtu.be/%s' % v.youtube_video_id)

    mail_user('%s successfully  uploaded to YouTube: http://youtu.be/%s' %
              (v.pk, v.youtube_video_id))
Beispiel #8
0
def video_extras(app_label,
                 model_name,
                 pk,
                 sendtask_callback=None,
                 sendtask_tasksetid=None):
    """
    Celery task to determine extra information about an video after
    saving. This allows speeding up the save queries.
    """
    try:
        # Load video
        cls = apps.get_model(app_label, model_name)
        v = cls.objects.get(pk=pk)

        logger.debug("Found video %s " % (v.pk))
        update_fields = []
        fields = {}

        # Identify the format with the largest resolution
        for resource in ('cylindrical_16kmaster', 'dome_8kmaster',
                         'cylindrical_8kmaster', 'vr_8k', 'vr_4k',
                         'dome_4kmaster', 'cylindrical_4kmaster',
                         'ultra_hd_broadcast', 'ultra_hd', 'dome_2kmaster',
                         'hd_1080p25_screen', 'dome_preview',
                         'hd_broadcast_720p25', 'hd_and_apple', 'large_qt',
                         'broadcast_sd', 'medium_flash', 'medium_podcast',
                         'medium_mpeg1', 'qtvr', 'ext_highres', 'ext_playback',
                         'old_video'):
            try:
                r = getattr(v, 'resource_' + resource)
                if not r:
                    continue
                path = r.path
            except AttributeError:
                continue

            if resource in ('cylindrical_16kmaster', 'cylindrical_8kmaster',
                            'cylindrical_4kmaster', 'dome_8kmaster',
                            'dome_4kmaster',
                            'dome_2kmaster') and path.endswith('.zip'):
                # dome_xkmaster and cylindrical_xkmaster formats are a .zip
                # file of .jpg, which can't be read by mplayer, but their
                # resolution is known and we count the number of included files
                # to get the duration (divided by the frame rate)

                if r.size == 0:
                    # This should not happen, unless we have some "fake"
                    # files on the volume which are served by the CDN
                    # but only there locally so that Djangoplicity sees them
                    continue

                z = ZipFile(path)

                # Divide the number of includes files (frames) by the framerate
                # to get the duration in seconds
                fields['file_duration'] = len(z.infolist()) / v.frame_rate

                # Assume the width and height is 8k, 4k or 2k unless it's already set
                if resource == 'dome_8kmaster':
                    fields['width'] = v.width or 8192
                    fields['height'] = v.height or 8192
                elif resource == 'dome_4kmaster':
                    fields['width'] = v.width or 4096
                    fields['height'] = v.height or 4096
                elif resource == 'dome_2kmaster':
                    fields['width'] = v.width or 2048
                    fields['height'] = v.height or 2048
                elif resource == 'cylindrical_16kmaster':
                    fields['width'] = v.width or 16384
                    fields['height'] = v.height or 8192
                elif resource == 'cylindrical_8kmaster':
                    fields['width'] = v.width or 8192
                    fields['height'] = v.height or 4096
                elif resource == 'cylindrical_4kmaster':
                    fields['width'] = v.width or 4096
                    fields['height'] = v.height or 2048
            else:
                # Use midentify to fetch a dict of key/values about the file
                args = [
                    '/usr/bin/mplayer', '-noconfig', 'all', '-cache-min', '0',
                    '-vo', 'null', '-ao', 'null', '-frames', '0', '-identify',
                    path
                ]
                try:
                    output = Popen(args, stdout=PIPE,
                                   stderr=PIPE).communicate()[0].split('\n')
                except OSError, e:
                    logger.error('Can\'t run mplayer identify command: "%s"' %
                                 ' '.join(args))
                    raise e
                output_d = dict([
                    data.split('=') for data in output
                    if data.startswith('ID_')
                ])

                try:
                    fields['width'] = int(output_d['ID_VIDEO_WIDTH'])
                    fields['height'] = int(output_d['ID_VIDEO_HEIGHT'])
                    fields['file_duration'] = float(output_d['ID_LENGTH'])
                except KeyError as e:
                    logger.warning('mplayer could not detect {} for video {} '
                                   ', format {}'.format(e, pk, resource))

                    continue

            # Convert the duration from seconds to h:mm:ss, we happen :000 at the
            # end to ignore the extra frames.
            # We use int(duration) to drop the microseconds
            fields['file_duration'] = str(
                datetime.timedelta(
                    seconds=int(fields['file_duration']))) + ':000'

            for key, value in fields.items():
                if value != getattr(v, key):
                    setattr(v, key, value)
                    update_fields.append(key)

            if update_fields:
                v.save(run_tasks=False, update_fields=update_fields)
                add_admin_history(
                    v, 'media.video_extras updated: %s' %
                    ', '.join(update_fields))

            break

        else:
            continue

    if not path:
        logger.error('Could not find audio file for %s.%s "%s"', app_label,
                     module_name, pk)
        return

    args = [
        '/usr/bin/mplayer', '-noconfig', 'all', '-cache-min', '0', '-vo',
        'null', '-ao', 'null', '-frames', '0', '-identify', path
    ]
    try:
        output = Popen(args, stdout=PIPE,
                       stderr=PIPE).communicate()[0].split('\n')
    except OSError, e:
        logger.error('Can\'t run mplayer identify command: "%s"',
                     ' '.join(args))
        raise e
    output_d = dict(
        [data.split('=') for data in output if data.startswith('ID_')])
    duration = float(output_d['ID_LENGTH'])

    # Convert the duration from seconds to h:mm:ss, we happen :000 at the
    # end to ignore the extra frames.
    # We use int(duration) to drop the microseconds
    duration = str(datetime.timedelta(seconds=int(duration))) + ':000'

    if duration != archive.file_duration:
        model.objects.filter(pk=archive.pk).update(file_duration=duration)
        add_admin_history(archive, 'media.audio_extras updated: duration')