animina-dating / animina

👫❤️ ANIMINA Dating Platform
https://animina.de
Other
7 stars 6 forks source link

We have to rescale the image size and decrease file size #656

Closed wintermeyer closed 3 weeks ago

wintermeyer commented 2 months ago

AFAIK we use the uploaded images right now:

Bildschirmfoto 2024-06-11 um 13 48 16

But that is a no no. We can not willy nilly force our users to download > 4MB for a single photo. Ideally we have different resolutions for different things. Like one size for an avatar and one for a normal photo. No image should be bigger than 800Kb.

wintermeyer commented 2 months ago

Important: We should keep the original image. But create alternative ones which we use for our webpage. Than we can always re-optimize in the future.

MICHAELMUNAVU83 commented 2 months ago

Hello @wintermeyer , I have been researching on this the whole evening since I have not tackled such before . I have seen various implimentations online. I know you have experience in web perfomance so you probably encountrred something similar to this . What would be the best way to approach this , I would highly appreciate any help

wintermeyer commented 2 months ago

@MICHAELMUNAVU83 After uploading an image I would fire up n processes which use https://github.com/elixir-mogrify/mogrify or something similar to create images which are smaller. I would store them in an extra resource which is a has_many one to the original one. We could store all in the same resource and add one more attribute. I don't care which way you go.

wintermeyer commented 2 months ago

Just for clarification: We don’t need a fancy multiple images for different systems in one element.

But we know that a 7000x7000 pixel photo is overkill for an avatar. So create 2-3 different sizes and use the one which makes most sense for a specific thing.

MICHAELMUNAVU83 commented 4 weeks ago

@wintermeyer , Hello , I could not manage to install ImageMagick which is used to rescale the images , do you know any out of box solutions we can use?

wintermeyer commented 3 weeks ago

What OS do you use?

wintermeyer commented 3 weeks ago

@MICHAELMUNAVU83 What OS do you use on your dev system?

MICHAELMUNAVU83 commented 3 weeks ago

I am on a 2017 version of Mac Airbook

wintermeyer commented 3 weeks ago

I am on a 2017 version of Mac Airbook

Do you have https://brew.sh installed? With that it shouldn't be a problem to brew install imagemagick Please try it and give me feedback.

MICHAELMUNAVU83 commented 3 weeks ago

I do ,this is what I had tried previously , let me give it another try.

wintermeyer commented 3 weeks ago

@MICHAELMUNAVU83 In case you can't get imagemagick working on your dev system just create a bash script with the same name and fake it in development.

#!/bin/bash

# Check if the number of arguments is less than 2
if [ "$#" -lt 2 ]; then
    echo "Usage: $0 [options] input_file output_file"
    exit 1
fi

# Initialize variables for input and output files
input_file=""
output_file=""

# Parse arguments
while [[ "$#" -gt 0 ]]; do
    case "$1" in
        -*)
            # Ignore options
            shift
            ;;
        *)
            if [ -z "$input_file" ]; then
                input_file="$1"
            elif [ -z "$output_file" ]; then
                output_file="$1"
            else
                echo "Unexpected argument: $1"
                echo "Usage: $0 [options] input_file output_file"
                exit 1
            fi
            shift
            ;;
    esac
done

# Check if input_file and output_file are set
if [ -z "$input_file" ] || [ -z "$output_file" ]; then
    echo "Usage: $0 [options] input_file output_file"
    exit 1
fi

# Copy the input file to the output file
cp "$input_file" "$output_file"

echo "Image copied from $input_file to $output_file"

Save it and:

chmod +x imagemagick
./imagemagick -any -options -you -want input.jpg output.jpg

It is a workaround that does the job.

MICHAELMUNAVU83 commented 3 weeks ago

Thanks , I will give this a go and advise.

MICHAELMUNAVU83 commented 3 weeks ago

@wintermeyer , this works in my terminal . When I plug it into a phoenix project where I have

config :mogrify,
  convert_command: [
    path: "/Users/mac/imagemagick",
    args: []
  ]

config :mogrify,
  mogrify_command: [
    path: "/Users/mac/imagemagick",
    args: []
  ]

config :mogrify,
  identify_command: [
    path: "/Users/mac/imagemagick",
    args: []
  ]

in config.exs .

If I run an example as such


 Mogrify.open("/Users/mac/Downloads/featured.jpeg") |> Mogrify.format("jpeg") |> Mogrify.save()

I get

** (MatchError) no match of right hand side value: {"Arguments: /Users/mac/Downloads/featured.jpeg -format jpeg /var/folders/86/s83z6g953k9bd1twmyhzq8840000gn/T/747382-featured.jpeg\nUnexpected argument: /var/folders/86/s83z6g953k9bd1twmyhzq8840000gn/T/747382-featured.jpeg\nUsage: /Users/mac/imagemagick [options] input_file output_file\n", 1}
    (mogrify 0.9.3) lib/mogrify.ex:43: Mogrify.save/2
    iex:5: (file)

Any Idea what I might be missing here , have you tried on your local using the bash script?

wintermeyer commented 3 weeks ago

@MICHAELMUNAVU83 Please try this script:

#!/bin/bash

# Check if the number of arguments is less than 2
if [ "$#" -lt 2 ]; then
    echo "Usage: $0 [options] input_file output_file"
    exit 1
fi

# Extract the input file (second to last argument) and output file (last argument)
input_file="${@: -2:1}"
output_file="${@: -1}"

# Copy the input file to the output file
cp "$input_file" "$output_file"

echo "Image copied from $input_file to $output_file"
MICHAELMUNAVU83 commented 3 weeks ago

Hey @wintermeyer , big thanks for this , we are moving . So now the function runs well , if I do

def test do
    Mogrify.open("/Users/mac/Documents/featured.jpeg")
    |> Mogrify.format("png")
    |> Mogrify.save(in_place: true)
  end

It gives this

%Mogrify.Image{
  path: "/Users/mac/Documents/featured.png",
  ext: ".png",
  format: "png",
  width: nil,
  height: nil,
  animated: false,
  frame_count: 1,
  buffer: nil,
  operations: [],
  dirty: %{}
}

But in the documents folder , there is no such file . If I try to specify a place for saving eg

def test do
    Mogrify.open("/Users/mac/Documents/featured.jpeg")
    |> Mogrify.format("png")
    |> Mogrify.save(path: "/Users/mac/Documents")
  end

I get

** (File.CopyError) could not copy from "/var/folders/86/s83z6g953k9bd1twmyhzq8840000gn/T/620794-featured.png" to "/Users/mac/Documents": no such file or directory
    (elixir 1.15.7) lib/file.ex:717: File.copy!/3
    (mogrify 0.9.3) lib/mogrify.ex:48: Mogrify.save/2
    iex:51: (file)

If I do it without specifying a location

 def test do
    Mogrify.open("/Users/mac/Documents/featured.jpeg")
    |> Mogrify.format("png")
    |> Mogrify.save()
  end

I get

%Mogrify.Image{
  path: "/var/folders/86/s83z6g953k9bd1twmyhzq8840000gn/T/271018-featured.png",
  ext: ".png",
  format: "png",
  width: nil,
  height: nil,
  animated: false,
  frame_count: 1,
  buffer: nil,
  operations: [],
  dirty: %{}
}

But This File does not exist

wintermeyer commented 3 weeks ago
def test do
    Mogrify.open("/Users/mac/Documents/featured.jpeg")
    |> Mogrify.format("png")
    |> Mogrify.save(in_place: true)
  end

It gives this

%Mogrify.Image{
  path: "/Users/mac/Documents/featured.png",
  ext: ".png",
  format: "png",
  width: nil,
  height: nil,
  animated: false,
  frame_count: 1,
  buffer: nil,
  operations: [],
  dirty: %{}
}

But in the documents folder , there is no such file .

Did you test the same with the Bash script in the command line?

$ imagemagick /Users/mac/Documents/featured.jpeg /Users/mac/Documents/featured.png

Does that work?

MICHAELMUNAVU83 commented 3 weeks ago

Yes , and it copies

wintermeyer commented 3 weeks ago

Does this work?

open("/Users/mac/Documents/featured.jpeg") |> resize("100x100") |> save(in_place: true)

Is there any sort of debugging information in the console?

MICHAELMUNAVU83 commented 3 weeks ago

I have

%Mogrify.Image{
  path: "/Users/mac/Documents/featured.jpeg",
  ext: ".jpeg",
  format: nil,
  width: nil,
  height: nil,
  animated: false,
  frame_count: 1,
  buffer: nil,
  operations: [],
  dirty: %{}
}

But the image stays the same , not 100 by 100

featured

wintermeyer commented 3 weeks ago

I have

%Mogrify.Image{
  path: "/Users/mac/Documents/featured.jpeg",
  ext: ".jpeg",
  format: nil,
  width: nil,
  height: nil,
  animated: false,
  frame_count: 1,
  buffer: nil,
  operations: [],
  dirty: %{}
}

But the image stays the same , not 100 by 100

Of course the image stays the same. You are just using a dummy Bash script which just copies the file from a to b. You will not see any change with that on your dev system. But that is fine because on production with the real imagemagick it will work.

wintermeyer commented 3 weeks ago

You might want to remove this line from the Bash script:

echo "Image copied from $input_file to $output_file"

But I don't know if that makes any difference.

MICHAELMUNAVU83 commented 3 weeks ago

When we remove the line , it does not work.

@wintermeyer , Kindly Advise if we will have something of the sort for each image upload

def resize(image_path, width, height) do
    Mogrify.open(image_path)
    |> Mogrify.resize("#{width}x#{height}")
    |> Mogrify.save(path: "/uploads/resized_#{width}x#{height}_image_path")
  end
wintermeyer commented 3 weeks ago

We probably have to resize 99% of all uploads.

wintermeyer commented 3 weeks ago

@MICHAELMUNAVU83 Let's solve this with a work around:

Add three sizes of each image:

Just use sensible values for those sizes. We can adapt/change them later anytime.

MICHAELMUNAVU83 commented 3 weeks ago

@wintermeyer , kindly run this function and tell me what you get as an output.

def check_if_image_magick_is_installed do
    try do
      {output, 0} = System.cmd("convert", ["--version"])
      {:ok, output}
    rescue
      e in ErlangError ->
        if e.reason == :enoent do
          {:error, "ImageMagick is not installed or not in the system's PATH"}
        else
          {:error, "An unknown error occurred: #{inspect(e)}"}
        end
    end
  end
wintermeyer commented 3 weeks ago

Looking good!

iex(1)>     try do
...(1)>       {output, 0} = System.cmd("convert", ["--version"])
...(1)>       {:ok, output}
...(1)>     rescue
...(1)>       e in ErlangError ->
...(1)>         if e.reason == :enoent do
...(1)>           {:error, "ImageMagick is not installed or not in the system's PATH"}
...(1)>         else
...(1)>           {:error, "An unknown error occurred: #{inspect(e)}"}
...(1)>         end
...(1)>     end
{:ok,
 "Version: ImageMagick 7.1.1-32 Q16-HDRI aarch64 22207 https://imagemagick.org\nCopyright: (C) 1999 ImageMagick Studio LLC\nLicense: https://imagemagick.org/script/license.php\nFeatures: Cipher DPC HDRI Modules OpenMP(5.0) \nDelegates (built-in): bzlib fontconfig freetype gslib heic jng jp2 jpeg jxl lcms lqr ltdl lzma openexr png ps raw tiff webp xml zlib zstd\nCompiler: gcc (4.2)\n"}

And just to be sure I tried it with a non existing program name:

iex(3)>     try do
...(3)>       {output, 0} = System.cmd("negative_example", ["--version"])
...(3)>       {:ok, output}
...(3)>     rescue
...(3)>       e in ErlangError ->
...(3)>         if e.reason == :enoent do
...(3)>           {:error, "ImageMagick is not installed or not in the system's PATH"}
...(3)>         else
...(3)>           {:error, "An unknown error occurred: #{inspect(e)}"}
...(3)>         end
...(3)>     end
{:error,
 "An unknown error occurred: %ErlangError{original: :enoent, reason: nil}"}
MICHAELMUNAVU83 commented 3 weeks ago

Good , Thanks