PK!JJimagedb/__init__.pyfrom flask import Flask import os import re from threading import Thread from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from . import db from .util import open_browser_tab app = Flask(__name__) config = dict() class ImageDB: def __init__(self, db_path): global config config['image_db'] = self self.db_folder = os.path.splitext(db_path)[0] self.engine = create_engine('sqlite:///' + db_path) Session = sessionmaker(bind=self.engine) self.session = Session() if not os.path.exists(db_path): db.Base.metadata.create_all(self.engine) @staticmethod def run_server(host='localhost', port='8000', debug=False): open_browser_tab('http://{}:{}'.format(host, port)) app_thread = Thread(target=app.run, kwargs=dict( host=host, port=port, debug=debug )) app_thread.daemon = True app_thread.start() def mark(self, db_image, tag_or_tags='marked'): def _mark(tag): db_tag = self.session.query(db.Tag).filter_by(name=tag).first() if db_tag is None: db_tag = db.Tag() db_tag.name = tag self.session.add(db_tag) self.session.commit() db_tic = self.session.query(db.TagImageConnect).filter_by(tag_id=db_tag.id, image_id=db_image.id).first() if db_tic is None: db_tic = db.TagImageConnect() db_tic.tag_id = db_tag.id db_tic.card_id = db_tic.id self.session.add(db_tic) self.session.commit() else: pass # raise ValueError('The card is already marked by "{}".'.format(tag)) return db_tag if isinstance(tag_or_tags, str): yield _mark(tag_or_tags) else: for x in tag_or_tags: yield _mark(x) def unmark(self, db_image, tag_or_tags='marked'): def _unmark(tag): db_tag = self.session.query(db.Tag).filter_by(name=tag).first() if db_tag is None: raise ValueError('Cannot unmark "{}"'.format(tag)) # return db_tic = self.session.query(db.TagImageConnect).filter_by(tag_id=db_tag.id, card_id=db_image.id).first() if db_tic is None: raise ValueError('Cannot unmark "{}"'.format(tag)) # return else: self.session.delete(db_tic) self.session.commit() yield db_tag if isinstance(tag_or_tags, str): yield _unmark(tag_or_tags) else: for x in tag_or_tags: yield _unmark(x) def search(self, content=None, tag_or_tags=None, type_='partial'): def _compare(text, text_compare): if type_ == 'partial': return text_compare in text elif type_ in {'regex', 'regexp', 're'}: return re.search(text_compare, text, flags=re.IGNORECASE) else: return text_compare == text def _filter_tag(text_compare): for db_image in query: if any(_compare(tic.tag.name, text_compare) for tic in db_image.tags): yield db_image def _filter_slide(text_compare): for db_card in query: if any(_compare(db_image.info, text_compare) for db_image in query if db_image.info): yield db_card query = iter(self.session.query(db.Image).order_by(db.Image.modified.desc())) if tag_or_tags: if isinstance(tag_or_tags, str): query = _filter_tag(tag_or_tags) else: for tag in tag_or_tags: query = _filter_tag(tag) if content: query = _filter_slide(content) return list(query) from .views import * from .api import * PK!SgP P imagedb/api.pyfrom flask import request, jsonify, Response from werkzeug.utils import secure_filename from pathlib import Path from nonrepeat import nonrepeat_filename import shutil from slugify import slugify from uuid import uuid4 from . import app, db, config filename = None @app.route('/api/images/create', methods=['POST']) def create_image(): global filename if 'file' in request.files: image_path = Path(config['image_db'].db_folder).joinpath(request.form.get('imagePath')) tags = request.form.get('tags') if image_path.suffix: image_path = image_path.parent file = request.files['file'] if file.filename == 'image.png': filename = 'blob/' + str(uuid4()) + '.png' image_path.joinpath('blob').mkdir(parents=True, exist_ok=True) else: filename = secure_filename(file.filename) image_path.mkdir(parents=True, exist_ok=True) filename = str(image_path.joinpath(filename) .relative_to(config['image_db'].db_folder)) filename = nonrepeat_filename(filename, primary_suffix=slugify('-'.join(tags)), root=str(image_path)) db_image = db.Image() db_image.filename = filename config['image_db'].mark(db_image, tags) config['image_db'].session.add(db_image) config['image_db'].session.commit() true_filename = str(image_path.joinpath(filename)) file.save(true_filename) return jsonify({ 'filename': filename, 'trueFilename': true_filename }), 201 return Response(status=304) @app.route('/api/images/rename', methods=['POST']) def rename_image(): global filename db_image = config['image_db'].session.query(db.Image).filter_by(filename=filename).first() if filename is not None and db_image is not None: post_json = request.get_json() new_filename = Path(post_json['filename'])\ .with_suffix(Path(filename).suffix) new_filename.parent.mkdir(parents=True, exist_ok=True) new_filename = new_filename.with_name(secure_filename(new_filename.name)) new_filename = nonrepeat_filename(str(new_filename), root=config['image_db'].db_folder) true_filename = str(Path(config['image_db'].db_folder).joinpath(new_filename)) shutil.move(str(Path(config['image_db'].db_folder).joinpath(filename)), true_filename) filename = new_filename db_image.filename = filename config['image_db'].mark(db_image, post_json['tags']) config['image_db'].session.commit() return jsonify({ 'filename': filename, 'trueFilename': true_filename }), 201 return Response(status=304) PK!RQ[[ imagedb/db.pyfrom datetime import datetime import os from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, select from sqlalchemy.orm import relationship, deferred Base = declarative_base() class Image(Base): __tablename__ = 'image' id = Column(Integer, primary_key=True, autoincrement=True) filename = Column(String, nullable=False, unique=True) info = Column(String, nullable=True, unique=True) created = Column(DateTime, default=datetime.now) modified = Column(DateTime, default=datetime.now, onupdate=datetime.now) tags = relationship('TagImageConnect', order_by='TagImageConnect.tag_name', back_populates='image') def to_html(self): from . import config return ''.format(os.path.join(config['image_db'].db_folder, self.filename)) def _repr_html_(self): return self.to_html() class Tag(Base): __tablename__ = 'tag' id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String, nullable=False) images = relationship('TagImageConnect', order_by='TagImageConnect.id', back_populates='tag') class TagImageConnect(Base): __tablename__ = 'tag_image_connect' id = Column(Integer, primary_key=True, autoincrement=True) image_id = Column(Integer, ForeignKey('image.id'), nullable=False) tag_id = Column(Integer, ForeignKey('tag.id'), nullable=False) image = relationship('Image', back_populates='tags') tag = relationship('Tag', back_populates='images') tag_name = deferred(select([Tag.name]).where(Tag.id == tag_id)) PK!"imagedb/static/index.cssinput { font-size: 24px; margin: 0 auto; margin-top: 20px; } #tags-area, #input-bar { width: 500px; } #tags-bar { width: 100%; } img { margin-top: 20px; } PK!=k k imagedb/static/index.jsconst tagsBar = document.getElementById('tags-bar'); const inputBar = document.getElementById('input-bar'); const imageCanvas = document.getElementById('image-canvas'); let tags = []; let imagePath = ''; inputBar.value = imagePath; inputBar.onpaste = ()=>{ const items = (event.clipboardData || event.originalEvent.clipboardData).items; // console.log(items); // will give you the mime types for (index in items) { const item = items[index]; if (item.kind === 'file') { const file = item.getAsFile(); console.log(file); let reader = new FileReader(); reader.onload = function(event) { const extension = file.type.match(/\/([a-z0-9]+)/i)[1].toLowerCase(); let formData = new FormData(); formData.append('file', file, file.name); formData.append('extension', extension); formData.append('mimetype', file.type); formData.append('submission-type', 'paste'); formData.append('imagePath', imagePath); formData.append('tags', tags); fetch('/api/images/create', { method: 'POST', body: formData }).then(response=>response.json()) .then(responseJson=>{ inputBar.value = responseJson.filename; imageCanvas.src = '/images?filename=' + encodeURIComponent(responseJson.trueFilename); }); }; reader.readAsBinaryString(file); } } } inputBar.addEventListener("keydown", function(event) { imagePath = inputBar.value; }); inputBar.addEventListener("keyup", function(event) { if (event.keyCode === 13) { fetch('/api/images/rename', { method: 'POST', headers: { "Content-Type": "application/json; charset=utf-8", }, body: JSON.stringify({ 'filename': imagePath, 'tags': tags }) }).then(response=>response.json()) .then(responseJson=>{ inputBar.value = responseJson.filename; imageCanvas.src = '/images?filename=' + encodeURIComponent(responseJson.trueFilename); alert('Renaming successful!'); }); } }); tagsBar.addEventListener("keydown", function(event) { function purge(tag){ tag = tag.trim(); if(tag){ tags.push(tag); } } tags = []; let purgable = true; let currentTag = '' tagsBar.value.split('').forEach((character, index)=>{ if(character === ',' && purgable){ if(purgable){ purge(currentTag); currentTag = ''; } else { currentTag += character; } } else if (character === '\"'){ purgable = !purgable; } else { currentTag += character; } }); purge(currentTag); }) PK!Z imagedb/templates/index.html

PK!imagedb/util.pyfrom threading import Thread from time import sleep import webbrowser def open_browser_tab(url): def _open_tab(): sleep(1) webbrowser.open_new_tab(url) thread = Thread(target=_open_tab) thread.daemon = True thread.start() PK!x::imagedb/views.pyfrom flask import render_template, send_file, request import os from urllib.parse import unquote from . import app, config @app.route('/') def index(): return render_template('index.html') @app.route('/images') def get_image(): return send_file(os.path.abspath(unquote(request.args.get('filename')))) PK!H WXimagedb-0.1.2.dist-info/WHEEL A н#Z."jm)Afb~ڠO68oF04UhoAf f4=4h0k::wXPK!H imagedb-0.1.2.dist-info/METADATAN0E /AJ$V % *`=M'?R;QǦ*;:H?yFBw[7D. 7HŊ@jRn-]رjL۠H^>/%[SƟx﻽uqZuR@உf+SMWgZiLӇ>.C>5&XL4z1l' |'y|ۛ\dɲ9aq"t&O#[R#6E!?oISo1 TG}}PK!Hvs )imagedb-0.1.2.dist-info/RECORDuIv@yЩ(vD))P TV&w{IR?!$0G̓k3$*pwSwvS;ܙXM'A~,Ԑ7кVnS߾8t+%05vbL9Qߔ|= ݨ^{(i7P/z8PKibIhS>AdyBGJzxmͪ%U,vkT/_yKV[?[=97vlDUcjJpCf*ѡ9AIFDD-=Eɺ2;Fʷ«y(r *B^1)(Rɜ74V9$X% bD1ߟ໲mt8; #0G iOf=QQX1=JnC?.XI6i#v CN4N`F`