diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..d8e9240 --- /dev/null +++ b/__init__.py @@ -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', + ) + ) \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..9b6e14b --- /dev/null +++ b/config.py @@ -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() \ No newline at end of file diff --git a/database.py b/database.py new file mode 100644 index 0000000..8491cca --- /dev/null +++ b/database.py @@ -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() \ No newline at end of file diff --git a/forms.py b/forms.py new file mode 100644 index 0000000..ce42e8f --- /dev/null +++ b/forms.py @@ -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', + ) \ No newline at end of file diff --git a/tasks.py b/tasks.py new file mode 100644 index 0000000..b9ca8d9 --- /dev/null +++ b/tasks.py @@ -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() \ No newline at end of file diff --git a/templates/thumbnailer_settings.html b/templates/thumbnailer_settings.html new file mode 100644 index 0000000..001a569 --- /dev/null +++ b/templates/thumbnailer_settings.html @@ -0,0 +1,180 @@ +{% extends "settings_page.html" %} + +{% block header_script_files %} + +{% endblock %} + +{% block settings_pane %} + +
{{ settings_form.max_width.label.text }} | [[ settings.THUMBNAILER.max_width ]] | +
---|---|
{{ settings_form.quality.label.text }} | [[ settings.THUMBNAILER.quality ]] | +
{{ settings_form.save_original_file.label.text }} | [[ settings.THUMBNAILER.save_original_file ]] | +