title: Can't view opencv processed videos in Django
body: <p>I have a Django app that lets you upload a video or image, it sends it to an OpenCV function that returns a new image/video with some annotations on it and then shows it in the template.</p>
  
  <p>For images it works just fine, but for video it doesn't.</p>
  
  <p>The video is shown as unavailable both in the view (as HTML5 video) and when following the /media/[path_to_file], but when I'm opening it with VLC (or some other desktop player, locally), the video exists and is working as expected.</p>
  
  <p>Moreover, I tried removing the OpenCV function and left only the upload functionality. When I uploaded a normal video, everything was fine, but when I tried to upload a video that was previously processed with OpenCV, it appears as unavailable again.</p>
  
  <p>My question is: is opencv changing the video properties in any way comparing to the original so that Django doesn't recognise it anymore? What might be the problem? And how can I make it that opencv processed videos are visible in my view?</p>
  
  <p>Also, the processed video has the same name and extension (and type) as the original.</p>
  
  <p>If you have any idea it's more than helpful. Thanks a lot!</p>
  
  <p><strong>Edit (included the code)</strong></p>
  
  <p>views.py</p>
  
  <pre><code>class IndexView(View):
  def __init__(self, **kwargs):
      super().__init__(**kwargs)
      self.context = dict()
  
  def get(self, request, *args, **kwargs):
      """ Action for GET requests """
      upload_form = UploadForm()
      self.context['upload_form'] = upload_form
      return render(request, 'homepage/home.html', self.context)
  
  def post(self, request, *args, **kwargs):
      """ Action for POST requests """
      upload_form = UploadForm(request.POST, request.FILES)
      if upload_form.is_valid:
          try:
              uploaded_file = upload_form.save()
              file_type = upload_form.cleaned_data['file'].content_type.split('/')[0]
              # task = start_annotation_process.delay(uploaded_file.file.name, file_type)
              task = add.delay(4, 5)
          except ValidationError:
              print('FAILED VALIDATION ERROR')
              self.context['upload_status'] = 'failed'
              return render(request, 'homepage/home.html', self.context)
          except ValueError:
              print('FAILED VALUE ERROR')
              self.context['upload_status'] = 'failed'
              return render(request, 'homepage/home.html', self.context)
  
          self.context['upload_form'] = upload_form
          self.context['task_id'] = task.task_id
          self.context['uploaded_file'] = uploaded_file
          self.context['upload_status'] = 'successfull'
          self.context['content_type'] = upload_form.cleaned_data['file'].content_type
          return render(request, 'homepage/home.html', self.context)
  </code></pre>
  
  <p>models.py</p>
  
  <pre><code>class UploadModel(models.Model):
  """ Model for storing uploaded files """
  file = models.FileField(upload_to='uploads/%Y/%m/%d/')
  upload_date = models.DateTimeField(auto_now_add=True)
  
  def __str__(self):
      return 'UploadModel({file}, {date})'.format(file=self.file, date=self.upload_date)
  
  class Meta:
      db_table = 'UploadModel'
  </code></pre>
  
  <p>forms.py</p>
  
  <pre><code>class UploadForm(forms.ModelForm):
  file = forms.FileField(widget=forms.ClearableFileInput(attrs={'class': "custom-file-input",
                                                                'id':"input-file",
                                                                'aria-describedby':"inputGroupFileAddon01"}))
  
  def clean_file(self):
      file = self.cleaned_data.get('file')
      if file != None:
          file = self.cleaned_data['file'] # try to delete
          file_type = file.content_type.split('/')[0]
          if file_type == settings.FILE_TYPES[0]:  # image
              self.__validate_size(file, settings.MAX_UPLOAD_IMAGE_SIZE)
          elif file_type == settings.FILE_TYPES[1]:  # video
              self.__validate_size(file, settings.MAX_UPLOAD_VIDEO_SIZE)
          else:
              print('File type ', file.content_type)
              raise forms.ValidationError(
                  'File type not supported. It has to be an image or a video.')
  
      return file
  
  def __validate_size(self, file, upload_size):
      if file.size &gt; int(upload_size):
          raise forms.ValidationError('The file size exceeds {upload_size}. '
                                      'Current file size is {file_size}'.format(upload_size=filesizeformat(upload_size),
                                                                                file_size=filesizeformat(file.size)))
  
  class Meta:
      model = UploadModel
      exclude = ('upload_date',)
      fields = ('file',)
  </code></pre>
  
  <p>urls.py (homepage)</p>
  
  <pre><code>urlpatterns = [
  path('', homepage_views.IndexView.as_view(), name='homepage'),
  path('celery-progress', include('celery_progress.urls')),  # the endpoint is configurable
  path('download/&lt;str:content_type&gt;/&lt;path:path&gt;', homepage_views.download_file, name='download'),
  </code></pre>
  
  <p>]</p>
  
  <p>urls.py (root app)</p>
  
  <pre><code>urlpatterns = [
      path('admin/', admin.site.urls),
      path('', include('homepage.urls')),
  ]
  
  if settings.DEBUG:
      urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
  </code></pre>
  
  <p>tasks.py (celery)</p>
  
  <pre><code>@shared_task(bind=True)
  def start_annotation_process(self, file_path, file_type):
      """ Celery task for starting the annotation process """
      print('start task')
      media_src_path = path.join(settings.MEDIA_ROOT, file_path)
      media_dst_path = path.join(settings.MEDIA_ROOT, 'results', file_path)
      media_res_path = path.join(settings.MEDIA_ROOT, 'results', path.dirname(file_path))
      if not path.exists(media_res_path):
          makedirs(media_res_path)
  
      if file_type == 'image':
          start_process_image(media_src_path, media_dst_path)
      elif file_type == 'video':
          start_process_video(media_src_path, media_dst_path)
  
      move(media_dst_path, media_src_path)
  
      print('finished task')
  </code></pre>
  
  <p>Opencv function (<code>FastObjectDetector</code> is a class that will predict the annotations and return the processed frame with <code>predict_img()</code> method)</p>
  
  <pre><code>def start_process_video(source_path, dest_path, process_offline=True, rotate=False):
      """ Start annotation process for video """
      if not process_offline:
          cfod = FastObjectDetector(score_threshold=0.5)
          vstrm = VideoCameraStream(logger=cfod.logger,
                                    process_func=cfod.predict_img,
                                    info_func=cfod._DEBUG_INFO,
                                    onclick_func=cfod.on_click,
                                    hd=1,
                                    camera=0)
          if vstrm.video != None:
              video_frame_shape = (vstrm.H, vstrm.W)
              cfod.prepare(image_shape=video_frame_shape)
              vstrm.play()
              vstrm.shutdown()
              if cfod.DEBUG:
                  cfod.show_fr_stats()
              cfod.shutdown()
  
      else:
          cfod = FastObjectDetector(score_threshold=0.5)
          FRAME_W, FRAME_H = 1280, 720
          cfod.prepare(image_shape=(FRAME_H, FRAME_W))
  
          video = cv2.VideoCapture(source_path)
          fourcc = int(video.get(cv2.CAP_PROP_FOURCC))
          fourcc = cv2.VideoWriter_fourcc(*'XVID')
          fps = video.get(cv2.CAP_PROP_FPS)
          frame_size = (FRAME_W, FRAME_H)
  
          source_path, _ = os.path.splitext(source_path)
          new_video = cv2.VideoWriter(dest_path, fourcc, fps, frame_size)
  
          while (video.isOpened()):
              ret, frame = video.read()
              if ret:
                  frame = cv2.resize(frame, (FRAME_W, FRAME_H))
                  if rotate:
                      (cW, cH) = (FRAME_W // 2, FRAME_H // 2)
                      m = cv2.getRotationMatrix2D((cW, cH), -90, 1)
                      frame = cv2.warpAffine(frame, m, (FRAME_W, FRAME_H))
  
                  frame = cfod.predict_img(frame)
  
                  new_video.write(frame)
                  if cv2.waitKey(1) &amp; 0xFF == ord('q'):
                      break
              else:
                  break
          video.release()
          new_video.release()
          cv2.destroyAllWindows()
  </code></pre>
  
  <p>template.html</p>
  
  <pre><code>&lt;video id="result-video" controls style="width:45vw; height:auto; max-height:40vh"&gt;
      &lt;p&gt; view this video please enable JavaScript, and consider upgrading to a web browser that &lt;a href="https://videojs.com/html5-video-support/" target="_blank"&gt;supports HTML5 video&lt;/a&gt; &lt;/p&gt;
  &lt;/video&gt;
  
  &lt;script type="text/javascript"&gt;
    {% if task_id and upload_status == "successfull" %}
  
      var progressUrl = "{% url 'celery_progress:task_status' task_id %}";
      $('#btn-upload').addClass('disabled');
      $(function () {
        let options = {
          onProgress: function(progressBarElement, progressBarMessageElement, progress){
            console.log('ON PROCESS');
            $('#div-progress').show();
            $('#div-results').hide();
          },
          onSuccess: function(progressBarElement, progressBarMessageElement){
            console.log('ON SUCCESS');
            var d = new Date();
            {% get_content_type content_type as type %}
            {% if type == 'image' %}
              $('.result-img').attr('src', '{{ uploaded_file.file.url }}?' + d.getTime());
            {% elif type == 'video' %}
              $('#result-video').html('&lt;source src="{{ uploaded_file.file.url }}" type="{{ content_type }}"&gt;');
              $('#result-video')[0].load();
            {% endif %}
            $('#div-progress').hide();
            $('#div-results').show();
            $('#btn-upload').removeClass('disabled')
          }
        };
        CeleryProgressBar.initProgressBar(progressUrl, options);
      });
    {% endif %}
  &lt;/script&gt;
  </code></pre>
  
  <p>For the template I used <code>celery_progress</code>'s callback functions to add the video source after the celery task is finished. The final HTML file will have the correct source included in the video tag. But the video is unavailable following the /media/ url too.</p>
  
  <p>I hope this helps. Thanks.</p>