parent
1ece5958b8
commit
21264da348
@ -0,0 +1,184 @@ |
||||
#!/usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
|
||||
"""------------------------------------------------------------------------------------------- |
||||
-- IMPORTS |
||||
-------------------------------------------------------------------------------------------""" |
||||
|
||||
""" --- PYTHON IMPORTS --- """ |
||||
import os, pathlib |
||||
from os.path import dirname, abspath |
||||
|
||||
""" --- STASHR CORE IMPORTS --- """ |
||||
from stashr import database as stashr_database |
||||
from stashr.stashr import stashr_image_downloaded |
||||
from stashr.api import create_json_return, api |
||||
|
||||
""" --- STASHR PLUGIN IMPORTS --- """ |
||||
from . import thumbnailer, tasks, forms |
||||
from .config import thumbconfig |
||||
|
||||
""" --- FLASK EXTENSION IMPORTS --- """ |
||||
from flask_login import current_user |
||||
|
||||
""" --- STASHR DEPENDENCY IMPORTS --- """ |
||||
from flask import Blueprint, render_template, request, flash, redirect, url_for |
||||
from flask_login import login_required, current_user |
||||
|
||||
"""------------------------------------------------------------------------------------------- |
||||
-- PLUGIN |
||||
-------------------------------------------------------------------------------------------""" |
||||
|
||||
# PLUGIN DETAILS |
||||
__plugin_name__ = "Image Thumbnailer" |
||||
__version__ = "0.1.0" |
||||
__author__ = "Stashr" |
||||
__description__ = "Shrink image filesize" |
||||
|
||||
"""------------------------------ |
||||
- PLUGIN ROUTES |
||||
------------------------------""" |
||||
|
||||
""" --- DEFINE BLUEPRINT --- """ |
||||
bp = Blueprint('thumbnailer', __name__, root_path=dirname(abspath(__file__)), template_folder='templates', static_folder='static') |
||||
|
||||
""" --- PAGES --- """ |
||||
|
||||
@bp.route('/settings', methods=['GET', 'POST']) |
||||
@login_required |
||||
def thumbnailer_settings_page(): |
||||
|
||||
if current_user.role != 'admin': |
||||
flash('Permission Denied', 'error') |
||||
return redirect(url_for('index_page')) |
||||
|
||||
settings_form = forms.settings_form() |
||||
|
||||
if request.method == 'POST': |
||||
if settings_form.validate(): |
||||
thumbconfig['THUMBNAILER']['max_width'] = settings_form.max_width.data |
||||
thumbconfig['THUMBNAILER']['quality'] = settings_form.quality.data |
||||
thumbconfig['THUMBNAILER']['save_original_file'] = settings_form.save_original_file.data |
||||
thumbconfig.write() |
||||
flash('Thumbnailer Settings Updated', 'success') |
||||
else: |
||||
for error in settings_form.errors.items(): |
||||
flash(f'{error[0]}: {error[1]}', 'error') |
||||
|
||||
return render_template( |
||||
'thumbnailer_settings.html', |
||||
settings_form=settings_form, |
||||
title='Thumbnailer Settings' |
||||
) |
||||
|
||||
|
||||
""" --- API --- """ |
||||
|
||||
# SCAN IMAGES |
||||
@bp.route('/api/scan', methods=['POST']) |
||||
def api_post_scan_images(): |
||||
|
||||
user = current_user |
||||
|
||||
if not user.is_authenticated: |
||||
if not request.json: |
||||
return create_json_return('400') |
||||
if "api_key" not in request.json: |
||||
return create_json_return('400') |
||||
if request.json['api_key'] == "": |
||||
return create_json_return('100') |
||||
|
||||
user = stashr_database.session \ |
||||
.query(stashr_database.Users) \ |
||||
.filter(stashr_database.Users.api_key == request.json['api_key']) \ |
||||
.first() |
||||
|
||||
if user is None: |
||||
return create_json_return('100') |
||||
|
||||
if user.role.lower() != 'admin': |
||||
return create_json_return('401') |
||||
|
||||
tasks.scan_images_task() |
||||
|
||||
return create_json_return('200') |
||||
|
||||
|
||||
# PROCESS IMAGES |
||||
@bp.route('/api/process/images', methods=['POST']) |
||||
def api_post_process_images(): |
||||
|
||||
user = current_user |
||||
|
||||
if not user.is_authenticated: |
||||
if not request.json: |
||||
return create_json_return('400') |
||||
if "api_key" not in request.json: |
||||
return create_json_return('400') |
||||
if request.json['api_key'] == "": |
||||
return create_json_return('100') |
||||
|
||||
user = stashr_database.session \ |
||||
.query(stashr_database.Users) \ |
||||
.filter(stashr_database.Users.api_key == request.json['api_key']) \ |
||||
.first() |
||||
|
||||
if user is None: |
||||
return create_json_return('100') |
||||
|
||||
if user.role.lower() != 'admin': |
||||
return create_json_return('401') |
||||
|
||||
tasks.process_images_task() |
||||
|
||||
return create_json_return('200') |
||||
|
||||
|
||||
@bp.route('/api/settings', methods=['GET']) |
||||
def api_get_settings(): |
||||
|
||||
user = current_user |
||||
|
||||
if not user.is_authenticated: |
||||
api_key = request.args.get('api_key') |
||||
if api_key == "": |
||||
return create_json_return('100') |
||||
user = stashr_database.session \ |
||||
.query(stashr_database.Users) \ |
||||
.filter(stashr_database.Users.api_key == api_key) \ |
||||
.first() |
||||
|
||||
if user is None: |
||||
return create_json_return('100') |
||||
|
||||
if user.role.lower() != 'admin': |
||||
return create_json_return('401') |
||||
|
||||
settings = thumbconfig |
||||
|
||||
return create_json_return('200', results=settings) |
||||
|
||||
|
||||
"""------------------------------ |
||||
- PLUGIN FUNCTIONS |
||||
------------------------------""" |
||||
|
||||
@stashr_image_downloaded.connect |
||||
def do_something(*args, **kwargs): |
||||
pass |
||||
|
||||
|
||||
"""------------------------------ |
||||
- REGISTER PLUGIN |
||||
------------------------------""" |
||||
|
||||
def register(): |
||||
return dict( |
||||
bep=dict( |
||||
blueprint=bp, |
||||
prefix='/thumbnailer' |
||||
), |
||||
tep = dict( |
||||
settings_menu='thumbnailer_tep_settings_menu.html', |
||||
) |
||||
) |
@ -0,0 +1,61 @@ |
||||
#!/usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
|
||||
"""------------------------------------------------------------------------------------------- |
||||
-- IMPORTS |
||||
-------------------------------------------------------------------------------------------""" |
||||
|
||||
""" --- PYTHON IMPORTS --- """ |
||||
import os, io, shutil |
||||
from os.path import dirname, abspath |
||||
|
||||
from configobj import ConfigObj |
||||
from validate import Validator |
||||
|
||||
"""------------------------------------------------------------------------------------------- |
||||
-- THUMBNAILER CONFIG |
||||
-------------------------------------------------------------------------------------------""" |
||||
|
||||
class ThumbConfig(ConfigObj): |
||||
|
||||
configspec = u""" |
||||
[THUMBNAILER] |
||||
max_width = integer(default=160) |
||||
quality = integer(default=50) |
||||
save_original_file = boolean(default=False) |
||||
""" |
||||
|
||||
def __init__(self): |
||||
super(ThumbConfig, self).__init__() |
||||
|
||||
configspecfile = os.path.join( |
||||
dirname(abspath(__file__)), |
||||
'configspec.ini' |
||||
) |
||||
|
||||
if not os.path.exists(configspecfile): |
||||
with open(configspecfile, 'w') as fd: |
||||
shutil.copyfileobj(io.StringIO(ThumbConfig.configspec), fd) |
||||
|
||||
self.filename = os.path.join( |
||||
dirname(abspath(__file__)), |
||||
'config.ini' |
||||
) |
||||
self.configspec = configspecfile |
||||
self.encoding = "UTF8" |
||||
|
||||
tmp = ConfigObj(self.filename, configspec=self.configspec, encoding=self.encoding) |
||||
validator = Validator() |
||||
tmp.validate(validator, copy=True) |
||||
|
||||
self.merge(tmp) |
||||
|
||||
if not os.path.exists(self.filename): |
||||
self.write() |
||||
|
||||
|
||||
"""------------------------------------------------------------------------------------------- |
||||
-- CONFIGURATION DEFINITION |
||||
-------------------------------------------------------------------------------------------""" |
||||
|
||||
thumbconfig = ThumbConfig() |
@ -0,0 +1,100 @@ |
||||
#!/usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
|
||||
"""------------------------------------------------------------------------------------------- |
||||
-- IMPORTS |
||||
-------------------------------------------------------------------------------------------""" |
||||
|
||||
""" --- HUEY IMPORT --- """ |
||||
|
||||
""" --- PYTHON IMPORTS --- """ |
||||
import os |
||||
from os.path import dirname, abspath |
||||
|
||||
""" --- STASHR DEPENDENCY IMPORTS --- """ |
||||
|
||||
""" --- STASHR CORE IMPORTS --- """ |
||||
from stashr import log |
||||
from stashr import database as stashrdb |
||||
|
||||
""" --- FLASK EXTENSION IMPORTS --- """ |
||||
|
||||
""" --- SQLALCHEMY IMPORTS --- """ |
||||
from sqlalchemy import * |
||||
from sqlalchemy.ext.declarative import declarative_base |
||||
from sqlalchemy.orm import * |
||||
|
||||
""" --- CREATE LOGGER --- """ |
||||
|
||||
""" --- STASHR PLUGIN IMPORTS --- """ |
||||
|
||||
""" --- CREATE LOGGER --- """ |
||||
logger = log.stashr_logger(__name__) |
||||
|
||||
""" --- CREATE SQLITE ENGINE --- """ |
||||
db_path = os.path.join(dirname(abspath(__file__)), 'database.db') |
||||
engine = create_engine('sqlite:///{0}'.format(db_path), |
||||
connect_args={'check_same_thread': False, 'timeout': 15}, |
||||
echo=False) |
||||
Base = declarative_base() |
||||
|
||||
from marshmallow_sqlalchemy import ModelSchema, SQLAlchemyAutoSchema, SQLAlchemySchema, auto_field |
||||
from marshmallow import fields |
||||
from marshmallow_sqlalchemy.fields import Nested |
||||
|
||||
|
||||
"""------------------------------------------------------------------------------------------- |
||||
-- DATABASE |
||||
-------------------------------------------------------------------------------------------""" |
||||
|
||||
class Images(Base): |
||||
__tablename__ = 'images' |
||||
|
||||
image_id = Column(Integer, primary_key=True) |
||||
|
||||
image_type = Column(String) |
||||
image_type_id = Column(Integer) |
||||
|
||||
image_filename = Column(String) |
||||
image_path = Column(String) |
||||
|
||||
image_original_size = Column(Integer) |
||||
image_processed_size = Column(Integer) |
||||
|
||||
image_original_width = Column(Integer) |
||||
image_original_height = Column(Integer) |
||||
|
||||
image_processed = Column(Boolean, server_default='0') |
||||
|
||||
"""------------------------------------------------------------------------------------------- |
||||
-- DATABASE SCHEMAS |
||||
-------------------------------------------------------------------------------------------""" |
||||
|
||||
class ImagesSchema(SQLAlchemyAutoSchema): |
||||
class Meta: |
||||
model = Images |
||||
|
||||
"""------------------------------------------------------------------------------------------- |
||||
-- DATABASE DEFINITION |
||||
-------------------------------------------------------------------------------------------""" |
||||
|
||||
Session = sessionmaker(bind=engine) |
||||
session = Session() |
||||
|
||||
"""------------------------------------------------------------------------------------------- |
||||
-- DATABASE FUNCTIONS |
||||
-------------------------------------------------------------------------------------------""" |
||||
|
||||
""" --- DATABASE MIGRATION --- """ |
||||
def migrate_database(): |
||||
# Check for new database tables |
||||
# Check for new table columns |
||||
pass |
||||
|
||||
""" --- CHECK DATABASE --- """ |
||||
if not os.path.exists(db_path): |
||||
logger.info('Creating Thumbnailer Database') |
||||
Base.metadata.create_all(engine) |
||||
else: |
||||
logger.info('Thumbnailer Database Exists') |
||||
migrate_database() |
@ -0,0 +1,47 @@ |
||||
#!/usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
|
||||
"""------------------------------------------------------------------------------------------- |
||||
-- IMPORTS |
||||
-------------------------------------------------------------------------------------------""" |
||||
|
||||
""" --- HUEY IMPORT --- """ |
||||
""" --- PYTHON IMPORTS --- """ |
||||
""" --- STASHR DEPENDENCY IMPORTS --- """ |
||||
""" --- STASHR CORE IMPORTS --- """ |
||||
from stashr import log, database |
||||
|
||||
""" --- FLASK EXTENSION IMPORTS --- """ |
||||
from flask_wtf import FlaskForm |
||||
|
||||
from wtforms import StringField, BooleanField, SelectField, IntegerField, HiddenField, TextAreaField, SubmitField |
||||
from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError, NumberRange |
||||
|
||||
""" --- CREATE LOGGER --- """ |
||||
logger = log.stashr_logger(__name__) |
||||
|
||||
"""------------------------------------------------------------------------------------------- |
||||
-- FORMS |
||||
-------------------------------------------------------------------------------------------""" |
||||
|
||||
class settings_form(FlaskForm): |
||||
|
||||
max_width = IntegerField( |
||||
'Max Image Width', |
||||
validators = [ |
||||
] |
||||
) |
||||
quality = IntegerField( |
||||
'Image Quality', |
||||
validators = [ |
||||
NumberRange(min=0, max=100, message='Enter a number between 0 and 100') |
||||
] |
||||
) |
||||
save_original_file = BooleanField( |
||||
'Backup Original Image Files', |
||||
validators = [ |
||||
] |
||||
) |
||||
settings_button = SubmitField( |
||||
'Save', |
||||
) |
@ -0,0 +1,38 @@ |
||||
#!/usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
|
||||
"""------------------------------------------------------------------------------------------- |
||||
-- IMPORTS |
||||
-------------------------------------------------------------------------------------------""" |
||||
|
||||
""" --- HUEY IMPORT --- """ |
||||
from stashr.stashr import huey |
||||
from huey import crontab |
||||
|
||||
""" --- PYTHON IMPORTS --- """ |
||||
|
||||
""" --- STASHR DEPENDENCY IMPORTS --- """ |
||||
|
||||
""" --- STASHR CORE IMPORTS --- """ |
||||
from stashr import log |
||||
|
||||
""" --- STASHR PLUGIN IMPORTS --- """ |
||||
from . import thumbnailer |
||||
|
||||
""" --- CREATE LOGGER --- """ |
||||
logger = log.stashr_logger(__name__) |
||||
|
||||
"""------------------------------------------------------------------------------------------- |
||||
-- THUMBNAILER TASKS |
||||
-------------------------------------------------------------------------------------------""" |
||||
|
||||
|
||||
@huey.task() |
||||
def scan_images_task(): |
||||
thumbnailer.get_images() |
||||
thumbnailer.get_image_stats() |
||||
|
||||
|
||||
@huey.task() |
||||
def process_images_task(): |
||||
thumbnailer.process_images() |
@ -0,0 +1,180 @@ |
||||
{% extends "settings_page.html" %} |
||||
|
||||
{% block header_script_files %} |
||||
<script src="https://unpkg.com/axios/dist/axios.min.js"></script> |
||||
{% endblock %} |
||||
|
||||
{% block settings_pane %} |
||||
|
||||
<div id="app"> |
||||
<settings ref="settings" v-bind:settings="settings"></settings> |
||||
<modals ref="modal" v-bind:settings="settings"></modals> |
||||
</div> |
||||
|
||||
{% endblock %} |
||||
|
||||
{% block script %} |
||||
|
||||
Vue.component('settings', { |
||||
props: ['settings'], |
||||
template: ` |
||||
<div class="row r-10 m-2"> |
||||
<div class="col col-12"> |
||||
<div class="row"> |
||||
<div class="col-sm-12 col-md-6 text-center text-md-start"> |
||||
<h2>Thumbnailer</h2> |
||||
</div> |
||||
</div> |
||||
<hr /> |
||||
<div class="row"> |
||||
<ul class="nav nav-tabs" id="myTab" role="tablist"> |
||||
<li class="nav-item" role="presentation"> |
||||
<button class="nav-link active" id="settings-tab" data-bs-toggle="tab" data-bs-target="#settings" type="button" role="tab" aria-controls="settings" aria-selected="true">Settings</button> |
||||
</li> |
||||
<li class="nav-item" role="presentation"> |
||||
<button class="nav-link" id="actions-tab" data-bs-toggle="tab" data-bs-target="#actions" type="button" role="tab" aria-controls="actions" aria-selected="false">Actions</button> |
||||
</li> |
||||
</ul> |
||||
<div class="tab-content py-2" id="myTabContent"> |
||||
<div class="tab-pane fade show active" id="settings" role="tabpanel" aria-labelledby="settings-tab"> |
||||
<div class="row"> |
||||
<table class="table table-striped"> |
||||
<tbody> |
||||
<tr> |
||||
<th scope="row">{{ settings_form.max_width.label.text }}</th><td>[[ settings.THUMBNAILER.max_width ]]</td> |
||||
</tr> |
||||
<tr> |
||||
<th scope="row">{{ settings_form.quality.label.text }}</th><td>[[ settings.THUMBNAILER.quality ]]</td> |
||||
</tr> |
||||
<tr> |
||||
<th scope="row">{{ settings_form.save_original_file.label.text }}</th><td>[[ settings.THUMBNAILER.save_original_file ]]</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
<div class="row w-100"> |
||||
<div class="col-12 text-end my-2"> |
||||
<button type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#settingsModal">Modify Settings</button> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div class="tab-pane fade" id="actions" role="tabpanel" aria-labelledby="actions-tab"> |
||||
<table class="table table-striped"> |
||||
<thead> |
||||
<tr> |
||||
<th scope="col">Actions</th> |
||||
<th scope="col"></th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr> |
||||
<th scope="row">Scan New Images</th><td><a onclick="scanImages()"><i class="fas fa-play"></i></a></td> |
||||
</tr> |
||||
<tr> |
||||
<th scope="row">Process All Images</th><td><a onclick="processImages()"><i class="fas fa-play"></i></a></td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
`, |
||||
methods: {}, |
||||
delimiters: ["[[","]]"] |
||||
}) |
||||
|
||||
Vue.component('modals',{ |
||||
props: ['settings'], |
||||
template: ` |
||||
<div> |
||||
<div class="modal" id="settingsModal" role="dialog" aria-labelledby="settingsModal" aria-hidden="true"> |
||||
<div class="modal-dialog modal-dialog-centered" role="document"> |
||||
<div class="modal-content"> |
||||
<div class="modal-header"> |
||||
<h5 class="modal-title" id="notesModalTitle"> |
||||
Modify App Settings |
||||
</h5> |
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> |
||||
</div> |
||||
|
||||
<form method="POST"> |
||||
<div class="modal-body"> |
||||
|
||||
{{ settings_form.csrf_token }} |
||||
|
||||
<div class="mb-3"> |
||||
{{ settings_form.max_width.label }} |
||||
{{ settings_form.max_width(class_='form-control', placeholder=settings_form.max_width.label.text) }} |
||||
</div> |
||||
<div class="mb-3"> |
||||
{{ settings_form.quality.label }} |
||||
{{ settings_form.quality(class_='form-control', placeholder=settings_form.quality.label.text) }} |
||||
</div> |
||||
<div class="mb-3"> |
||||
{{ settings_form.save_original_file }} |
||||
{{ settings_form.save_original_file.label }} |
||||
</div> |
||||
|
||||
</div> |
||||
<div class="modal-footer"> |
||||
{{ settings_form.settings_button(class_='btn btn-success') }} |
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> |
||||
</div> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
`, |
||||
methods: {}, |
||||
delimiters: ["[[","]]"] |
||||
}) |
||||
|
||||
var app = new Vue({ |
||||
el: '#app', |
||||
data: { |
||||
settings: [] |
||||
}, |
||||
created() { |
||||
this.getSettings() |
||||
}, |
||||
methods: { |
||||
getSettings() { |
||||
axios.get('{{ url_for('thumbnailer.api_get_settings') }}') |
||||
.then(res => { |
||||
this.settings = res.data.results; |
||||
document.getElementById('max_width').value = res.data.results.THUMBNAILER.max_width; |
||||
document.getElementById('quality').value = res.data.results.THUMBNAILER.quality; |
||||
document.getElementById('quality').checked = res.data.results.THUMBNAILER.settings_button; |
||||
}) |
||||
.catch(err => console.log(err)) |
||||
} |
||||
}, |
||||
delimiters: ["[[","]]"] |
||||
}) |
||||
|
||||
function scanImages() { |
||||
axios.post('{{ url_for('thumbnailer.api_post_scan_images') }}') |
||||
.then(res => { |
||||
if(res.data.status_code == 200) { |
||||
stashrToast('Scanning Images', 'success') |
||||
} else { |
||||
stashrToast('Error', 'error') |
||||
} |
||||
}) |
||||
} |
||||
|
||||
function processImages() { |
||||
axios.post('{{ url_for('thumbnailer.api_post_process_images') }}') |
||||
.then(res => { |
||||
if(res.data.status_code == 200) { |
||||
stashrToast('Processing Images', 'success') |
||||
} else { |
||||
stashrToast('Error', 'error') |
||||
} |
||||
}) |
||||
} |
||||
|
||||
{% endblock %} |
@ -0,0 +1,6 @@ |
||||
<li class="nav-item"> |
||||
<a class="nav-link{% if request.path == url_for('thumbnailer.thumbnailer_settings_page') %} active{% endif %}" href="{{ url_for('thumbnailer.thumbnailer_settings_page') }}"> |
||||
<i class="far fa-images"></i> |
||||
Thumbnailer |
||||
</a> |
||||
</li> |
@ -0,0 +1,153 @@ |
||||
#!/usr/bin/env python |
||||
# -*- coding: utf-8 -*- |
||||
|
||||
"""------------------------------------------------------------------------------------------- |
||||
-- IMPORTS |
||||
-------------------------------------------------------------------------------------------""" |
||||
|
||||
""" --- PYTHON IMPORTS --- """ |
||||
import os, pathlib, shutil |
||||
|
||||
""" --- STASHR CORE IMPORTS --- """ |
||||
from stashr import log, folders, utils |
||||
|
||||
""" --- STASHR PLUGIN IMPORTS --- """ |
||||
from . import database |
||||
from .config import thumbconfig |
||||
|
||||
""" --- STASHR PLUGIN DEPENDENCY IMPORTS --- """ |
||||
try: |
||||
from PIL import Image |
||||
except: |
||||
utils.install_package('pillow') |
||||
from PIL import Image |
||||
|
||||
""" --- CREATE LOGGER --- """ |
||||
logger = log.stashr_logger(__name__) |
||||
|
||||
"""------------------------------------------------------------------------------------------- |
||||
-- THUMBNAILER FUNCTIONS |
||||
-------------------------------------------------------------------------------------------""" |
||||
|
||||
|
||||
def get_images(): |
||||
|
||||
stored_images = database.session \ |
||||
.query(database.Images) \ |
||||
.all() |
||||
|
||||
stored_images = database.ImagesSchema(many=True).dump(stored_images) |
||||
|
||||
new_images = [] |
||||
existing_images = [] |
||||
|
||||
images_folder = folders.StashrFolders().images_folder() |
||||
|
||||
subdirs = os.scandir(images_folder) |
||||
for subdir in subdirs: |
||||
files = os.scandir(os.path.join(images_folder, subdir)) |
||||
for image in files: |
||||
existing_images.append(image) |
||||
for item in existing_images: |
||||
if not any(path['image_path'] == item.path for path in stored_images): |
||||
new_image = database.Images( |
||||
image_type = pathlib.Path(item.path).parent.name, |
||||
image_type_id = pathlib.Path(item.path).stem, |
||||
image_filename = item.name, |
||||
image_path = item.path, |
||||
) |
||||
new_images.append(new_image) |
||||
|
||||
database.session.bulk_save_objects(new_images) |
||||
database.session.commit() |
||||
|
||||
|
||||
def get_image_stats(): |
||||
|
||||
missing_stats = database.session \ |
||||
.query(database.Images) \ |
||||
.filter(database.Images.image_original_size == None) \ |
||||
.all() |
||||
|
||||
try: |
||||
for image in missing_stats: |
||||
with Image.open(image.image_path) as img: |
||||
width, height = img.size |
||||
|
||||
image.image_original_width = width |
||||
image.image_original_height = height |
||||
image.image_original_size = pathlib.Path(image.image_path).stat().st_size |
||||
|
||||
database.session.merge(image) |
||||
except Exception as e: |
||||
logger.error(e) |
||||
|
||||
database.session.commit() |
||||
|
||||
|
||||
def process_images(): |
||||
|
||||
non_processed = database.session \ |
||||
.query(database.Images) \ |
||||
.filter(database.Images.image_processed == False) \ |
||||
.all() |
||||
|
||||
for image in non_processed: |
||||
|
||||
thumbnail = pathlib.Path(new_process_image(image)) |
||||
|
||||
if thumbnail.stat().st_size == 0: |
||||
thumbnail.unlink() |
||||
continue |
||||
|
||||
if thumbconfig['THUMBNAILER']['save_original_file']: |
||||
backup_original_image(image.image_path) |
||||
|
||||
thumbnail.replace(image.image_path) |
||||
|
||||
image.image_processed = True |
||||
image.image_processed_size = pathlib.Path(image.image_path).stat().st_size |
||||
database.session.merge(image) |
||||
|
||||
database.session.commit() |
||||
|
||||
|
||||
def new_process_image(image): |
||||
|
||||
max_width = thumbconfig['THUMBNAILER']['max_width'] |
||||
quality = thumbconfig['THUMBNAILER']['quality'] |
||||
|
||||
ratio = image.image_original_height/image.image_original_width |
||||
size = (max_width, int(max_width*ratio)) |
||||
|
||||
thumbnail_file = os.path.join( |
||||
pathlib.Path(image.image_path).parent, |
||||
f'{pathlib.Path(image.image_path).stem}-thumbnail.jpg' |
||||
) |
||||
try: |
||||
with Image.open(image.image_path) as img: |
||||
img.thumbnail(size) |
||||
img.save(thumbnail_file, quality=quality) |
||||
finally: |
||||
return thumbnail_file |
||||
|
||||
|
||||
def backup_original_image(original_file_path): |
||||
|
||||
original_file_path = pathlib.Path(original_file_path) |
||||
|
||||
backup_folder = os.path.join( |
||||
folders.StashrFolders().backup_folder(), |
||||
'images', |
||||
original_file_path.parent.name |
||||
) |
||||
|
||||
if not os.path.isdir(backup_folder): |
||||
os.makedirs(backup_folder) |
||||
|
||||
backup_file = pathlib.Path(os.path.join( |
||||
backup_folder, |
||||
original_file_path.name |
||||
)) |
||||
|
||||
shutil.copy(original_file_path, backup_file) |
Loading…
Reference in new issue