How do i set the file width & height of a video file using controller

Hey, how do i set the file width & height of a video file
if i console log video.width & element.width i get the results but when i return albums at the end, they are not set, i dont understand
can someone help me pls

    "use strict";

    const { sanitizeEntity } = require("strapi-utils");
    const path = require("path");
    const ffmpeg = require("fluent-ffmpeg");

    module.exports = {
      async find(ctx) {
        let entities;
        if (ctx.query._q) {
          entities = await strapi.services.albums.search(ctx.query);
        } else {
          entities = await strapi.services.albums.find(ctx.query);
        }

        return entities.map((entity) => {
          const albums = sanitizeEntity(entity, {
            model: strapi.models.albums,
          });

          albums.media.forEach((video) => {
            if (video.width === null || video.height === null) {
              const videoFile = path.resolve(
                __dirname,
                "../../../public" + video.url
              );

              ffmpeg.ffprobe(videoFile, function (err, metadata) {
                if (err) {
                  console.error(err);
                } else {
                  metadata.streams.forEach((element) => {
                    video.width = element.width;
                    video.height = element.height;
                  });
                }
              });
            }
          });

          return albums;
        });
      },
    };

@kgnfth

Well, here are some javascript basic issues.

Your albums remains unchanged because your definitions: video.width = element.width and video.height = element.height remain scoped inside forEach function which can’t mutate array.

About Array.prototype.forEach():

  • it does not mutate the array on which it is called.
  • it always returns the value undefined

So, to solve your problem, replace forEach with map and return the modified video object.

Replace:

albums.media.forEach((video) => {

With

albums.media = albums.media.map((video) => { 

Replace:

   });

   return albums;

With:

    return video;  //return the modified video inside .map function
   });

   return albums;
2 Likes

Hi thx for helping, but width & height is still not applied
this is the output with and without the code

  {
    "id": 5,
    "title": "Test video",
    "description": null,
    "public": true,
    "password": null,
    "slug": "test-video",
    "created_at": "2021-03-10T12:46:27.000Z",
    "updated_at": "2021-03-10T12:51:40.000Z",
    "media": [
      {
        "id": 88,
        "name": "video1.mp4",
        "alternativeText": "",
        "caption": "",
        "width": null,
        "height": null,
        "formats": null,
        "hash": "video1_9af9b07285",
        "ext": ".mp4",
        "mime": "video/mp4",
        "size": 1420.88,
        "url": "/uploads/video1_9af9b07285.mp4",
        "previewUrl": null,
        "provider": "local",
        "provider_metadata": null,
        "created_at": "2021-03-10T12:51:28.000Z",
        "updated_at": "2021-03-10T12:51:28.000Z"
      },
      {
        "id": 89,
        "name": "video2.mp4",
        "alternativeText": "",
        "caption": "",
        "width": null,
        "height": null,
        "formats": null,
        "hash": "video2_343f455334",
        "ext": ".mp4",
        "mime": "video/mp4",
        "size": 1559.68,
        "url": "/uploads/video2_343f455334.mp4",
        "previewUrl": null,
        "provider": "local",
        "provider_metadata": null,
        "created_at": "2021-03-10T12:51:28.000Z",
        "updated_at": "2021-03-10T12:51:28.000Z"
      },
      {
        "id": 90,
        "name": "video3.mp4",
        "alternativeText": "",
        "caption": "",
        "width": null,
        "height": null,
        "formats": null,
        "hash": "video3_a6a229646b",
        "ext": ".mp4",
        "mime": "video/mp4",
        "size": 3220.14,
        "url": "/uploads/video3_a6a229646b.mp4",
        "previewUrl": null,
        "provider": "local",
        "provider_metadata": null,
        "created_at": "2021-03-10T12:51:29.000Z",
        "updated_at": "2021-03-10T12:51:29.000Z"
      },
      {
        "id": 91,
        "name": "video4.mp4",
        "alternativeText": "",
        "caption": "",
        "width": null,
        "height": null,
        "formats": null,
        "hash": "video4_238bebcaa6",
        "ext": ".mp4",
        "mime": "video/mp4",
        "size": 3435.84,
        "url": "/uploads/video4_238bebcaa6.mp4",
        "previewUrl": null,
        "provider": "local",
        "provider_metadata": null,
        "created_at": "2021-03-10T12:51:30.000Z",
        "updated_at": "2021-03-10T12:51:30.000Z"
      }
    ]
  }

Because of

metadata.streams.forEach((element) => {
                    video.width = element.width;
                    video.height = element.height;
                  });

streams contains both, video and audio.

So in your case, video.width and video.height are defined by the last iterration of forEach, and the last element of streams is an audio file, which doesn’t have any width and height. That’s why you get null.

Please read documantion of forEach and fluent-ffmpeg.

1 Like

Thx i will take a look

i cant make it work, i’m trying hard to understand javascript

Here i am providing shorter version of the code that includes the necessary changes:

const { sanitizeEntity } = require(“strapi-utils”);
const path = require(“path”);
const ffmpeg = require(“fluent-ffmpeg”);

module.exports = {
async find(ctx) {
const entities = ctx.query._q
? await strapi.services.albums.search(ctx.query)
: await strapi.services.albums.find(ctx.query);

const updatedEntities = await Promise.all(
  entities.map(async (entity) => {
    const albums = sanitizeEntity(entity, {
      model: strapi.models.albums,
    });

    await Promise.all(
      albums.media.map(async (video) => {
        if (video.width === null || video.height === null) {
          const videoFile = path.resolve(
            __dirname,
            "../../../public" + video.url
          );

          try {
            const metadata = await ffmpeg.ffprobeSync(videoFile);
            metadata.streams.forEach((element) => {
              video.width = element.width;
              video.height = element.height;
            });
          } catch (err) {
            console.error(err);
          }
        }
      })
    );

    return albums;
  })
);

return updatedEntities;

},
};

I used the same code for my client blog which is Basket Random Unblocked.

In this shortened version, the changes include:

1- Replaced the if statement for querying entities with a ternary operator for a more concise code.
2- Used ffprobeSync instead of ffprobe to make the function synchronous and avoid using additional promises.
3- Wrapped the inner map function with async and used await when calling ffprobeSync to handle the asynchronous code execution.
4- Replaced the forEach loop with forEach to iterate over the streams and set the width and height values.
5- Added a try/catch block to handle any errors that occur during the ffprobeSync call.