Compare commits

..

10 Commits

23 changed files with 136 additions and 81 deletions

14
.gitignore vendored
View File

@ -2,15 +2,11 @@
**/__pycache__ **/__pycache__
# config # config
.vscode .vscode/
# data # data
instance instance/
partitioncloud/partitions
partitioncloud/search-partitions
partitioncloud/static/thumbnails
partitioncloud/static/search-thumbnails
partitioncloud/attachments
.venv
backups .venv/
backups/

View File

@ -7,11 +7,11 @@ init () {
mkdir -p "$INSTANCE_PATH/partitions" mkdir -p "$INSTANCE_PATH/partitions"
mkdir -p "$INSTANCE_PATH/attachments" mkdir -p "$INSTANCE_PATH/attachments"
mkdir -p "$INSTANCE_PATH/search-partitions" mkdir -p "$INSTANCE_PATH/search-partitions"
mkdir -p "$INSTANCE_PATH/static/thumbnails" mkdir -p "$INSTANCE_PATH/cache/thumbnails"
mkdir -p "$INSTANCE_PATH/static/search-thumbnails" mkdir -p "$INSTANCE_PATH/cache/search-thumbnails"
if ! test -f "$INSTANCE_PATH/config.py"; then if ! test -f "$INSTANCE_PATH/config.py"; then
echo "SECRET_KEY=$(python3 -c 'import secrets; print(secrets.token_hex())')" > "$INSTANCE_PATH/config.py" echo "SECRET_KEY=\"$(python3 -c 'import secrets; print(secrets.token_hex())')\"" > "$INSTANCE_PATH/config.py"
fi fi
if test -f "$INSTANCE_PATH/partitioncloud.sqlite"; then if test -f "$INSTANCE_PATH/partitioncloud.sqlite"; then
@ -45,7 +45,7 @@ usage () {
if [[ $1 && $(type "$1") = *"is a"*"function"* || $(type "$1") == *"est une fonction"* ]]; then if [[ $1 && $(type "$1") = *"is a"*"function"* || $(type "$1") == *"est une fonction"* ]]; then
# Import config # Import config
source "default_config.py" source "default_config.py"
[[ ! -x" $INSTANCE_PATH/config.py" ]] && source "$INSTANCE_PATH/config.py" [[ ! -x "$INSTANCE_PATH/config.py" ]] && source "$INSTANCE_PATH/config.py"
$1 ${*:2} # Call the function $1 ${*:2} # Call the function
else else
usage usage

View File

@ -12,7 +12,7 @@ from flask import Flask, g, redirect, render_template, request, send_file, flash
from werkzeug.security import generate_password_hash from werkzeug.security import generate_password_hash
from .modules.utils import User, Album, get_all_albums from .modules.utils import User, Album, get_all_albums
from .modules import albums, auth, partition, admin, groupe from .modules import albums, auth, partition, admin, groupe, thumbnails
from .modules.auth import admin_required, login_required from .modules.auth import admin_required, login_required
from .modules.db import get_db from .modules.db import get_db
@ -39,8 +39,9 @@ def load_config():
app.config.from_object(user_config) app.config.from_object(user_config)
if os.path.abspath(app.config["INSTANCE_PATH"]) != app.instance_path: if os.path.abspath(app.config["INSTANCE_PATH"]) != app.instance_path:
print("[ERROR] Using two different instance path. \ print(("[ERROR] Using two different instance path.\n"
\nPlease modify INSTANCE_PATH only in default_config.py and remove it from $INSTANCE_PATH/config.py") "Please modify INSTANCE_PATH only in default_config.py ",
"and remove it from $INSTANCE_PATH/config.py"))
sys.exit(1) sys.exit(1)
else: else:
print("[WARNING] Using default config") print("[WARNING] Using default config")
@ -66,6 +67,7 @@ app.register_blueprint(admin.bp)
app.register_blueprint(groupe.bp) app.register_blueprint(groupe.bp)
app.register_blueprint(albums.bp) app.register_blueprint(albums.bp)
app.register_blueprint(partition.bp) app.register_blueprint(partition.bp)
app.register_blueprint(thumbnails.bp)
__version__ = get_version() __version__ = get_version()
@ -107,35 +109,6 @@ def add_user():
return render_template("auth/register.html", albums=get_all_albums(), user=current_user) return render_template("auth/register.html", albums=get_all_albums(), user=current_user)
@app.route("/static/search-thumbnails/<uuid>.jpg")
@login_required
def search_thumbnail(uuid):
"""
Renvoie l'apercu d'un résultat de recherche
"""
db = get_db()
part = db.execute(
"""
SELECT uuid, url FROM search_results
WHERE uuid = ?
""",
(uuid,)
).fetchone()
if part is None:
abort(404)
if not os.path.exists(os.path.join(app.static_folder, "search-thumbnails", f"{uuid}.jpg")):
os.system(
f'/usr/bin/convert -thumbnail\
"178^>" -background white -alpha \
remove -crop 178x178+0+0 \
{app.instance_path}/search-partitions/{uuid}.pdf[0] \
partitioncloud/static/search-thumbnails/{uuid}.jpg'
)
return send_file(os.path.join(app.static_folder, "search-thumbnails", f"{uuid}.jpg"))
@app.before_request @app.before_request
def before_request(): def before_request():
"""Set cookie max age to 31 days""" """Set cookie max age to 31 days"""

View File

@ -299,13 +299,6 @@ def add_partition(album_uuid):
partition_path partition_path
) )
os.system(
f'/usr/bin/convert -thumbnail\
"178^>" -background white -alpha \
remove -crop 178x178+0+0 \
{partition_path}[0] \
partitioncloud/static/thumbnails/{partition_uuid}.jpg'
)
db.commit() db.commit()
album.add_partition(partition_uuid) album.add_partition(partition_uuid)

View File

@ -133,8 +133,8 @@ class Album():
attachment.delete(instance_path) attachment.delete(instance_path)
os.remove(f"{instance_path}/partitions/{partition['uuid']}.pdf") os.remove(f"{instance_path}/partitions/{partition['uuid']}.pdf")
if os.path.exists(f"partitioncloud/static/thumbnails/{partition['uuid']}.jpg"): if os.path.exists(f"{instance_path}/cache/thumbnails/{partition['uuid']}.jpg"):
os.remove(f"partitioncloud/static/thumbnails/{partition['uuid']}.jpg") os.remove(f"{instance_path}/cache/thumbnails/{partition['uuid']}.jpg")
partitions = db.execute( partitions = db.execute(
""" """

View File

@ -43,8 +43,8 @@ class Partition():
db.commit() db.commit()
os.remove(f"{instance_path}/partitions/{self.uuid}.pdf") os.remove(f"{instance_path}/partitions/{self.uuid}.pdf")
if os.path.exists(f"partitioncloud/static/thumbnails/{self.uuid}.jpg"): if os.path.exists(f"{instance_path}/cache/thumbnails/{self.uuid}.jpg"):
os.remove(f"partitioncloud/static/thumbnails/{self.uuid}.jpg") os.remove(f"{instance_path}/cache/thumbnails/{self.uuid}.jpg")
db.execute( db.execute(
""" """

View File

@ -150,14 +150,11 @@ def flush_cache(instance_path):
).fetchall() ).fetchall()
for element in expired_cache: for element in expired_cache:
uuid = element["uuid"] uuid = element["uuid"]
try: if os.path.exists(f"{instance_path}/search-partitions/{uuid}.pdf"):
os.remove(f"{instance_path}/search-partitions/{uuid}.pdf") os.remove(f"{instance_path}/search-partitions/{uuid}.pdf")
except FileNotFoundError:
pass if os.path.exists(f"{instance_path}/cache/search-thumbnails/{uuid}.jpg"):
try: os.remove(f"{instance_path}/cache/search-thumbnails/{uuid}.jpg")
os.remove(f"partitioncloud/static/search-thumbnails/{uuid}.jpg")
except FileNotFoundError:
pass
db.execute( db.execute(
""" """

View File

@ -0,0 +1,57 @@
"""
Thumbnails
"""
import os
from flask import current_app, abort, Blueprint, send_file
from .auth import login_required
bp = Blueprint("thumbnails", __name__, url_prefix="/thumbnails")
def generate_thumbnail(source, dest):
"""
Generates a thumbnail with 'convert' (ImageMagick)
"""
os.system(
f'/usr/bin/convert -thumbnail\
"178^>" -background white -alpha \
remove -crop 178x178+0+0 \
{source}[0] \
{dest}'
)
def serve_thumbnail(partition_file, thumbnail_file):
"""
Generates thumbnail if non-existent
"""
if not os.path.exists(partition_file):
abort(404)
if not os.path.exists(thumbnail_file):
generate_thumbnail(partition_file, thumbnail_file)
return send_file(thumbnail_file)
@bp.route("/search/<uuid>.jpg")
@login_required
def search_thumbnail(uuid):
"""
Renvoie l'apercu d'un résultat de recherche
"""
return serve_thumbnail(
os.path.join(current_app.instance_path, "search-partitions", f"{uuid}.pdf"),
os.path.join(current_app.instance_path, "cache", "search-thumbnails", f"{uuid}.jpg")
)
@bp.route("/<uuid>.jpg")
def regular_thumbnail(uuid):
"""
Renvoie l'apercu d'une partition déjà enregistrée
"""
return serve_thumbnail(
os.path.join(current_app.instance_path, "partitions", f"{uuid}.pdf"),
os.path.join(current_app.instance_path, "cache", "thumbnails", f"{uuid}.jpg")
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 KiB

After

Width:  |  Height:  |  Size: 210 KiB

View File

@ -1,5 +1,5 @@
{ {
"background_color": "#eff1f5", "background_color": "#1E1E2E",
"description": "Partitioncloud", "description": "Partitioncloud",
"display": "fullscreen", "display": "fullscreen",
"icons": [ "icons": [

View File

@ -356,6 +356,11 @@ img.partition-thumbnail {
transform: translateX(-22px) translateY(-115px); transform: translateX(-22px) translateY(-115px);
} }
.partition-name {
text-overflow: ellipsis;
overflow: hidden;
}
/** Albums grid in groupe view */ /** Albums grid in groupe view */
#albums-grid > a > .album { #albums-grid > a > .album {
@ -755,6 +760,9 @@ midi-player {
margin: 20px; margin: 20px;
margin-top: 50px; margin-top: 50px;
border-radius: 15px; border-radius: 15px;
width: 370px;
height: 370px;
background-color: white;
} }
#share-url { #share-url {

View File

@ -11,7 +11,7 @@
<div> <div>
<a href="/partition/{{ partition['uuid'] }}"> <a href="/partition/{{ partition['uuid'] }}">
<div class="partition" id="partition-{{ partition['uuid'] }}"> <div class="partition" id="partition-{{ partition['uuid'] }}">
<img class="partition-thumbnail" src="/static/thumbnails/{{ partition['uuid'] }}.jpg"> <img class="partition-thumbnail" src="/thumbnails/{{ partition['uuid'] }}.jpg" loading="lazy">
<div class="partition-description"> <div class="partition-description">
<div class="partition-name">{{ partition["name"] }}</div> <div class="partition-name">{{ partition["name"] }}</div>
<div class="partition-author">{{ partition["author"] }}</div> <div class="partition-author">{{ partition["author"] }}</div>

View File

@ -78,7 +78,7 @@
<div> <div>
<a href="/partition/{{ partition['uuid'] }}"> <a href="/partition/{{ partition['uuid'] }}">
<div class="partition" id="partition-{{ partition['uuid'] }}"> <div class="partition" id="partition-{{ partition['uuid'] }}">
<img class="partition-thumbnail" src="/static/thumbnails/{{ partition['uuid'] }}.jpg"> <img class="partition-thumbnail" src="/thumbnails/{{ partition['uuid'] }}.jpg" loading="lazy">
<div class="partition-description"> <div class="partition-description">
<div class="partition-name">{{ partition["name"] }}</div> <div class="partition-name">{{ partition["name"] }}</div>
<div class="partition-author">{{ partition["author"] }}</div> <div class="partition-author">{{ partition["author"] }}</div>

View File

@ -10,7 +10,7 @@
<div class="partition-container"> <div class="partition-container">
<a href="/partition/{{ partition['uuid'] }}"> <a href="/partition/{{ partition['uuid'] }}">
<div class="partition" id="partition-{{ partition['uuid'] }}"> <div class="partition" id="partition-{{ partition['uuid'] }}">
<img class="partition-thumbnail" src="/static/thumbnails/{{ partition['uuid'] }}.jpg"> <img class="partition-thumbnail" src="/thumbnails/{{ partition['uuid'] }}.jpg" loading="lazy">
<div class="partition-description"> <div class="partition-description">
<div class="partition-name">{{ partition["name"] }}</div> <div class="partition-name">{{ partition["name"] }}</div>
<div class="partition-author">{{ partition["author"] }}</div> <div class="partition-author">{{ partition["author"] }}</div>
@ -48,7 +48,7 @@
<div class="partition-container"> <div class="partition-container">
<a href="/partition/search/{{ partition['uuid'] }}"> <a href="/partition/search/{{ partition['uuid'] }}">
<div class="partition" id="partition-{{ partition['uuid'] }}"> <div class="partition" id="partition-{{ partition['uuid'] }}">
<img class="partition-thumbnail" src="/static/search-thumbnails/{{ partition['uuid'] }}.jpg"> <img class="partition-thumbnail" src="/thumbnails/search/{{ partition['uuid'] }}.jpg" loading="lazy">
<div class="partition-description"> <div class="partition-description">
<div class="partition-name">{{ partition["name"] }}</div> <div class="partition-name">{{ partition["name"] }}</div>
</div> </div>

View File

@ -1,6 +1,6 @@
<dialog id="share"> <dialog id="share">
<center> <center>
<img src="{{ share_qrlink }}" id="share-qrcode"><br/> <img src="{{ share_qrlink }}" id="share-qrcode" loading="lazy"><br/>
<div id="share-url" onclick='navigator.clipboard.writeText("{{ share_link }}")'> <div id="share-url" onclick='navigator.clipboard.writeText("{{ share_link }}")'>
{{ share_link }} {{ share_link }}
</div> </div>

View File

@ -50,7 +50,9 @@
</tr> </tr>
<tr> <tr>
<td>Fichier</td> <td>Fichier</td>
<td><a href="/partition/{{ partition.uuid }}"><img class="partition-thumbnail" src="/static/thumbnails/{{ partition.uuid }}.jpg"></a></td> <td><a href="/partition/{{ partition.uuid }}">
<img class="partition-thumbnail" src="/thumbnails/{{ partition.uuid }}.jpg" loading="lazy">
</a></td>
</tr> </tr>
<tr> <tr>
<td>Titre</td> <td>Titre</td>

View File

@ -11,7 +11,9 @@
<tbody> <tbody>
<tr> <tr>
<td>Fichier</td> <td>Fichier</td>
<td><a href="/partition/{{ partition.uuid }}"><img class="partition-thumbnail" src="/static/thumbnails/{{ partition.uuid }}.jpg"></a></td> <td><a href="/partition/{{ partition.uuid }}">
<img class="partition-thumbnail" src="/thumbnails/{{ partition.uuid }}.jpg" loading="lazy">
</a></td>
</tr> </tr>
{% if partition.source != "unknown" and partition.source != "upload" %} {% if partition.source != "unknown" and partition.source != "upload" %}
<tr> <tr>

5
scripts/hooks/config.py Normal file
View File

@ -0,0 +1,5 @@
"""
Config parameters, shared between files
"""
instance = "instance"

View File

@ -1,11 +1,17 @@
import os
import random import random
import string import string
import sqlite3 import sqlite3
from . import config
def run_sqlite_command(*args): def run_sqlite_command(*args):
"""Run a command against the database""" """Run a command against the database"""
con = sqlite3.connect("instance/partitioncloud.sqlite") con = sqlite3.connect(os.path.join(
config.instance,
"partitioncloud.sqlite"
))
cur = con.cursor() cur = con.cursor()
cur.execute(*args) cur.execute(*args)
con.commit() con.commit()
@ -14,7 +20,10 @@ def run_sqlite_command(*args):
def get_sqlite_data(*args): def get_sqlite_data(*args):
"""Get data from the db""" """Get data from the db"""
con = sqlite3.connect("instance/partitioncloud.sqlite") con = sqlite3.connect(os.path.join(
config.instance,
"partitioncloud.sqlite"
))
cur = con.cursor() cur = con.cursor()
data = cur.execute(*args) data = cur.execute(*args)
new_data = list(data) new_data = list(data)

View File

@ -1,9 +1,11 @@
import os import os
import shutil import shutil
import sqlite3 import sqlite3
from hooks import utils
from colorama import Fore, Style from colorama import Fore, Style
from . import utils
from . import config
""" """
v1.3.* v1.3.*
""" """
@ -159,5 +161,13 @@ def move_instance():
for path in paths: for path in paths:
shutil.move( shutil.move(
os.path.join("partitioncloud", path), os.path.join("partitioncloud", path),
os.path.join("instance", path) os.path.join(config.instance, path)
) )
def move_thumbnails():
shutil.rmtree("partitioncloud/static/thumbnails", ignore_errors=True)
shutil.rmtree("partitioncloud/static/search-thumbnails", ignore_errors=True)
os.makedirs(os.path.join(config.instance, "cache", "thumbnails"), exist_ok=True)
os.makedirs(os.path.join(config.instance, "cache", "search-thumbnails"), exist_ok=True)

View File

@ -6,7 +6,7 @@ import argparse
from functools import cmp_to_key from functools import cmp_to_key
from colorama import Fore, Style from colorama import Fore, Style
from hooks import v1 as v1_hooks from hooks import v1 as v1_hooks, config
def get_version(v: str) -> (int, int, int): def get_version(v: str) -> (int, int, int):
@ -33,7 +33,8 @@ hooks = [
], ],
), ),
("v1.4.1", [("Install qrcode", v1_hooks.install_qrcode)]), ("v1.4.1", [("Install qrcode", v1_hooks.install_qrcode)]),
("v1.5.0", [("Move to instance directory", v1_hooks.move_instance)]) ("v1.5.0", [("Move to instance directory", v1_hooks.move_instance)]),
("v1.5.1", [("Move thumbnails", v1_hooks.move_thumbnails)])
] ]
@ -73,7 +74,7 @@ def backup_instance(version, verbose=True):
os.makedirs(dest) os.makedirs(dest)
paths = [ paths = [
("instance", os.path.join(dest, "instance")), (config.instance, os.path.join(dest, "instance")),
( (
os.path.join("partitioncloud", "partitions"), os.path.join("partitioncloud", "partitions"),
os.path.join(dest, "partitions"), os.path.join(dest, "partitions"),
@ -155,7 +156,7 @@ def restore(version):
dest = os.path.join("backups", version) dest = os.path.join("backups", version)
print(f"Restoring from {dest}") print(f"Restoring from {dest}")
paths = [ paths = [
("instance", os.path.join(dest, "instance")), (config.instance, os.path.join(dest, "instance")),
( (
os.path.join("partitioncloud", "partitions"), os.path.join("partitioncloud", "partitions"),
os.path.join(dest, "partitions"), os.path.join(dest, "partitions"),
@ -191,6 +192,7 @@ if __name__ == "__main__":
parser.add_argument("-c", "--current", help="current version (vx.y.z)") parser.add_argument("-c", "--current", help="current version (vx.y.z)")
parser.add_argument("-t", "--target", help="target version (vx.y.z)") parser.add_argument("-t", "--target", help="target version (vx.y.z)")
parser.add_argument("-i", "--instance", help="instance folder", default="instance")
parser.add_argument("-s", "--skip-backup", action="store_true") parser.add_argument("-s", "--skip-backup", action="store_true")
parser.add_argument( parser.add_argument(
"-r", "-r",
@ -199,6 +201,7 @@ if __name__ == "__main__":
) )
args = parser.parse_args() args = parser.parse_args()
config.instance = os.path.abspath(args.instance)
if args.restore is None: if args.restore is None:
migrate(args.current, args.target, skip_backup=args.skip_backup) migrate(args.current, args.target, skip_backup=args.skip_backup)