Pricing Blog

Image/video dimensions (width/height/aspect ration)

  • ssssadsadasd-1407028789191643197

    ssssadsadasd

    3 months ago

    I need to save the image/video dimensions (width/height/aspect ration) in the database.
    so I use the input element in NC, upload the image/video, and before saving to database I need to fetch the image/video dimensions.
    has anyone implemented this before? I tried with AI but was not successful.

    thanks
  • ssssadsadasd-1407052259950465034

    ssssadsadasd

    3 months ago

    I built [this](https://editor.nordcraft.com/projects/seno12/branches/start/components/test-dimensions?rightpanel=style&selection=nodes.root&canvas-width=800&canvas-height=800), but when I try to upload it returns {message: 'No files provided or invalid input.', details: {}}
  • whitep4nth3r-1408009408646086799

    salma

    3 months ago

    You're not uploading it anywhere with the current code.

    If you log the event on change of the input, you'll see that you have a files array

    What you need to do is then make a POST to an API that is exposed by the service/backend you want to upload to
    1408009408436506695-image.png
  • ssssadsadasd-1408012013782700072

    ssssadsadasd

    3 months ago

    @salma thanks for this. yeah, I know I am not uploading anything at this stage. I simply want to get each image's dimensions from that files object. but I cannot seem to find it in the files. or isnt this the stage where I can see the dimensions?
  • whitep4nth3r-1408012547369467914

    salma

    3 months ago

    so it looks like it's this part of the custom action that the code is reaching, so perhaps you are not passing in the files correctly from the event?

    if (!args || !Array.isArray(args.files) || args.files.length === 0) {
    ctx.triggerActionEvent('On error', { message: 'No files provided or invalid input.', details: args });
    return;
    }


    I have to go into a meeting right now so if you dont' get any further in an hour, I can look again
  • whitep4nth3r-1408031107533901899

    salma

    3 months ago

    So the code in the custom action is always checking for an array of files
  • but when a single file is uploaded, it's not an array
  • whitep4nth3r-1408031682938015824

    salma

    3 months ago

    one moment I think I have it
  • whitep4nth3r-1408032926968315956

    salma

    3 months ago

    ok, it's a little complex
  • your files will be available at args.files.target.files in the custom action in the function getMediaDimensions
  • however the rest of the code expects files to be an array, but it is an object
  • whitep4nth3r-1408033888751849564

    salma

    3 months ago

    here's how I changed the getMediaDimensions formula in the custom action to not error

    https://editor.nordcraft.com/projects/_hite_ayla_secura_scra_ny_co_/branches/start/actions/getMediaDimensions?rightpanel=style
  • hope that helps you on your way!
  • I'm not sure where you got the code from for this custom action 🙂
  • whitep4nth3r-1408034344911769722

    salma

    3 months ago

    you also need to pass in the whole event in the change event for the input, and I did that in the branch too, so check that
  • whitep4nth3r-1408034938263044187

    salma

    3 months ago

    you can console log some things out in the processImageFile to check it is being processed correctly
  • logging out in processImageFile gets you the dimensions 🙂
    1408035085088723035-image.png
  • ssssadsadasd-1408334824313458764

    ssssadsadasd

    3 months ago

    @salma thanks this workd. just for future reference this is the code I used:

    /**
    * A generic function to create a media processor. It handles the common logic
    * for loading a media element and extracting its dimensions.
    * @param {string} elementType - The type of element to create ('img' or 'video').
    * @returns {function(File): Promise<object>} A function that takes a file and returns a promise with its dimensions.
    */
    function createMediaProcessor(elementType) {
    return function(file) {
    return new Promise((resolve, reject) => {
    const element = document.createElement(elementType);
    const url = URL.createObjectURL(file);
    const loadEvent = elementType === 'img' ? 'load' : 'loadedmetadata';

    // Success handler
    element.addEventListener(loadEvent, () => {
    URL.revokeObjectURL(url);
    const width = element.videoWidth element.naturalHeight;

    resolve({
    fileName: file.name,
    width: width,
    height: height,
    // Calculate aspect ratio on success
    aspect_ratio: height > 0 ? width / height : 0,
    status: 'fulfilled'
    });
    });
  • // Error handler
    element.addEventListener('error', () => {
    URL.revokeObjectURL(url);
    const fallbackWidth = 160;
    const fallbackHeight = 90;

    reject({
    fileName: file.name,
    width: fallbackWidth,
    height: fallbackHeight,
    // Calculate aspect ratio for the fallback dimensions
    aspect_ratio: fallbackWidth / fallbackHeight,
    status: 'rejected',
    error: Failed to load ${elementType} metadata.
    });
    });

    element.src = url;
    });
    };
    }

    // Create specific processors from the generic function
    const processImageFile = createMediaProcessor('img');
    const processVideoFile = createMediaProcessor('video');

    // A lookup map for routing file types to the correct processor
    const fileProcessors = {
    'image': processImageFile,
    'video': processVideoFile,
    };
  • /**
    * Main function to get media dimensions.
    */
    async function getMediaDimensions(args, ctx) {
    try {
    const fileList = args.files.target.files;

    if (!fileList || !fileList.length) {
    ctx.triggerActionEvent('On error', { message: 'No files were provided.' });
    return;
    }

    const processingPromises = Array.from(fileList).map(file => {
    const fileType = file.type.split('/')[0];
    const processor = fileProcessors[fileType];

    if (processor) {
    return processor(file);
    } else {
    const fallbackWidth = 160;
    const fallbackHeight = 90;

    // Add fallback dimensions and aspect ratio for unsupported files
    return Promise.resolve({
    fileName: file.name,
    width: fallbackWidth,
    height: fallbackHeight,
    aspect_ratio: fallbackWidth / fallbackHeight,
    status: 'rejected',
    error: 'Unsupported file type.'
    });
    }
    });

    const results = await Promise.allSettled(processingPromises);

    const mediaDimensions = results.map(result =>
    result.status === 'fulfilled' ? result.value : result.reason
    );

    ctx.triggerActionEvent('On success', { mediaDimensions });

    } catch (error) {
    ctx.triggerActionEvent('On error', { message: 'A critical error occurred.', details: error.message });
    }
    }