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