Skip to content

Instantly share code, notes, and snippets.

@kuusei
Created August 10, 2025 12:40
Show Gist options
  • Save kuusei/f7cd57bb581337c02cb2c91932c12362 to your computer and use it in GitHub Desktop.
Save kuusei/f7cd57bb581337c02cb2c91932c12362 to your computer and use it in GitHub Desktop.
package LANraragi::Plugin::Metadata::ExHentaiMangaManager;
use strict;
use warnings;
use utf8;
use v5.36;
use URI::Escape;
use Mojo::JSON qw(decode_json);
use File::Basename qw(fileparse);
use LANraragi::Utils::Logging qw(get_plugin_logger);
use Encode qw(decode);
use Encode::Guess;
sub plugin_info {
return (
name => "ExHentai Manga Manager 元数据获取",
type => "metadata",
namespace => "exhentai-manga-manager",
author => "kuusei",
version => "1.1",
description => "通过自定义 API 使用文件名获取元数据,自动检测编码,并合并去重已有标签",
parameters => [
{ type => "string", desc => "API 基础 URL(不含 ?filter= 部分)" },
],
oneshot_arg => "文件名或过滤字符串",
cooldown => 0,
);
}
sub decode_filename {
my ($raw) = @_;
my $decoder = Encode::Guess->guess($raw);
if (ref($decoder)) {
return $decoder->decode($raw);
} else {
eval {
return decode('UTF-8', $raw, Encode::FB_CROAK);
} or do {
return $raw;
};
}
}
sub get_tags {
shift;
my $lrr_info = shift;
my ($base_api) = @_;
my $logger = get_plugin_logger();
my $ua = $lrr_info->{user_agent};
my $raw_path = $lrr_info->{file_path} // '';
$logger->info("原始文件路径: $raw_path");
my $decoded_path = eval { decode_filename($raw_path) };
if ($@) {
$logger->error("解码失败,使用原始路径: $@");
$decoded_path = $raw_path;
}
$logger->info("解码后文件路径: $decoded_path");
my ($filename, $filepath, $suffix) = fileparse($decoded_path, qr/\.[^.]*/);
my $file = $filename . $suffix;
$file =~ s{^/+}{};
$logger->info("用于API查询的文件名: '$file'");
my $url = $base_api // '';
$url =~ s/\/$//;
$url .= "?filter=" . uri_escape_utf8($file) . "&length=1000000";
$logger->info("请求API的URL: $url");
my $res = $ua->max_redirects(5)->get($url)->result;
if ($res->is_error) {
$logger->error("API请求失败: " . $res->message);
die "API请求失败: " . $res->message . "\n";
}
my $json;
eval { $json = decode_json($res->body); };
if ($@) {
$logger->error("JSON解析失败: $@");
die "JSON解析失败\n";
}
if (!exists $json->{data} || ref $json->{data} ne 'ARRAY' || !@{ $json->{data} }) {
$logger->info("没有找到匹配的资源或数据格式无效");
die "No matching gallery found\n";
}
my $first = $json->{data}[0];
my $tags_raw = $first->{tags} // '';
$tags_raw =~ s/^\s+|\s+$//g;
if ($tags_raw =~ /\S/ && $tags_raw !~ /,/) {
$tags_raw =~ s/\s+/, /g;
} else {
$tags_raw =~ s/\s*,\s*/, /g;
}
my @tags = grep { defined && $_ ne '' } map { s/^\s+|\s+$//g; $_ } split /,\s*/, $tags_raw;
my $category = $first->{category} // '';
push @tags, "category:$category" if $category ne '';
if ($first->{url} && $first->{url} =~ m{https?://(.+)}) {
push @tags, "source:" . $1;
}
my $existing_tags_str = $lrr_info->{existing_tags} // '';
$logger->debug("现有标签(原始): $existing_tags_str");
my @existing_tags =
$existing_tags_str eq '' ? () :
grep { defined && $_ ne '' } map { s/^\s+|\s+$//g; $_ } split /,\s*/, $existing_tags_str;
my %seen;
my @final_tags;
for my $t (@existing_tags) {
next unless defined $t && $t ne '';
push @final_tags, $t unless $seen{$t}++;
}
for my $t (@tags) {
next unless defined $t && $t ne '';
push @final_tags, $t unless $seen{$t}++;
}
my $tags_final = join(", ", @final_tags);
$logger->info("已有标签: " . ($existing_tags_str eq '' ? '(无)' : $existing_tags_str));
$logger->info("插件新增/合并后最终标签: $tags_final");
return (
tags => $tags_final,
title => $first->{title} // '',
source => $first->{url} // ''
);
}
1;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment