├── .gitignore
├── LICENSE
├── MANIFEST.in
├── README.rst
├── image_optimizer
├── __init__.py
├── fields.py
├── settings.py
└── utils.py
├── image_optimizer_demo
├── app_demo
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── forms.py
│ ├── migrations
│ │ ├── .gitignore
│ │ └── __init__.py
│ └── models.py
├── image_optimizer_demo
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py
├── push.sh
├── setup.cfg
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | build/
3 | dist/
4 | *.egg-info/
5 | *.pypirc
6 | *.pyc
7 | *backup*
8 | db.sqlite3
9 | .vscode
10 | image_optimizer_demo/media/*
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 agus makmun
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE
2 | include README.md
3 | include requirements.txt
4 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | django-image-optimizer |pypi version|
2 | ---------------------------------------
3 |
4 | .. |pypi version|
5 | image:: https://img.shields.io/pypi/v/django-image-optimizer.svg
6 | :target: https://pypi.python.org/pypi/django-image-optimizer
7 |
8 | .. image:: https://img.shields.io/badge/license-MIT-blue.svg
9 | :target: https://raw.githubusercontent.com/agusmakmun/django-image-optimizer/master/LICENSE
10 |
11 | .. image:: https://img.shields.io/pypi/pyversions/django-image-optimizer.svg
12 | :target: https://pypi.python.org/pypi/django-image-optimizer
13 |
14 | .. image:: https://img.shields.io/badge/Django-1.8%20%3E=%203.0-green.svg
15 | :target: https://www.djangoproject.com
16 |
17 |
18 | Django Image Optimizer is a simple Django library that allows optimization
19 | of images by using `TinyPNG `_ or `Pillow `_.
20 |
21 |
22 | Installation
23 | ------------------------------
24 |
25 | Django Image Optimizer is available directly from `PyPI `_:
26 |
27 | 1. Installing the package.
28 |
29 | ::
30 |
31 | $ pip install django-image-optimizer
32 |
33 |
34 | 2. Don't forget to add ``'image_optimizer'`` to your ``'INSTALLED_APPS'``.
35 |
36 | ::
37 |
38 | # settings.py
39 | INSTALLED_APPS = [
40 | ....
41 | 'image_optimizer',
42 | ]
43 |
44 |
45 | 3. You have the option to use either TinyPNG or Pillow for optimizing images.
46 | Inform ``optimized_image`` which one you want to use by setting the following
47 |
48 | ::
49 |
50 | # To use Pillow
51 | OPTIMIZED_IMAGE_METHOD = 'pillow'
52 | # To use TinyPNG
53 | OPTIMIZED_IMAGE_METHOD = 'tinypng'
54 |
55 | Any other string that is set for this setting will mean that optimization does
56 | not occur. If you are unsure of whether you would like to use TinyPNG or Pillow,
57 | feel free to consult the documentation of each.
58 |
59 | If you choose to use TinyPNG, you will need to get an API key from
60 | TinyPNG. Visit https://tinypng.com/developers for more details on getting an
61 | API key. Once you have done so, add the following setting to your settings
62 | file. Note: it is a good idea to keep this secret
63 |
64 | ::
65 |
66 | TINYPNG_KEY = 'your-key'
67 |
68 |
69 | 4. You may use the ``OptimizedImageField`` by importing it
70 |
71 | ::
72 |
73 | from django.db import models
74 |
75 | from image_optimizer.fields import OptimizedImageField
76 |
77 |
78 | class MyModel(models.Model):
79 | ...
80 | image = OptimizedImageField()
81 |
82 |
83 | class MyModel2(models.Model):
84 | """
85 | If you using OPTIMIZED_IMAGE_METHOD = 'pillow'
86 | You can use this optional arguments.
87 |
88 | This model represents a MyModel2 with a few
89 | fields including a `image` field which is an OptimizedImageField
90 | instance with `optimized_image_output_size` and
91 | `optimized_image_resize_method` arguments set.
92 |
93 | This means that image would be a resized
94 | version of the source image, meant to keep a given screen resolution,
95 | in this case (400, 300) pixels.
96 | """
97 | image = OptimizedImageField(
98 | upload_to="uploads/%Y/%m/%d",
99 | optimized_image_output_size=(400, 300),
100 | optimized_image_resize_method="cover" # "crop", "cover", "contain", "width", "height", "thumbnail" or None
101 | )
102 |
103 |
104 | and saving images into it, the same way you would to a Django ``ImageField``.
105 | The optimized image will be saved into the ``url`` field in place of the
106 | unoptimized image.
107 |
108 |
109 | 5. Or you can directly use the ``image_optimizer`` function from utils.
110 |
111 | ::
112 |
113 | from image_optimizer.utils import image_optimizer
114 |
115 |
116 | def post_image(request):
117 | image_data = request.FILES.get('image')
118 | image_data = image_optimizer(image_data=image_data,
119 | output_size=(400, 300),
120 | resize_method='cover')
121 | ....
122 |
123 |
124 | **P.S:**
125 |
126 | Note about TinyPNG API keys: If you obtain the free TinyPNG API token, you are limited to 500
127 | image optimizations per month, so this function may fail if you have a
128 | lot of images. You may either obtain a paid API key, or wait until next month.
129 |
130 | This project forked from: https://github.com/dchukhin/django_optimized_image
131 |
--------------------------------------------------------------------------------
/image_optimizer/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/agusmakmun/django-image-optimizer/323659dc81cbdf2c068789550e81fa4b97ac2bc9/image_optimizer/__init__.py
--------------------------------------------------------------------------------
/image_optimizer/fields.py:
--------------------------------------------------------------------------------
1 | from django.db.models import ImageField
2 | from .utils import image_optimizer
3 |
4 |
5 | class OptimizedImageField(ImageField):
6 | """An ImageField that gets optimized on save() using tinyPNG."""
7 |
8 | def save_form_data(self, instance, data):
9 | """Remove the OptimizedNotOptimized object on clearing the image."""
10 | # Are we updating an image?
11 | updating_image = (
12 | True if data and getattr(instance, self.name) != data else False
13 | )
14 |
15 | if updating_image:
16 | data = image_optimizer(
17 | data,
18 | self.optimized_image_output_size,
19 | self.optimized_image_resize_method,
20 | )
21 |
22 | super().save_form_data(instance, data)
23 |
24 | def __init__(
25 | self,
26 | optimized_image_output_size=None,
27 | optimized_image_resize_method=None,
28 | *args,
29 | **kwargs
30 | ):
31 | """
32 | Initialize OptimizedImageField instance.
33 |
34 | set up the `optimized_image_output_size` and
35 | `optimized_image_resize_method` arguments for the current
36 | `OptimizedImageField` instance.
37 | """
38 | # Set the optimized_image_output_size specified on your
39 | # OptimizedImageField model instances
40 | self.optimized_image_output_size = optimized_image_output_size
41 |
42 | # Set the optimized_image_resize_method specified on your
43 | # OptimizedImageField model instances
44 | self.optimized_image_resize_method = optimized_image_resize_method
45 |
46 | super().__init__(**kwargs)
47 |
48 | def deconstruct(self):
49 | """
50 | Deconstruct method.
51 |
52 | deconstruct the field, allowing us to handle the field data, useful
53 | in cases where you want to add optional arguments to your custom
54 | field but you need to exclude them from migrations.
55 | """
56 | name, path, args, kwargs = super().deconstruct()
57 |
58 | if kwargs.get("optimized_image_output_size"):
59 | del kwargs["optimized_image_output_size"]
60 |
61 | if kwargs.get("optimized_image_resize_method"):
62 | del kwargs["optimized_image_resize_method"]
63 |
64 | return name, path, args, kwargs
65 |
--------------------------------------------------------------------------------
/image_optimizer/settings.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 |
3 | OPTIMIZED_IMAGE_METHOD = getattr(settings, "OPTIMIZED_IMAGE_METHOD", "pillow")
4 | TINYPNG_KEY = getattr(settings, "TINYPNG_KEY", None)
5 |
--------------------------------------------------------------------------------
/image_optimizer/utils.py:
--------------------------------------------------------------------------------
1 | import tinify
2 | import logging
3 | import requests
4 | from io import BytesIO
5 | from PIL import Image
6 | from resizeimage import resizeimage
7 | from uuid import uuid4
8 |
9 | from .settings import OPTIMIZED_IMAGE_METHOD, TINYPNG_KEY
10 |
11 |
12 | BACKGROUND_TRANSPARENT = (255, 255, 255, 0)
13 |
14 |
15 | def get_file_name(image_data):
16 | return image_data.name
17 |
18 |
19 | def get_file_extension(file_name):
20 | extension = None
21 | # Get image file extension
22 | if file_name.split(".")[-1].lower() != "jpg":
23 | extension = file_name.split(".")[-1].upper()
24 | else:
25 | extension = "JPEG"
26 | return extension
27 |
28 |
29 | def get_image_extension(image):
30 | return image.format
31 |
32 |
33 | def image_optimizer(image_data, output_size=None, resize_method=None):
34 | """
35 | Optimize an image that has not been saved to a file.
36 | :param `image_data` is image data, e.g from request.FILES['image']
37 | :param `output_size` is float pixel scale of image (width, height) or None, for example: (400, 300) # noqa: E501
38 | :param `resize_method` is string resize method, choices are:
39 | None or resizeimage.resize() method argument values,
40 | i.e: "crop", "cover", "contain", "width", "height", "thumbnail"
41 | :return optimized image data.
42 | """
43 | if OPTIMIZED_IMAGE_METHOD == "pillow":
44 | image = Image.open(image_data)
45 | bytes_io = BytesIO()
46 |
47 | extension = get_image_extension(image)
48 |
49 | # If output_size is set, resize the image with the selected
50 | # resize_method. 'thumbnail' is used by default
51 | if output_size is not None:
52 | if resize_method:
53 | image = resizeimage.resize(
54 | method=resize_method,
55 | image=image,
56 | size=output_size,
57 | )
58 |
59 | output_image = Image.new(
60 | "RGBA",
61 | output_size,
62 | BACKGROUND_TRANSPARENT,
63 | )
64 | output_image_center = (
65 | int((output_size[0] - image.size[0]) / 2),
66 | int((output_size[1] - image.size[1]) / 2),
67 | )
68 | output_image.paste(image, output_image_center)
69 | else:
70 | # If output_size is None the output_image
71 | # would be the same as source
72 | output_image = image
73 |
74 | # If the file extension is JPEG, convert the output_image to RGB
75 | if extension == "JPEG":
76 | output_image = output_image.convert("RGB")
77 |
78 | output_image.save(bytes_io, format=extension, optimize=True)
79 |
80 | image_data.seek(0)
81 | image_data.file.write(bytes_io.getvalue())
82 | image_data.file.truncate()
83 |
84 | elif OPTIMIZED_IMAGE_METHOD == "tinypng":
85 | # disable warning info
86 | requests.packages.urllib3.disable_warnings()
87 |
88 | # just info for people
89 | if any([output_size, resize_method]):
90 | message = (
91 | '[django-image-optimizer] "output_size" and "resize_method" '
92 | 'only for OPTIMIZED_IMAGE_METHOD="pillow"'
93 | )
94 | logging.info(message)
95 |
96 | tinify.key = TINYPNG_KEY
97 | optimized_buffer = tinify.from_buffer(
98 | image_data.file.read()
99 | ).to_buffer() # noqa: E501
100 | image_data.seek(0)
101 | image_data.file.write(optimized_buffer)
102 | image_data.file.truncate()
103 |
104 | return image_data
105 |
106 |
107 | def crop_image_on_axis(image, width, height, x, y, extension):
108 | """
109 | function to crop the image using axis (using Pillow).
110 | :param `image` is image data, e.g from request.FILES['image']
111 | :param `width` float width of image
112 | :param `height` float height of image
113 | :param `x` is float x axis
114 | :param `y` is float y axis
115 | :param `extension` is string, e.g: ".png"
116 | """
117 | # Open the passed image
118 | img = Image.open(image)
119 |
120 | # Initialise bytes io
121 | bytes_io = BytesIO()
122 |
123 | # crop the image through axis
124 | img = img.crop((x, y, width + x, height + y))
125 |
126 | # resize the image and optimise it for file size,
127 | # making smaller as possible
128 | img = img.resize((width, height), Image.ANTIALIAS)
129 |
130 | # This line is optional, for safe side, image name should be unique.
131 | img.name = "{}.{}".format(uuid4().hex, extension)
132 |
133 | # If the file extension is JPEG, convert the output_image to RGB
134 | if extension == "JPEG":
135 | img = image.convert("RGB")
136 | img.save(bytes_io, format=extension, optimize=True)
137 |
138 | # return the image
139 | image.seek(0)
140 |
141 | # Write back new image
142 | image.file.write(bytes_io.getvalue())
143 |
144 | # truncate the file size
145 | image.file.truncate()
146 | return image
147 |
--------------------------------------------------------------------------------
/image_optimizer_demo/app_demo/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/agusmakmun/django-image-optimizer/323659dc81cbdf2c068789550e81fa4b97ac2bc9/image_optimizer_demo/app_demo/__init__.py
--------------------------------------------------------------------------------
/image_optimizer_demo/app_demo/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from app_demo.models import (
3 | Post,
4 | Collaborator,
5 | OtherImage,
6 | )
7 | from app_demo.forms import CropImageAxisForm
8 |
9 |
10 | class PostAdmin(admin.ModelAdmin):
11 | list_display = ["title", "created"]
12 | list_filter = ["created"]
13 |
14 |
15 | class CollaboratorAdmin(admin.ModelAdmin):
16 | list_display = ["name", "created"]
17 | list_filter = ["created"]
18 |
19 |
20 | class CropImageAxisAdmin(admin.ModelAdmin):
21 | list_display = ["created", "image"]
22 | list_filter = ["created"]
23 | form = CropImageAxisForm
24 |
25 | def get_form(self, request, *args, **kwargs):
26 | form = super().get_form(request, *args, **kwargs)
27 | form.request = request
28 | return form
29 |
30 |
31 | admin.site.register(Post, PostAdmin)
32 | admin.site.register(Collaborator, CollaboratorAdmin)
33 | admin.site.register(OtherImage, CropImageAxisAdmin)
34 |
--------------------------------------------------------------------------------
/image_optimizer_demo/app_demo/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class AppDemoConfig(AppConfig):
5 | name = "app_demo"
6 |
--------------------------------------------------------------------------------
/image_optimizer_demo/app_demo/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from .models import OtherImage
3 | from image_optimizer.utils import crop_image_on_axis, get_file_extension
4 |
5 |
6 | class CropImageAxisForm(forms.ModelForm):
7 | width = forms.IntegerField()
8 | height = forms.IntegerField()
9 | x = forms.FloatField()
10 | y = forms.FloatField()
11 |
12 | def save(self, commit=True):
13 | instance = super().save(commit=False)
14 | request = self.request
15 |
16 | # process on create only
17 | image = request.FILES.get("image")
18 | if image is not None:
19 | width = float(request.POST["width"])
20 | height = float(request.POST["height"])
21 | x = float(request.POST["x"])
22 | y = float(request.POST["y"])
23 | extension = get_file_extension(image.name)
24 |
25 | try:
26 | image = crop_image_on_axis(image, width, height, x, y, extension)
27 | except ValueError as error:
28 | raise forms.ValidationError(error)
29 |
30 | instance.image = image
31 | instance.save()
32 | return instance
33 |
34 | return super().save(commit)
35 |
36 | class Meta:
37 | model = OtherImage
38 | fields = ["image"]
39 |
--------------------------------------------------------------------------------
/image_optimizer_demo/app_demo/migrations/.gitignore:
--------------------------------------------------------------------------------
1 | [^.]*
2 | !__init__.py
3 |
--------------------------------------------------------------------------------
/image_optimizer_demo/app_demo/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/agusmakmun/django-image-optimizer/323659dc81cbdf2c068789550e81fa4b97ac2bc9/image_optimizer_demo/app_demo/migrations/__init__.py
--------------------------------------------------------------------------------
/image_optimizer_demo/app_demo/models.py:
--------------------------------------------------------------------------------
1 | """app_demo models."""
2 | from django.db import models
3 | from image_optimizer.fields import OptimizedImageField
4 |
5 |
6 | class Post(models.Model):
7 | """
8 | Post model.
9 | This model represents a Blog Post with a few fields including a `photo`
10 | field which is an OptimizedImageField instance without any optional
11 | argument. This means that out Post photo would keep source image original
12 | size.
13 | """
14 |
15 | title = models.CharField(max_length=100)
16 | photo = OptimizedImageField(upload_to="uploads/posts/%Y/%m/%d")
17 | created = models.DateTimeField(auto_now_add=True)
18 |
19 | def __str__(self):
20 | return self.title
21 |
22 | class Meta:
23 | ordering = ["-created"]
24 |
25 |
26 | class Collaborator(models.Model):
27 | """
28 | Collaborator model.
29 | This model represents a Blog Collaborator(a.k.a. Writter) with a few
30 | fields including a `profile_image` field which is an OptimizedImageField
31 | instance with `optimized_image_output_size` and
32 | `optimized_image_resize_method` arguments set.
33 | This means that our Collaborator profile_image would be a resized
34 | version of the source image, meant to keep a given screen resolution,
35 | in this case (400, 300) pixels.
36 | """
37 |
38 | name = models.CharField(max_length=100)
39 | profile_image = OptimizedImageField(
40 | upload_to="uploads/collaborators/%Y/%m/%d",
41 | optimized_image_output_size=(400, 300),
42 | optimized_image_resize_method="cover", # 'thumbnail' or 'cover'
43 | )
44 | created = models.DateTimeField(auto_now_add=True)
45 |
46 | def __str__(self):
47 | return self.name
48 |
49 | class Meta:
50 | ordering = ["-created"]
51 |
52 |
53 | class OtherImage(models.Model):
54 | image = models.ImageField(upload_to="uploads/%Y/%m/%d")
55 | created = models.DateTimeField(auto_now_add=True)
56 |
57 | def __str__(self):
58 | return str(self.image)
59 |
60 | class Meta:
61 | ordering = ["-created"]
62 |
--------------------------------------------------------------------------------
/image_optimizer_demo/image_optimizer_demo/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/agusmakmun/django-image-optimizer/323659dc81cbdf2c068789550e81fa4b97ac2bc9/image_optimizer_demo/image_optimizer_demo/__init__.py
--------------------------------------------------------------------------------
/image_optimizer_demo/image_optimizer_demo/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for image_optimizer_demo project.
3 |
4 | Generated by 'django-admin startproject' using Django 2.0.7.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.0/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/2.0/ref/settings/
11 | """
12 |
13 | import os
14 |
15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 |
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = "*^mmp(4)=8vx5%aksf+$#446gs974xyp4--2&+zp(ezsq0oxxs"
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = True
27 |
28 | ALLOWED_HOSTS = ["*"]
29 |
30 | OPTIMIZED_IMAGE_METHOD = "pillow" # 'tinypng'
31 | TINYPNG_KEY = "key-key-key-key"
32 |
33 |
34 | # Application definition
35 |
36 | INSTALLED_APPS = [
37 | "django.contrib.admin",
38 | "django.contrib.auth",
39 | "django.contrib.contenttypes",
40 | "django.contrib.sessions",
41 | "django.contrib.messages",
42 | "django.contrib.staticfiles",
43 | "image_optimizer",
44 | "app_demo",
45 | ]
46 |
47 | MIDDLEWARE = [
48 | "django.middleware.security.SecurityMiddleware",
49 | "django.contrib.sessions.middleware.SessionMiddleware",
50 | "django.middleware.common.CommonMiddleware",
51 | "django.middleware.csrf.CsrfViewMiddleware",
52 | "django.contrib.auth.middleware.AuthenticationMiddleware",
53 | "django.contrib.messages.middleware.MessageMiddleware",
54 | "django.middleware.clickjacking.XFrameOptionsMiddleware",
55 | ]
56 |
57 | ROOT_URLCONF = "image_optimizer_demo.urls"
58 |
59 | TEMPLATES = [
60 | {
61 | "BACKEND": "django.template.backends.django.DjangoTemplates",
62 | "DIRS": [],
63 | "APP_DIRS": True,
64 | "OPTIONS": {
65 | "context_processors": [
66 | "django.template.context_processors.debug",
67 | "django.template.context_processors.request",
68 | "django.contrib.auth.context_processors.auth",
69 | "django.contrib.messages.context_processors.messages",
70 | ],
71 | },
72 | },
73 | ]
74 |
75 | WSGI_APPLICATION = "image_optimizer_demo.wsgi.application"
76 |
77 |
78 | # Database
79 | # https://docs.djangoproject.com/en/2.0/ref/settings/#databases
80 |
81 | DATABASES = {
82 | "default": {
83 | "ENGINE": "django.db.backends.sqlite3",
84 | "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
85 | }
86 | }
87 |
88 |
89 | # Password validation
90 | # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
91 |
92 | AUTH_PASSWORD_VALIDATORS = [
93 | {
94 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
95 | },
96 | {
97 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
98 | },
99 | {
100 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
101 | },
102 | {
103 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
104 | },
105 | ]
106 |
107 |
108 | # Internationalization
109 | # https://docs.djangoproject.com/en/2.0/topics/i18n/
110 |
111 | LANGUAGE_CODE = "en-us"
112 |
113 | TIME_ZONE = "UTC"
114 |
115 | USE_I18N = True
116 |
117 | USE_L10N = True
118 |
119 | USE_TZ = True
120 |
121 |
122 | # Static files (CSS, JavaScript, Images)
123 | # https://docs.djangoproject.com/en/2.0/howto/static-files/
124 |
125 |
126 | STATIC_URL = "/static/"
127 | MEDIA_URL = "/media/"
128 | STATIC_ROOT = os.path.join(BASE_DIR, "static")
129 | MEDIA_ROOT = os.path.join(BASE_DIR, "media")
130 |
--------------------------------------------------------------------------------
/image_optimizer_demo/image_optimizer_demo/urls.py:
--------------------------------------------------------------------------------
1 | """image_optimizer_demo URL Configuration
2 | The `urlpatterns` list routes URLs to views. For more information please see:
3 | https://docs.djangoproject.com/en/2.0/topics/http/urls/
4 | Examples:
5 | Function views
6 | 1. Add an import: from my_app import views
7 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
8 | Class-based views
9 | 1. Add an import: from other_app.views import Home
10 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
11 | Including another URLconf
12 | 1. Import the include() function: from django.urls import include, path
13 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
14 | """
15 | from django.contrib import admin
16 | from django.urls import path
17 | from django.conf import settings
18 | from django.conf.urls.static import static
19 |
20 | urlpatterns = (
21 | [
22 | path("admin/", admin.site.urls),
23 | ]
24 | + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
25 | + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
26 | )
27 |
--------------------------------------------------------------------------------
/image_optimizer_demo/image_optimizer_demo/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for image_optimizer_demo project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "image_optimizer_demo.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/image_optimizer_demo/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "image_optimizer_demo.settings")
7 | try:
8 | from django.core.management import execute_from_command_line
9 | except ImportError as exc:
10 | raise ImportError(
11 | "Couldn't import Django. Are you sure it's installed and "
12 | "available on your PYTHONPATH environment variable? Did you "
13 | "forget to activate a virtual environment?"
14 | ) from exc
15 | execute_from_command_line(sys.argv)
16 |
--------------------------------------------------------------------------------
/push.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo '';
4 | echo -n "[+] commit ➜ ";
5 | read commit;
6 |
7 | if [ "$commit" ]; then
8 | git add .;
9 | git commit -m "$commit";
10 | git push -u origin master;
11 | fi
12 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [flake8]
2 | ignore = E402,E501,W503,W504,E731,E741
3 | exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules
4 |
5 | [pycodestyle]
6 | max-line-length = 120
7 | exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules
8 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from setuptools import setup, find_packages
4 |
5 | __VERSION__ = "1.0.3"
6 | __AUTHOR__ = "Agus Makmun (Summon Agus)"
7 | __AUTHOR_EMAIL__ = "summon.agus@gmail.com"
8 |
9 | install_requires = [
10 | "Django",
11 | "Pillow",
12 | "requests",
13 | "tinify",
14 | "python-resize-image",
15 | ]
16 |
17 | setup(
18 | name="django-image-optimizer",
19 | version=__VERSION__,
20 | author=__AUTHOR__,
21 | author_email=__AUTHOR_EMAIL__,
22 | description="Django Image Optimizer (Compressor)",
23 | packages=find_packages(exclude=["*demo"]),
24 | include_package_data=True,
25 | zip_safe=False,
26 | url="https://github.com/agusmakmun/django-image-optimizer",
27 | download_url=(
28 | "https://github.com/agusmakmun/django-image-optimizer/tarball/v%s" % __VERSION__
29 | ),
30 | keywords=["image optimizer", "django image optimizer", "image optimizer"],
31 | long_description=open("README.rst").read(),
32 | license="MIT",
33 | classifiers=[
34 | "Development Status :: 5 - Production/Stable",
35 | "Environment :: Web Environment",
36 | "Framework :: Django",
37 | "Framework :: Django :: 1.8",
38 | "Framework :: Django :: 1.9",
39 | "Framework :: Django :: 1.10",
40 | "Framework :: Django :: 1.11",
41 | "Framework :: Django :: 2.0",
42 | "Intended Audience :: Developers",
43 | "Operating System :: OS Independent",
44 | "Programming Language :: Python",
45 | "Programming Language :: Python :: 2.7",
46 | "Programming Language :: Python :: 3.3",
47 | "Programming Language :: Python :: 3.5",
48 | "Programming Language :: Python :: 3.7",
49 | "Topic :: Software Development :: Libraries :: Python Modules",
50 | "License :: OSI Approved :: MIT License",
51 | ],
52 | install_requires=install_requires,
53 | )
54 |
--------------------------------------------------------------------------------