store post created_at timestamp, add sort by date option
This commit is contained in:
@@ -27,7 +27,8 @@ def serve_preview(filename):
|
|||||||
def slideshow():
|
def slideshow():
|
||||||
raw_query = request.args.get('tags', '').strip()
|
raw_query = request.args.get('tags', '').strip()
|
||||||
selected_site = request.args.get('_site', 'e621')
|
selected_site = request.args.get('_site', 'e621')
|
||||||
results = search_images(raw_query)
|
sort = request.args.get('_sort', '')
|
||||||
|
results = search_images(raw_query, sort=sort or None)
|
||||||
base_dir = os.path.dirname(os.path.abspath(__file__))
|
base_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
pictures_dir = os.path.join(base_dir, 'Pictures')
|
pictures_dir = os.path.join(base_dir, 'Pictures')
|
||||||
previews_dir = os.path.join(base_dir, 'Previews')
|
previews_dir = os.path.join(base_dir, 'Previews')
|
||||||
@@ -68,6 +69,7 @@ def slideshow():
|
|||||||
active_tags=active_tags,
|
active_tags=active_tags,
|
||||||
tag_query=raw_query,
|
tag_query=raw_query,
|
||||||
selected_site=selected_site,
|
selected_site=selected_site,
|
||||||
|
sort=sort,
|
||||||
job_id=job_id,
|
job_id=job_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
19
db.py
19
db.py
@@ -1,5 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
DB_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'booru.db')
|
DB_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'booru.db')
|
||||||
|
|
||||||
@@ -34,6 +35,11 @@ def init_db():
|
|||||||
c.execute("ALTER TABLE images ADD COLUMN preview_filename TEXT NOT NULL DEFAULT ''")
|
c.execute("ALTER TABLE images ADD COLUMN preview_filename TEXT NOT NULL DEFAULT ''")
|
||||||
except sqlite3.OperationalError:
|
except sqlite3.OperationalError:
|
||||||
pass
|
pass
|
||||||
|
# migrate existing DBs that predate the created_at column
|
||||||
|
try:
|
||||||
|
c.execute("ALTER TABLE images ADD COLUMN created_at INTEGER NOT NULL DEFAULT 0")
|
||||||
|
except sqlite3.OperationalError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def image_exists(site, post_id):
|
def image_exists(site, post_id):
|
||||||
@@ -46,11 +52,11 @@ def image_exists(site, post_id):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def insert_image(post_id, site, filename, tags, file_url, post_url, preview_filename=''):
|
def insert_image(post_id, site, filename, tags, file_url, post_url, preview_filename='', created_at=0):
|
||||||
with get_conn() as c:
|
with get_conn() as c:
|
||||||
c.execute(
|
c.execute(
|
||||||
"INSERT OR IGNORE INTO images (post_id, site, filename, tags, file_url, post_url, preview_filename) VALUES (?,?,?,?,?,?,?)",
|
"INSERT OR IGNORE INTO images (post_id, site, filename, tags, file_url, post_url, preview_filename, created_at) VALUES (?,?,?,?,?,?,?,?)",
|
||||||
(post_id, site, filename, tags, file_url, post_url, preview_filename),
|
(post_id, site, filename, tags, file_url, post_url, preview_filename, created_at),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -59,18 +65,19 @@ def get_image_count():
|
|||||||
return c.execute('SELECT COUNT(*) FROM images').fetchone()[0]
|
return c.execute('SELECT COUNT(*) FROM images').fetchone()[0]
|
||||||
|
|
||||||
|
|
||||||
def search_images(tag_query):
|
def search_images(tag_query, sort=None):
|
||||||
terms = tag_query.split() if tag_query.strip() else []
|
terms = tag_query.split() if tag_query.strip() else []
|
||||||
|
order = 'created_at DESC' if sort == 'newest' else 'created_at ASC' if sort == 'oldest' else 'id ASC'
|
||||||
with get_conn() as c:
|
with get_conn() as c:
|
||||||
if not terms:
|
if not terms:
|
||||||
rows = c.execute(
|
rows = c.execute(
|
||||||
"SELECT filename, preview_filename, post_url, tags FROM images ORDER BY id"
|
f"SELECT filename, preview_filename, post_url, tags FROM images ORDER BY {order}"
|
||||||
).fetchall()
|
).fetchall()
|
||||||
else:
|
else:
|
||||||
where = ' AND '.join(['tags LIKE ?'] * len(terms))
|
where = ' AND '.join(['tags LIKE ?'] * len(terms))
|
||||||
params = [f'%{t}%' for t in terms]
|
params = [f'%{t}%' for t in terms]
|
||||||
rows = c.execute(
|
rows = c.execute(
|
||||||
f"SELECT filename, preview_filename, post_url, tags FROM images WHERE {where} ORDER BY id",
|
f"SELECT filename, preview_filename, post_url, tags FROM images WHERE {where} ORDER BY {order}",
|
||||||
params,
|
params,
|
||||||
).fetchall()
|
).fetchall()
|
||||||
return [{'filename': r[0], 'preview_filename': r[1], 'post_url': r[2], 'tags': r[3]} for r in rows]
|
return [{'filename': r[0], 'preview_filename': r[1], 'post_url': r[2], 'tags': r[3]} for r in rows]
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import argparse
|
import argparse
|
||||||
|
from datetime import datetime, timezone
|
||||||
import requests
|
import requests
|
||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
@@ -16,14 +17,22 @@ def parse_e621(post):
|
|||||||
if not file_url:
|
if not file_url:
|
||||||
return None
|
return None
|
||||||
tags = ' '.join(t for cat in post.get('tags', {}).values() for t in cat)
|
tags = ' '.join(t for cat in post.get('tags', {}).values() for t in cat)
|
||||||
return str(post['id']), file_url, tags
|
try:
|
||||||
|
created_at = int(datetime.fromisoformat(post['created_at']).timestamp())
|
||||||
|
except Exception:
|
||||||
|
created_at = 0
|
||||||
|
return str(post['id']), file_url, tags, created_at
|
||||||
|
|
||||||
|
|
||||||
def parse_moebooru(post):
|
def parse_moebooru(post):
|
||||||
file_url = post.get('file_url')
|
file_url = post.get('file_url')
|
||||||
if not file_url:
|
if not file_url:
|
||||||
return None
|
return None
|
||||||
return str(post['id']), file_url, post.get('tags', '')
|
try:
|
||||||
|
created_at = int(post.get('created_at', 0))
|
||||||
|
except Exception:
|
||||||
|
created_at = 0
|
||||||
|
return str(post['id']), file_url, post.get('tags', ''), created_at
|
||||||
|
|
||||||
|
|
||||||
SITES = {
|
SITES = {
|
||||||
@@ -144,7 +153,7 @@ def download_one(post, site_name, adapter, pictures_dir, previews_dir, session):
|
|||||||
parsed = adapter['parse'](post)
|
parsed = adapter['parse'](post)
|
||||||
if not parsed:
|
if not parsed:
|
||||||
return 'skip:no_url'
|
return 'skip:no_url'
|
||||||
post_id, file_url, tags = parsed
|
post_id, file_url, tags, created_at = parsed
|
||||||
if image_exists(site_name, post_id):
|
if image_exists(site_name, post_id):
|
||||||
return f'skip:dup:{post_id}'
|
return f'skip:dup:{post_id}'
|
||||||
clean_url = file_url.split('?')[0]
|
clean_url = file_url.split('?')[0]
|
||||||
@@ -157,7 +166,7 @@ def download_one(post, site_name, adapter, pictures_dir, previews_dir, session):
|
|||||||
f.write(r.content)
|
f.write(r.content)
|
||||||
post_url = adapter['post_url_fmt'].format(post_id=post_id)
|
post_url = adapter['post_url_fmt'].format(post_id=post_id)
|
||||||
preview_filename = make_preview(dest, previews_dir)
|
preview_filename = make_preview(dest, previews_dir)
|
||||||
insert_image(post_id, site_name, filename, tags, file_url, post_url, preview_filename)
|
insert_image(post_id, site_name, filename, tags, file_url, post_url, preview_filename, created_at)
|
||||||
return f'ok:{filename}'
|
return f'ok:{filename}'
|
||||||
|
|
||||||
|
|
||||||
@@ -207,7 +216,7 @@ def main():
|
|||||||
parsed = adapter['parse'](post)
|
parsed = adapter['parse'](post)
|
||||||
if not parsed:
|
if not parsed:
|
||||||
continue
|
continue
|
||||||
post_id, _, _ = parsed
|
post_id, _, _, _ = parsed
|
||||||
if image_exists(args.site, post_id):
|
if image_exists(args.site, post_id):
|
||||||
skipped += 1
|
skipped += 1
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
#tag-form button{background:#444;color:#fff}
|
#tag-form button{background:#444;color:#fff}
|
||||||
.tag-chip{display:inline-block;background:#335;border-radius:1rem;padding:.2rem .7rem;margin:.2rem;font-size:.85rem}
|
.tag-chip{display:inline-block;background:#335;border-radius:1rem;padding:.2rem .7rem;margin:.2rem;font-size:.85rem}
|
||||||
#no-results{font-size:1.4rem;margin-top:10vh;color:#888}
|
#no-results{font-size:1.4rem;margin-top:10vh;color:#888}
|
||||||
#site-select{padding:.5rem .6rem;font-size:1rem;border-radius:.4rem;border:0;background:#333;color:#fff;margin-left:.4rem}
|
#site-select,#sort-select{padding:.5rem .6rem;font-size:1rem;border-radius:.4rem;border:0;background:#333;color:#fff;margin-left:.4rem}
|
||||||
#view{display:flex;justify-content:center;align-items:flex-start;margin-top:2vh;gap:1rem}
|
#view{display:flex;justify-content:center;align-items:flex-start;margin-top:2vh;gap:1rem}
|
||||||
#tag-sidebar{width:180px;max-height:80vh;overflow-y:auto;background:#1a1a1a;border-radius:.5rem;padding:.5rem;flex-shrink:0;text-align:left}
|
#tag-sidebar{width:180px;max-height:80vh;overflow-y:auto;background:#1a1a1a;border-radius:.5rem;padding:.5rem;flex-shrink:0;text-align:left}
|
||||||
.tag-row{display:flex;align-items:baseline;gap:.3rem}
|
.tag-row{display:flex;align-items:baseline;gap:.3rem}
|
||||||
@@ -69,6 +69,11 @@
|
|||||||
<option value="yandere" {% if selected_site == 'yandere' %}selected{% endif %}>yandere</option>
|
<option value="yandere" {% if selected_site == 'yandere' %}selected{% endif %}>yandere</option>
|
||||||
<option value="rule34" {% if selected_site == 'rule34' %}selected{% endif %}>rule34.xxx</option>
|
<option value="rule34" {% if selected_site == 'rule34' %}selected{% endif %}>rule34.xxx</option>
|
||||||
</select>
|
</select>
|
||||||
|
<select name="_sort" id="sort-select">
|
||||||
|
<option value="" {% if not sort %}selected{% endif %}>default order</option>
|
||||||
|
<option value="newest" {% if sort == 'newest' %}selected{% endif %}>newest first</option>
|
||||||
|
<option value="oldest" {% if sort == 'oldest' %}selected{% endif %}>oldest first</option>
|
||||||
|
</select>
|
||||||
<button type="button" id="get-btn">Get Images</button>
|
<button type="button" id="get-btn">Get Images</button>
|
||||||
</form>
|
</form>
|
||||||
<div id="progress-box">
|
<div id="progress-box">
|
||||||
|
|||||||
Reference in New Issue
Block a user