diff --git a/.github/workflows/push-continous-delivery.yml b/.github/workflows/push-continous-delivery.yml
index 99e20c9d0..71c9ef214 100644
--- a/.github/workflows/push-continous-delivery.yml
+++ b/.github/workflows/push-continous-delivery.yml
@@ -99,7 +99,9 @@ jobs:
buildNightlyWSL:
name: Build Nightly Windows Packages
- needs: exportDockerRootFS
+ needs:
+ - exportDockerRootFS
+ - exportDockerRootFSLegacy
runs-on: windows-latest
steps:
- uses: actions/checkout@master
diff --git a/README.md b/README.md
index 90caf8842..c24a4594f 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,6 @@
[
](https://github.com/Difegue/LANraragi/releases)
[
](https://github.com/Difegue/LANraragi/releases/latest)
[
](https://formulae.brew.sh/formula/lanraragi)
-
[
](https://lrr.tvc-16.science/)
[
](https://github.com/Difegue/LANraragi/actions)
[
](https://discord.gg/aRQxtbg)
diff --git a/lib/LANraragi.pm b/lib/LANraragi.pm
index 0dc1847b7..90e74e148 100644
--- a/lib/LANraragi.pm
+++ b/lib/LANraragi.pm
@@ -53,9 +53,6 @@ sub startup {
$self->secrets( [ $secret . hostname() ] );
$self->plugin('RenderFile');
- # Load i18n
- LANraragi::Utils::I18NInitializer::initialize($self);
-
# Set Template::Toolkit as default renderer so we can use the LRR templates
$self->plugin('TemplateToolkit');
$self->renderer->default_handler('tt2');
@@ -91,6 +88,9 @@ sub startup {
die;
}
+ # Load i18n
+ LANraragi::Utils::I18NInitializer::initialize($self);
+
# Catch Redis errors on our first connection. This is useful in case of temporary LOADING errors,
# Where Redis lets us send commands but doesn't necessarily reply to them properly.
# (https://github.com/redis/redis/issues/4624)
@@ -194,18 +194,19 @@ sub startup {
my $prefix = $self->LRR_BASEURL;
if ($prefix) {
- if (!$prefix =~ m|^/[^"]*[^/"]$|) {
+ if ( !$prefix =~ m|^/[^"]*[^/"]$| ) {
say "Warning: configured URL prefix '$prefix' invalid, ignoring";
+
# if prefix is invalid, then set it to empty for the cookie
$prefix = "";
- }
- else {
+ } else {
$c->req->url->base->path($prefix);
}
}
+
# SameSite=Lax is the default behavior here; I set it
# explicitly to get rid of a warning in the browser
- $c->cookie("lrr_baseurl" => $prefix, { samesite => "lax" });
+ $c->cookie( "lrr_baseurl" => $prefix, { samesite => "lax" } );
}
);
diff --git a/lib/LANraragi/Controller/Api/Archive.pm b/lib/LANraragi/Controller/Api/Archive.pm
index f0a5f1d5d..dccdd5fe7 100644
--- a/lib/LANraragi/Controller/Api/Archive.pm
+++ b/lib/LANraragi/Controller/Api/Archive.pm
@@ -12,7 +12,6 @@ use File::Temp qw(tempdir);
use File::Basename;
use File::Find;
-use LANraragi::Utils::Archive qw(extract_thumbnail);
use LANraragi::Utils::Generic qw(render_api_response is_archive get_bytelength);
use LANraragi::Utils::Database qw(get_archive_json set_isnew);
use LANraragi::Utils::Logging qw(get_logger);
diff --git a/lib/LANraragi/Controller/Api/Search.pm b/lib/LANraragi/Controller/Api/Search.pm
index 16c8dbc73..8861d4f58 100644
--- a/lib/LANraragi/Controller/Api/Search.pm
+++ b/lib/LANraragi/Controller/Api/Search.pm
@@ -34,16 +34,19 @@ sub handle_datatables {
# Collection (tags column)
if ( $req->param("columns[$i][name]") eq "tags" ) {
$categoryfilter = $req->param("columns[$i][search][value]");
- }
- # New filter (isnew column)
- if ( $req->param("columns[$i][name]") eq "isnew" ) {
- $newfilter = $req->param("columns[$i][search][value]") eq "true";
- }
+ # Specific hacks for the buily-in newonly/untagged selectors
+ # Those have hardcoded 'category' IDs
+ if ( $categoryfilter eq "NEW_ONLY" ) {
+ $newfilter = 1;
+ $categoryfilter = "";
+ }
+
+ if ( $categoryfilter eq "UNTAGGED_ONLY" ) {
+ $untaggedfilter = 1;
+ $categoryfilter = "";
+ }
- # Untagged filter (untagged column)
- if ( $req->param("columns[$i][name]") eq "untagged" ) {
- $untaggedfilter = $req->param("columns[$i][search][value]") eq "true";
}
$i++;
}
diff --git a/lib/LANraragi/Model/Archive.pm b/lib/LANraragi/Model/Archive.pm
index 0377d0f8d..603a1c193 100644
--- a/lib/LANraragi/Model/Archive.pm
+++ b/lib/LANraragi/Model/Archive.pm
@@ -1,12 +1,12 @@
package LANraragi::Model::Archive;
+use v5.36;
+use experimental 'try';
+
use strict;
use warnings;
use utf8;
-use feature qw(signatures);
-no warnings 'experimental::signatures';
-
use Cwd 'abs_path';
use Redis;
use Time::HiRes qw(usleep);
@@ -70,17 +70,17 @@ sub update_thumbnail {
my $newthumb = "";
# Get the required thumbnail we want to make the main one
- eval { $newthumb = extract_thumbnail( $thumbdir, $id, $page, 1 ) };
+ no warnings 'experimental::try';
+ try {
+ $newthumb = extract_thumbnail( $thumbdir, $id, $page, 1, 1 )
+ } catch ($e) {
+ render_api_response( $self, "update_thumbnail", $e );
+ return;
+ }
- if ( $@ || !$newthumb ) {
- render_api_response( $self, "update_thumbnail", $@ );
+ if ( !$newthumb ) {
+ render_api_response( $self, "update_thumbnail", "Thumbnail not generated." );
} else {
- if ( $newthumb ne $thumbname && $newthumb ne "" ) {
-
- # Copy the thumbnail to the main thumbnail location
- cp( $newthumb, $thumbname );
- }
-
$self->render(
json => {
operation => "update_thumbnail",
@@ -114,8 +114,8 @@ sub generate_page_thumbnails {
my $should_queue_job = 0;
- for ( my $page = 2; $page <= $pages; $page++ ) {
- my $thumbname = ( $page - 1 > 0 ) ? "$thumbdir/$subfolder/$id/$page.$format" : "$thumbdir/$subfolder/$id.$format";
+ for ( my $page = 1; $page <= $pages; $page++ ) {
+ my $thumbname = "$thumbdir/$subfolder/$id/$page.$format";
unless ( $force == 0 && -e $thumbname ) {
$logger->debug("Thumbnail for page $page doesn't exist (path: $thumbname or force=$force), queueing job.");
diff --git a/lib/LANraragi/Model/Plugins.pm b/lib/LANraragi/Model/Plugins.pm
index 4e0acee6f..775df4ccd 100644
--- a/lib/LANraragi/Model/Plugins.pm
+++ b/lib/LANraragi/Model/Plugins.pm
@@ -22,9 +22,8 @@ use LANraragi::Utils::Tags qw(rewrite_tags split_tags_to_array);
use LANraragi::Utils::Plugins qw(get_plugin_parameters get_plugin);
# Sub used by Auto-Plugin.
-sub exec_enabled_plugins_on_file {
+sub exec_enabled_plugins_on_file ($id) {
- my $id = shift;
my $logger = get_logger( "Auto-Plugin", "lanraragi" );
$logger->info("Executing enabled metadata plugins on archive with id $id.");
@@ -95,10 +94,10 @@ sub exec_enabled_plugins_on_file {
# Unlike the two other methods, exec_login_plugin takes a plugin name and does the Redis lookup itself.
# Might be worth consolidating this later.
-sub exec_login_plugin {
- my $plugname = shift;
- my $ua = Mojo::UserAgent->new;
- my $logger = get_logger( "Plugin System", "lanraragi" );
+sub exec_login_plugin ($plugname) {
+
+ my $ua = Mojo::UserAgent->new;
+ my $logger = get_logger( "Plugin System", "lanraragi" );
if ($plugname) {
$logger->debug("Calling matching login plugin $plugname.");
@@ -125,9 +124,7 @@ sub exec_login_plugin {
return $ua;
}
-sub exec_script_plugin {
-
- my ( $plugin, %settings ) = @_;
+sub exec_script_plugin ( $plugin, %settings ) {
no warnings 'experimental::try';
@@ -153,9 +150,8 @@ sub exec_script_plugin {
}
}
-sub exec_download_plugin {
+sub exec_download_plugin ( $plugin, $input, @settings ) {
- my ( $plugin, $input, @settings ) = @_;
my $logger = get_logger( "Plugin System", "lanraragi" );
my %pluginfo = $plugin->plugin_info();
@@ -186,9 +182,7 @@ sub exec_download_plugin {
}
# Execute a specified plugin on a file, described through its Redis ID.
-sub exec_metadata_plugin {
-
- my ( $plugin, $id, %args ) = @_;
+sub exec_metadata_plugin ( $plugin, $id, %args ) {
no warnings 'experimental::try';
@@ -212,7 +206,7 @@ sub exec_metadata_plugin {
$thumbhash = "";
try {
- extract_thumbnail( $thumbdir, $id, 0, 1 );
+ extract_thumbnail( $thumbdir, $id, 1, 1, 1 );
$thumbhash = $redis->hget( $id, "thumbhash" );
$thumbhash = LANraragi::Utils::Database::redis_decode($thumbhash);
} catch ($e) {
@@ -299,8 +293,7 @@ sub exec_metadata_plugin {
}
# TODO: remove after the deprecation period
-sub has_old_style_params {
- my (%params) = @_;
+sub has_old_style_params (%params) {
return ( exists $params{'customargs'} );
}
diff --git a/lib/LANraragi/Model/Reader.pm b/lib/LANraragi/Model/Reader.pm
index 5201675b3..19f1908c9 100644
--- a/lib/LANraragi/Model/Reader.pm
+++ b/lib/LANraragi/Model/Reader.pm
@@ -1,5 +1,8 @@
package LANraragi::Model::Reader;
+use v5.36;
+use experimental 'try';
+
use strict;
use warnings;
use utf8;
@@ -12,46 +15,54 @@ use File::Copy qw(move);
use Mojo::JSON qw(encode_json);
use Data::Dumper;
use URI::Escape;
-use Image::Magick;
-use LANraragi::Utils::Generic qw(is_image shasum);
-use LANraragi::Utils::Archive qw(extract_archive get_filelist);
+use LANraragi::Utils::Generic qw(is_image shasum);
+use LANraragi::Utils::Logging qw(get_logger);
+use LANraragi::Utils::Archive qw(extract_archive get_filelist);
use LANraragi::Utils::Database qw(redis_decode);
# resize_image(image,quality, size_threshold)
# Convert an image to a cheaper on bandwidth format through ImageMagick.
-sub resize_image {
+# This will no-op if the ImageMagick bindings are unavailable.
+sub resize_image ( $imgpath, $quality, $threshold ) {
+
+ no warnings 'experimental::try';
+ try {
+ require Image::Magick;
+ my $img = Image::Magick->new;
- my ( $imgpath, $quality, $threshold ) = @_;
- my $img = Image::Magick->new;
+ #Is the file size higher than the threshold?
+ if ( ( int( ( -s $imgpath ) / 1024 * 10 ) / 10 ) > $threshold ) {
- #Is the file size higher than the threshold?
- if ( ( int( ( -s $imgpath ) / 1024 * 10 ) / 10 ) > $threshold ) {
+ # For JPEG, the size option (or jpeg:size option) provides a hint to the JPEG decoder
+ # that it can reduce the size on-the-fly during decoding. This saves memory because
+ # it never has to allocate memory for the full-sized image
+ $img->Set( option => 'jpeg:size=1064x' );
- # For JPEG, the size option (or jpeg:size option) provides a hint to the JPEG decoder
- # that it can reduce the size on-the-fly during decoding. This saves memory because
- # it never has to allocate memory for the full-sized image
- $img->Set( option => 'jpeg:size=1064x' );
+ $img->Read($imgpath);
- $img->Read($imgpath);
+ my ( $origw, $origh ) = $img->Get( 'width', 'height' );
+ if ( $origw > 1064 ) {
+ $img->Resize( geometry => '1064x' );
+ }
- my ( $origw, $origh ) = $img->Get( 'width', 'height' );
- if ( $origw > 1064 ) {
- $img->Resize( geometry => '1064x' );
+ # Set format to jpeg and quality
+ $img->Set( quality => $quality, magick => "jpg" );
+ $img->Write($imgpath);
}
+ undef $img;
+ } catch ($e) {
- # Set format to jpeg and quality
- $img->Set( quality => $quality, magick => "jpg" );
- $img->Write($imgpath);
+ # Magick is unavailable, do nothing
+ my $logger = get_logger( "Reader", "lanraragi" );
+ $logger->debug("ImageMagick is not available , skipping image resizing: $e");
}
- undef $img;
+
}
# build_reader_JSON(mojo, id, forceReload)
# Opens the archive specified by its ID, and returns a json containing the page names.
-sub build_reader_JSON {
-
- my ( $self, $id, $force ) = @_;
+sub build_reader_JSON ( $self, $id, $force ) {
# Queue a full extract job into Minion.
# This'll fill in the missing pages (or regen everything if force = 1)
@@ -62,7 +73,7 @@ sub build_reader_JSON {
# Get the path from Redis.
# Filenames are stored as they are on the OS, so no decoding!
- my $redis = LANraragi::Model::Config->get_redis;
+ my $redis = LANraragi::Model::Config->get_redis;
my $archive = $redis->hget( $id, "file" );
# Parse archive to get its list of images
diff --git a/lib/LANraragi/Model/Tankoubon.pm b/lib/LANraragi/Model/Tankoubon.pm
index 0a0f23e87..91e1ff9b5 100644
--- a/lib/LANraragi/Model/Tankoubon.pm
+++ b/lib/LANraragi/Model/Tankoubon.pm
@@ -166,7 +166,7 @@ sub get_tankoubon ( $tank_id, $fulldata = 0, $page = 0 ) {
%tank = filter_hash_by_keys( \@allowed_keys, %tank );
- my $total = $redis->zcard($tank_id) - 1;
+ my $total = $redis->zcount($tank_id, 1, "+inf");
return ( $total, $#archives + 1, %tank );
}
@@ -232,8 +232,8 @@ sub update_metadata ( $tank_id, $data ) {
my $redis = LANraragi::Model::Config->get_redis;
my $err = "";
my $name = $data->{"metadata"}->{"name"} || undef;
- my $summary = $data->{"metadata"}->{"summary"} || undef;
- my $tags = $data->{"metadata"}->{"tags"} || undef;
+ my $summary = exists $data->{"metadata"}->{"summary"} ? $data->{"metadata"}->{"summary"} : undef;
+ my $tags = exists $data->{"metadata"}->{"tags"} ? $data->{"metadata"}->{"tags"} : undef;
if ( $redis->exists($tank_id) ) {
if ( defined $name ) {
diff --git a/lib/LANraragi/Model/Upload.pm b/lib/LANraragi/Model/Upload.pm
index ccc1cbefa..8244be93c 100644
--- a/lib/LANraragi/Model/Upload.pm
+++ b/lib/LANraragi/Model/Upload.pm
@@ -1,5 +1,7 @@
package LANraragi::Model::Upload;
+use v5.36;
+
use strict;
use warnings;
@@ -31,9 +33,8 @@ use LANraragi::Model::Category;
# You can also specify tags to add to the metadata for the processed file before autoplugin is ran. (if it's enabled)
#
# Returns an HTTP status code, the ID and title of the file, and a status message.
-sub handle_incoming_file {
+sub handle_incoming_file ( $tempfile, $catid, $tags, $title, $summary ) {
- my ( $tempfile, $catid, $tags, $title, $summary ) = @_;
my ( $filename, $dirs, $suffix ) = fileparse( $tempfile, qr/\.[^.]*/ );
$filename = $filename . $suffix;
my $logger = get_logger( "File Upload/Download", "lanraragi" );
@@ -145,7 +146,7 @@ sub handle_incoming_file {
# Generate thumbnail
my $thumbdir = LANraragi::Model::Config->get_thumbdir;
- extract_thumbnail( $thumbdir, $id, 0, 1 );
+ extract_thumbnail( $thumbdir, $id, 1, 1, 1 );
$logger->debug("Running autoplugin on newly uploaded file $id...");
@@ -177,9 +178,7 @@ sub handle_incoming_file {
# Download the given URL, using the given Mojo::UserAgent object.
# This downloads the URL to a temporaryfolder and returns the full path to the downloaded file.
-sub download_url {
-
- my ( $url, $ua ) = @_;
+sub download_url ( $url, $ua ) {
my $logger = get_logger( "File Upload/Download", "lanraragi" );
@@ -195,16 +194,20 @@ sub download_url {
my $attempts = 0;
- while ( !$content_disp || $attempts < 5 ) {
+ while ( !$content_disp && $attempts < 5 ) {
$tx = $ua->max_response_size(0)->max_redirects(5)->get($url);
$content_disp = $tx->result->headers->content_disposition;
unless ($content_disp) {
- $logger->warn("No valid Content-Disposition header received, waiting and retrying...");
+ $logger->warn("No valid Content-Disposition header received, waiting and retrying... (attempt $attempts / 5)");
+ $logger->debug( "Result of this attempt: " . $tx->result->body );
sleep 1;
$attempts++;
}
+ }
+ if ( !$content_disp ) {
+ die( "No valid Content-Disposition header received after 5 attempts, aborting. (Last result: " . $tx->result->body . ")" );
}
$logger->debug("Content-Disposition Header: $content_disp");
diff --git a/lib/LANraragi/Plugin/Metadata/EHentai.pm b/lib/LANraragi/Plugin/Metadata/EHentai.pm
index 56587785e..12813d33e 100644
--- a/lib/LANraragi/Plugin/Metadata/EHentai.pm
+++ b/lib/LANraragi/Plugin/Metadata/EHentai.pm
@@ -122,9 +122,8 @@ sub get_tags {
## EH Specific Methods
######
-sub lookup_gallery {
+sub lookup_gallery ( $title, $tags, $thumbhash, $ua, $domain, $defaultlanguage, $usethumbs, $search_gid, $expunged ) {
- my ( $title, $tags, $thumbhash, $ua, $domain, $defaultlanguage, $usethumbs, $search_gid, $expunged ) = @_;
my $logger = get_plugin_logger();
my $URL = "";
@@ -187,15 +186,11 @@ sub lookup_gallery {
return &ehentai_parse( $URL, $ua );
}
-# ehentai_parse(URL, UA)
# Performs a remote search on e- or exhentai, and returns the ID/token matching the found gallery.
-sub ehentai_parse {
-
- my ( $url, $ua ) = @_;
+sub ehentai_parse ( $url, $ua ) {
my $logger = get_plugin_logger();
-
- my $dom = search_gallery( $url, $ua );
+ my $dom = search_gallery( $url, $ua );
# Get the first row of the search results
# The "glink" class is parented by a tag containing the gallery link in href.
@@ -216,9 +211,8 @@ sub ehentai_parse {
return ( $gID, $gToken );
}
-sub search_gallery {
+sub search_gallery ( $url, $ua ) {
- my ( $url, $ua ) = @_;
my $logger = get_plugin_logger();
my $res = $ua->max_redirects(5)->get($url)->result;
@@ -232,9 +226,8 @@ sub search_gallery {
# get_tags_from_EH(userAgent, gID, gToken, jpntitle, additionaltags)
# Executes an e-hentai API request with the given JSON and returns tags and title.
-sub get_tags_from_EH {
+sub get_tags_from_EH ( $ua, $gID, $gToken, $jpntitle, $additionaltags ) {
- my ( $ua, $gID, $gToken, $jpntitle, $additionaltags ) = @_;
my $uri = 'https://api.e-hentai.org/api.php';
my $logger = get_plugin_logger();
@@ -266,9 +259,8 @@ sub get_tags_from_EH {
return ( $ehtags, $ehtitle );
}
-sub get_json_from_EH {
+sub get_json_from_EH ( $ua, $gID, $gToken ) {
- my ( $ua, $gID, $gToken ) = @_;
my $uri = 'https://api.e-hentai.org/api.php';
my $logger = get_plugin_logger();
diff --git a/lib/LANraragi/Utils/Archive.pm b/lib/LANraragi/Utils/Archive.pm
index 892bbf5f6..7bb543e8d 100644
--- a/lib/LANraragi/Utils/Archive.pm
+++ b/lib/LANraragi/Utils/Archive.pm
@@ -1,13 +1,12 @@
package LANraragi::Utils::Archive;
+use v5.36;
+use experimental 'try';
+
use strict;
use warnings;
use utf8;
-use feature qw(say);
-use feature qw(signatures);
-no warnings 'experimental::signatures';
-
use Time::HiRes qw(gettimeofday);
use File::Basename;
use File::Path qw(remove_tree make_path);
@@ -18,7 +17,6 @@ use Encode::Guess qw/euc-jp shiftjis 7bit-jis/;
use Redis;
use Cwd;
use Data::Dumper;
-use Image::Magick;
use Archive::Libarchive qw( ARCHIVE_OK );
use Archive::Libarchive::Extract;
use Archive::Libarchive::Peek;
@@ -44,34 +42,43 @@ sub is_pdf {
# If use_jxl is true, JPEG XL will be used instead of JPEG.
sub generate_thumbnail ( $orig_path, $thumb_path, $use_hq, $use_jxl ) {
- my $img = Image::Magick->new;
+ no warnings 'experimental::try';
+ try {
+ require Image::Magick;
+ my $img = Image::Magick->new;
- my $format = $use_jxl ? 'jxl' : 'jpg';
+ my $format = $use_jxl ? 'jxl' : 'jpg';
- # For JPEG, the size option (or jpeg:size option) provides a hint to the JPEG decoder
- # that it can reduce the size on-the-fly during decoding. This saves memory because
- # it never has to allocate memory for the full-sized image
- if ( $format eq 'jpg' ) {
- $img->Set( option => 'jpeg:size=500x' );
- }
+ # For JPEG, the size option (or jpeg:size option) provides a hint to the JPEG decoder
+ # that it can reduce the size on-the-fly during decoding. This saves memory because
+ # it never has to allocate memory for the full-sized image
+ if ( $format eq 'jpg' ) {
+ $img->Set( option => 'jpeg:size=500x' );
+ }
- # If the image is a gif, only take the first frame
- if ( $orig_path =~ /\.gif$/ ) {
- $img->Read( $orig_path . "[0]" );
- } else {
- $img->Read($orig_path);
- }
+ # If the image is a gif, only take the first frame
+ if ( $orig_path =~ /\.gif$/ ) {
+ $img->Read( $orig_path . "[0]" );
+ } else {
+ $img->Read($orig_path);
+ }
- # The "-scale" resize operator is a simplified, faster form of the resize command.
- if ($use_hq) {
- $img->Scale( geometry => '500x1000' );
- } else { # Sample is very fast due to not applying filters.
- $img->Sample( geometry => '500x1000' );
- }
+ # The "-scale" resize operator is a simplified, faster form of the resize command.
+ if ($use_hq) {
+ $img->Scale( geometry => '500x1000' );
+ } else { # Sample is very fast due to not applying filters.
+ $img->Sample( geometry => '500x1000' );
+ }
- $img->Set( quality => "50", magick => $format );
- $img->Write($thumb_path);
- undef $img;
+ $img->Set( quality => "50", magick => $format );
+ $img->Write($thumb_path);
+ undef $img;
+ } catch ($e) {
+
+ # Magick is unavailable, do nothing
+ my $logger = get_logger( "Archive", "lanraragi" );
+ $logger->debug("ImageMagick is not available , skipping thumbnail generation: $e");
+ }
}
# Extract the given archive to the given path.
@@ -150,9 +157,10 @@ sub extract_pdf ( $destination, $to_extract ) {
}
# Extracts a thumbnail from the specified archive ID and page. Returns the path to the thumbnail.
-# Non-cover thumbnails land in a folder named after the ID. Specify page=0 if you want the cover.
+# Non-cover thumbnails land in a folder named after the ID.
+# Specify $set_cover if you want the given to page to be placed as the cover thumbnail instead.
# Thumbnails will be generated at low quality by default unless you specify use_hq=1.
-sub extract_thumbnail ( $thumbdir, $id, $page, $use_hq ) {
+sub extract_thumbnail ( $thumbdir, $id, $page, $set_cover, $use_hq ) {
my $logger = get_logger( "Archive", "lanraragi" );
@@ -187,7 +195,7 @@ sub extract_thumbnail ( $thumbdir, $id, $page, $use_hq ) {
}
my $thumbname;
- if ( $page - 1 > 0 ) {
+ unless ($set_cover) {
# Non-cover thumbnails land in a dedicated folder.
$thumbname = "$thumbdir/$subfolder/$id/$page.$format";
diff --git a/lib/LANraragi/Utils/I18NInitializer.pm b/lib/LANraragi/Utils/I18NInitializer.pm
index ce011db02..0f1b0f874 100644
--- a/lib/LANraragi/Utils/I18NInitializer.pm
+++ b/lib/LANraragi/Utils/I18NInitializer.pm
@@ -20,17 +20,17 @@ sub initialize {
if ($accept_language) {
($lang) = split /,/, $accept_language;
$lang =~ s/-.*//;
- $c->log->trace("Detected language: $lang");
+ $c->LRR_LOGGER->trace("Detected language: $lang");
}
my $handle = LANraragi::Utils::I18N->get_handle($lang);
unless ($handle) {
- $c->log->trace("No handle for language: $lang, fallback to en");
+ $c->LRR_LOGGER->trace("No handle for language: $lang, fallback to en");
$handle = LANraragi::Utils::I18N->get_handle('en');
return $key unless $handle;
}
- $c->log->trace( "Key: $key, Args: " . join( ", ", map { defined($_) ? $_ : 'undef' } @args ) );
+ $c->LRR_LOGGER->trace( "Key: $key, Args: " . join( ", ", map { defined($_) ? $_ : 'undef' } @args ) );
# make sure all args are encoded in UTF-8
my @encoded_args = map { Encode::encode( 'UTF-8', $_ ) } @args;
@@ -38,7 +38,7 @@ sub initialize {
my $translated;
eval { $translated = $handle->maketext( $key, @encoded_args ); };
if ($@) {
- $c->log->error("Maketext error: $@");
+ $c->LRR_LOGGER->error("Maketext error: $@");
return $key;
}
@@ -47,12 +47,12 @@ sub initialize {
$translated = Encode::decode_utf8($translated);
}
- $c->log->trace("Translated result: $translated");
+ $c->LRR_LOGGER->trace("Translated result: $translated");
return $translated;
}
);
- $app->log->debug("I18N system initialized.");
+ $app->LRR_LOGGER->debug("I18N system initialized.");
}
1;
diff --git a/lib/LANraragi/Utils/Minion.pm b/lib/LANraragi/Utils/Minion.pm
index e333f2c7a..54c4ba580 100644
--- a/lib/LANraragi/Utils/Minion.pm
+++ b/lib/LANraragi/Utils/Minion.pm
@@ -34,7 +34,8 @@ sub add_tasks {
my $use_hq = $page eq 0 || LANraragi::Model::Config->get_hqthumbpages;
my $thumbname = "";
- eval { $thumbname = extract_thumbnail( $thumbdir, $id, $page, $use_hq ); };
+ # Take a shortcut here - Minion jobs can keep the old basic behavior of page 0 = cover.
+ eval { $thumbname = extract_thumbnail( $thumbdir, $id, $page, $page eq 0, $use_hq ); };
if ($@) {
my $msg = "Error building thumbnail: $@";
$logger->error($msg);
@@ -76,7 +77,7 @@ sub add_tasks {
# Generate thumbnails for all pages -- Cover should already be handled in higher resolution
my @keys = ();
- for ( my $i = 2; $i <= $pages; $i++ ) {
+ for ( my $i = 1; $i <= $pages; $i++ ) {
push @keys, $i;
}
my @sections = split_workload_by_cpu( $numCpus, @keys );
@@ -91,7 +92,7 @@ sub add_tasks {
my $thumbname = "$thumbdir/$subfolder/$id/$i.$format";
unless ( $force == 0 && -e $thumbname ) {
$logger->debug("Generating thumbnail for page $i... ($thumbname)");
- eval { $thumbname = extract_thumbnail( $thumbdir, $id, $i, $use_hq ); };
+ eval { $thumbname = extract_thumbnail( $thumbdir, $id, $i, 0, $use_hq ); };
if ($@) {
$logger->warn("Error while generating thumbnail: $@");
push @errors, $@;
@@ -147,7 +148,7 @@ sub add_tasks {
unless ( $force == 0 && -e $thumbname ) {
eval {
$logger->debug("Regenerating for $id...");
- extract_thumbnail( $thumbdir, $id, 0, 1 );
+ extract_thumbnail( $thumbdir, $id, 0, 1, 1 );
};
if ($@) {
diff --git a/lib/Shinobu.pm b/lib/Shinobu.pm
index df4ed3f1f..8f6db506c 100644
--- a/lib/Shinobu.pm
+++ b/lib/Shinobu.pm
@@ -332,7 +332,7 @@ sub add_new_file ( $id, $file ) {
# Generate thumbnail
my $thumbdir = LANraragi::Model::Config->get_thumbdir;
- extract_thumbnail( $thumbdir, $id, 0, 1 );
+ extract_thumbnail( $thumbdir, $id, 1, 1, 1 );
# AutoTagging using enabled plugins goes here!
LANraragi::Model::Plugins::exec_enabled_plugins_on_file($id);
diff --git a/locales/template/en.po b/locales/template/en.po
index 8e3b72362..f0c473c88 100644
--- a/locales/template/en.po
+++ b/locales/template/en.po
@@ -1010,18 +1010,6 @@ msgstr "Infinite Scrolling"
msgid "Display all images in a vertical view in the same page."
msgstr "Display all images in a vertical view in the same page."
-msgid "outer-left"
-msgstr "outer-left"
-
-msgid "left"
-msgstr "left"
-
-msgid "right"
-msgstr "right"
-
-msgid "outer-right"
-msgstr "outer-right"
-
msgid "Reader Settings"
msgstr "Reader Settings"
@@ -1115,7 +1103,7 @@ msgid "URL(s) to download:"
msgstr "URL(s) to download:"
msgid "Add from URL(s)"
-msgstr "Add from URL(s)""
+msgstr "Add from URL(s)"
msgid "Return to Library"
msgstr "Return to Library"
diff --git a/locales/template/zh.po b/locales/template/zh.po
index 7b56c7f5d..9dafdb94e 100644
--- a/locales/template/zh.po
+++ b/locales/template/zh.po
@@ -1010,18 +1010,6 @@ msgstr "无极滚动"
msgid "Display all images in a vertical view in the same page."
msgstr "在同一页面中以垂直视图显示所有图片。"
-msgid "outer-left"
-msgstr "最左"
-
-msgid "left"
-msgstr "左"
-
-msgid "right"
-msgstr "右"
-
-msgid "outer-right"
-msgstr "最右"
-
msgid "Reader Settings"
msgstr "阅读器设置"
diff --git a/package.json b/package.json
index 7e6c8f042..de3c3223e 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "lanraragi",
- "version": "0.9.30",
- "version_name": "Law (Earthlings On Fire)",
+ "version": "0.9.31",
+ "version_name": "Earthlings On Fire (January 1997 pre-show)",
"description": "I'm under Japanese influence and my honor's at stake!",
"scripts": {
"test": "prove -r -l -v tests/",
diff --git a/public/js/common.js b/public/js/common.js
index abebda1f4..dfdc94681 100644
--- a/public/js/common.js
+++ b/public/js/common.js
@@ -369,6 +369,15 @@ LRR.getImgSizeAsync = function (target) {
});
};
+LRR.getDocHeight = function() {
+ var D = document;
+ return Math.max(
+ D.body.scrollHeight, D.documentElement.scrollHeight,
+ D.body.offsetHeight, D.documentElement.offsetHeight,
+ D.body.clientHeight, D.documentElement.clientHeight
+ );
+}
+
/**
* Show a generic toast with a given header and message.
* This is a compatibility layer to migrate jquery-toast-plugin to react-toastify.
diff --git a/public/js/index.js b/public/js/index.js
index 9b2a4ab5b..56f1ff9eb 100644
--- a/public/js/index.js
+++ b/public/js/index.js
@@ -664,7 +664,16 @@ Index.loadCategories = function () {
// Pinned categories are shown at the beginning
data.sort((b, a) => b.name.localeCompare(a.name));
data.sort((a, b) => b.pinned - a.pinned);
- let html = "";
+ // Queue some hardcoded categories at the beginning - those are special-cased in the DataTables variant of the search endpoint.
+ let html = `
+
+
+
+
`;
const iteration = (data.length > 10 ? 10 : data.length);
diff --git a/public/js/reader.js b/public/js/reader.js
index 42423d7a1..f25ca4fa5 100644
--- a/public/js/reader.js
+++ b/public/js/reader.js
@@ -319,7 +319,7 @@ Reader.handleShortcuts = function (e) {
if ($(".page-overlay").is(":visible")) { break; }
if (e.originalEvent.getModifierState("Shift") && (window.scrollY) === 0) {
(Reader.mangaMode) ? Reader.changePage(1) : Reader.changePage(-1);
- } else if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
+ } else if (($(window).height() + $(window).scrollTop()) >= LRR.getDocHeight()) {
(Reader.mangaMode) ? Reader.changePage(-1) : Reader.changePage(1);
}
// spacebar is always forward regardless of reading direction, so it needs to be flipped
diff --git a/templates/reader.html.tt2 b/templates/reader.html.tt2
index 58b059125..f62c7533e 100644
--- a/templates/reader.html.tt2
+++ b/templates/reader.html.tt2
@@ -267,16 +267,16 @@
[% BLOCK arrows %]
-
-
+
+
... /
...
-
-
+
+
[% END %]
diff --git a/tests/mocks.pl b/tests/mocks.pl
index c7a8c3011..c80809824 100644
--- a/tests/mocks.pl
+++ b/tests/mocks.pl
@@ -148,6 +148,7 @@ sub setup_redis_mock {
$redis->mock( 'zincrby', sub { 1 } );
$redis->mock( 'zrem', sub { 1 } );
$redis->mock( 'watch', sub { 1 } );
+ $redis->mock( 'set', sub { 1 } );
$redis->mock( 'hlen', sub { 1337 } );
$redis->mock( 'dbsize', sub { 1337 } );
@@ -217,6 +218,20 @@ sub setup_redis_mock {
}
);
+ $redis->mock(
+ 'zcount', # $redis->zcard => number of values in list named by key in datamodel
+ sub {
+ my $self = shift;
+ my ( $key, $weight, $value ) = @_;
+
+ if ( !exists $datamodel{$key} ) {
+ $datamodel{$key} = [];
+ }
+
+ return scalar @{ $datamodel{$key} } - 1;
+ }
+ );
+
$redis->mock(
'zcard', # $redis->zcard => number of values in list named by key in datamodel
sub {
@@ -300,6 +315,7 @@ sub setup_redis_mock {
my @indexed_res;
if ( $start < 0 ) {
+
# From -start to 0 or -end insert ""
push @indexed_res, "tags_test";
push @indexed_res, -2;
@@ -318,7 +334,7 @@ sub setup_redis_mock {
# Add index after each element to simulate zrangebyscore behavior
my $ind = $start;
-
+
foreach my $val (@res) {
unless ($val) {
next;
diff --git a/tests/plugins.t b/tests/plugins.t
index c43af527a..ff560fd3b 100644
--- a/tests/plugins.t
+++ b/tests/plugins.t
@@ -28,13 +28,14 @@ setup_redis_mock();
note("E-Hentai Tests");
{
+
my $ua = trap { LANraragi::Plugin::Login::EHentai::do_login( "", "", "" ); };
my $domain = "e-hentai.org";
my $eH_gID = "618395";
my $eH_gToken = "0439fa3666";
my ( $test_eH_gID, $test_eH_gToken ) =
- trap { LANraragi::Plugin::Metadata::EHentai::lookup_gallery( "TOUHOU GUNMANIA", "", "", $ua, $domain, "", 0, 0 ); };
+ trap { LANraragi::Plugin::Metadata::EHentai::lookup_gallery( "TOUHOU GUNMANIA", "", "", $ua, $domain, "", 0, 0, 0 ); };
is( $test_eH_gID, $eH_gID, 'eHentai search test 1/2' );
is( $test_eH_gToken, $eH_gToken, 'eHentai search test 2/2' );
diff --git a/tests/tankoubon.t b/tests/tankoubon.t
index 4ca2e8865..7689b8d5c 100644
--- a/tests/tankoubon.t
+++ b/tests/tankoubon.t
@@ -29,9 +29,11 @@ LANraragi::Model::Stats::build_stat_hashes();
my ( $total, $filtered, @rgs );
# Get Tankoubon
-my %tankoubon = LANraragi::Model::Tankoubon::get_tankoubon("TANK_1589141306", 0, 0);
+my ( $total, $filtered, %tankoubon ) = LANraragi::Model::Tankoubon::get_tankoubon("TANK_1589141306", 0, 0);
is($tankoubon{id}, "TANK_1589141306", 'ID test');
is($tankoubon{name}, "Hello", 'Name test');
+is($total, 2, 'Total Test');
+is($filtered, 2, 'Count Test');
ok($tankoubon{archives}[0] eq "28697b96f0ac5858be2666ed10ca47742c955555", 'Archives test');
# List Tankoubon
diff --git a/tools/Documentation/installing-lanraragi/source.md b/tools/Documentation/installing-lanraragi/source.md
index 2dd5f594e..97ab17712 100644
--- a/tools/Documentation/installing-lanraragi/source.md
+++ b/tools/Documentation/installing-lanraragi/source.md
@@ -27,7 +27,9 @@ perlmagick ghostscript npm
_Base software dependencies._
{% hint style="info" %}
-If your package manager requires you to specify which ImageMagick version to install, choose version 7.
+If your package manager requires you to specify which ImageMagick version to install, choose version 7.
+If you're using perlbrew, you'll have to install `Alien::ImageMagick` with CPAN in perlbrew's perl lib, which will handle downloading and building the `perlmagick` API bindings for you.
+(LRR can work without ImageMagick running, but you'll lose out on any thumbnail support)
{% endhint %}
{% hint style="info" %}
diff --git a/tools/build/docker/install-everything.sh b/tools/build/docker/install-everything.sh
index 3957559d0..cfca23e86 100755
--- a/tools/build/docker/install-everything.sh
+++ b/tools/build/docker/install-everything.sh
@@ -24,8 +24,8 @@ done
#Just do everything
apk update
apk add tzdata
-apk add perl perl-io-socket-ssl perl-dev redis libarchive-dev libbz2 openssl-dev zlib-dev linux-headers
-apk add imagemagick imagemagick-perlmagick libwebp-tools libheif
+apk add redis libarchive-dev libbz2 openssl-dev zlib-dev linux-headers
+apk add imagemagick libwebp-tools libheif
apk add g++ make pkgconf gnupg wget curl file
apk add shadow s6 s6-portable-utils ghostscript
@@ -33,20 +33,50 @@ apk add shadow s6 s6-portable-utils ghostscript
if [ -f /etc/alpine-release ]; then
alpine_version=$(cat /etc/alpine-release)
if [ "$alpine_version" = "3.12.12" ]; then
- apk add nodejs-npm
- else # Those packages don't exist on 3.12
- apk add s6-overlay libjxl
+ apk add nodejs-npm tar gcc build-base zlib openssl
+ mkdir -p /usr/src/perl
+ cd /usr/src/perl
+
+ # Install Perl 5.36 at the very least to maintain compat with LRR requirements
+ curl -SLO https://www.cpan.org/src/5.0/perl-5.38.0.tar.gz \
+ && echo '5c4dea06509959fedcccaada8d129518487399b7 perl-5.38.0.tar.gz' | sha1sum -c - \
+ && tar --strip-components=1 -xaf perl-5.38.0.tar.gz -C /usr/src/perl \
+ && rm perl-5.38.0.tar.gz \
+ && ./Configure -des \
+ -Duse64bitall \
+ -Dcccdlflags='-fPIC' \
+ -Dcccdlflags='-fPIC' \
+ -Dccdlflags='-rdynamic' \
+ -Dlocincpth=' ' \
+ -Duselargefiles \
+ -Dusethreads \
+ -Duseshrplib \
+ -Dd_semctl_semun \
+ -Dusenm \
+ -Dprefix='/opt/perl' \
+ && make -j$(nproc) \
+ && make install \
+
+ ln -s /opt/perl/bin/perl5.38.0 /usr/bin/perl
+
+ # Install cpanm
+ curl -L https://cpanmin.us | perl - App::cpanminus
+ ln -s /opt/perl/bin/cpanm /usr/bin/cpanm
+ cpanm IO::Socket::SSL --notest
+
+ else # Those packages either don't exist on 3.12 or aren't necessary with the local perl rebuild
+ apk add perl perl-io-socket-ssl perl-dev s6-overlay libjxl imagemagick-perlmagick
# Install node v18 as v20 breaks with QEMU (https://github.com/nodejs/docker-node/issues/1798)
echo 'http://dl-cdn.alpinelinux.org/alpine/v3.18/main' >> /etc/apk/repositories
apk update
apk add nodejs=18.20.1-r0 npm=10.9.1-r0
+
+ # Install cpanm
+ curl -L https://cpanmin.us | perl - App::cpanminus
fi
fi
-# Install cpanm
-curl -L https://cpanmin.us | perl - App::cpanminus
-
# Check for WSL1 to install specific versions of packages
if [ $WSL -eq 1 ]; then
# Install Linux::Inotify 2.2 explicitly as 2.3 doesn't work properly on WSL:
@@ -54,7 +84,7 @@ if [ $WSL -eq 1 ]; then
# WSL1 works with both default watcher and inotify 2.2, but crashes with inotify 2.3 ("can't open fd 4 as perl handle")
# Doing the install here allows us to use 2.3 on non-WSL builds.
- cpanm https://cpan.metacpan.org/authors/id/M/ML/MLEHMANN/Linux-Inotify2-2.2.tar.gz --reinstall
+ cpanm https://cpan.metacpan.org/authors/id/M/ML/MLEHMANN/Linux-Inotify2-2.2.tar.gz --reinstall --force
fi
#Alpine's libffi build comes with AVX instructions enabled
@@ -74,7 +104,7 @@ if [ $(uname -m) == 'x86_64' ]; then
fi
#Install the LRR dependencies proper
-cd tools && cpanm --notest --installdeps . -M https://cpan.metacpan.org && cd ..
+cd /home/koyomi/lanraragi/tools && cpanm --notest --installdeps . -M https://cpan.metacpan.org && cd ..
if [ $WSL -eq 1 ]; then
npm run lanraragi-installer install-full legacy
else
diff --git a/tools/cpanfile b/tools/cpanfile
index f149d8857..5b2b4a251 100755
--- a/tools/cpanfile
+++ b/tools/cpanfile
@@ -1,5 +1,5 @@
-requires 'perl', '5.20.2';
+requires 'perl', '5.36.0';
requires 'local::lib', 2.000024;
# LRR Core