fix(books): tri des séries par volume + suppression de l'ancienne extract_page
- Ajout de `b.volume NULLS LAST` comme première clé de tri dans list_books et dans tous les ROW_NUMBER() OVER (...) des CTEs series, pour corriger l'ordre des volumes dont les titres varient en format (ex: "Round" vs "R") - Suppression de l'ancienne extract_page publique et de ses 4 helpers (extract_cbz_page_n, extract_cbz_page_n_streaming, extract_cbr_page_n, extract_pdf_page_n) remplacés par la nouvelle implémentation avec cache - Suppression de archive_index_cache dans AppState (remplacé par le cache statique CBZ_INDEX_CACHE dans parsers), import StdMutex nettoyé Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -141,7 +141,7 @@ pub async fn list_books(
|
||||
let order_clause = if query.sort.as_deref() == Some("latest") {
|
||||
"b.updated_at DESC".to_string()
|
||||
} else {
|
||||
"REGEXP_REPLACE(LOWER(b.title), '[0-9]+', '', 'g'), COALESCE((REGEXP_MATCH(LOWER(b.title), '\\d+'))[1]::int, 0), b.title ASC".to_string()
|
||||
"b.volume NULLS LAST, REGEXP_REPLACE(LOWER(b.title), '[0-9]+', '', 'g'), COALESCE((REGEXP_MATCH(LOWER(b.title), '\\d+'))[1]::int, 0), b.title ASC".to_string()
|
||||
};
|
||||
|
||||
// DATA: mêmes params filtre, puis $N+1=limit $N+2=offset
|
||||
@@ -400,6 +400,7 @@ pub async fn list_series(
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY COALESCE(NULLIF(series, ''), 'unclassified')
|
||||
ORDER BY
|
||||
volume NULLS LAST,
|
||||
REGEXP_REPLACE(LOWER(title), '[0-9]+', '', 'g'),
|
||||
COALESCE((REGEXP_MATCH(LOWER(title), '\d+'))[1]::int, 0),
|
||||
title ASC
|
||||
@@ -586,6 +587,7 @@ pub async fn list_all_series(
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY COALESCE(NULLIF(series, ''), 'unclassified')
|
||||
ORDER BY
|
||||
volume NULLS LAST,
|
||||
REGEXP_REPLACE(LOWER(title), '[0-9]+', '', 'g'),
|
||||
COALESCE((REGEXP_MATCH(LOWER(title), '\d+'))[1]::int, 0),
|
||||
title ASC
|
||||
@@ -714,6 +716,7 @@ pub async fn ongoing_series(
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY COALESCE(NULLIF(series, ''), 'unclassified')
|
||||
ORDER BY
|
||||
volume NULLS LAST,
|
||||
REGEXP_REPLACE(LOWER(title), '[0-9]+', '', 'g'),
|
||||
COALESCE((REGEXP_MATCH(LOWER(title), '\d+'))[1]::int, 0),
|
||||
title ASC
|
||||
|
||||
@@ -30,7 +30,6 @@ use std::num::NonZeroUsize;
|
||||
use stripstream_core::config::ApiConfig;
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
use tokio::sync::{Mutex, RwLock, Semaphore};
|
||||
use std::sync::Mutex as StdMutex;
|
||||
use tracing::info;
|
||||
|
||||
use crate::state::{load_concurrent_renders, load_dynamic_settings, AppState, Metrics, ReadRateLimit};
|
||||
@@ -78,7 +77,6 @@ async fn main() -> anyhow::Result<()> {
|
||||
requests_in_window: 0,
|
||||
})),
|
||||
settings: Arc::new(RwLock::new(dynamic_settings)),
|
||||
archive_index_cache: Arc::new(StdMutex::new(LruCache::new(NonZeroUsize::new(256).expect("non-zero")))),
|
||||
};
|
||||
|
||||
let admin_routes = Router::new()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::sync::{
|
||||
atomic::AtomicU64,
|
||||
Arc,
|
||||
Mutex as StdMutex,
|
||||
};
|
||||
use std::time::Instant;
|
||||
|
||||
@@ -20,9 +19,6 @@ pub struct AppState {
|
||||
pub metrics: Arc<Metrics>,
|
||||
pub read_rate_limit: Arc<Mutex<ReadRateLimit>>,
|
||||
pub settings: Arc<RwLock<DynamicSettings>>,
|
||||
/// Sorted image name list per archive path — avoids re-enumerating entries on every cold render.
|
||||
/// Uses StdMutex (not tokio) so it's accessible from spawn_blocking.
|
||||
pub archive_index_cache: Arc<StdMutex<LruCache<String, Arc<Vec<String>>>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
||||
Reference in New Issue
Block a user