This commit is contained in:
@@ -16,8 +16,8 @@ This plugin analyzes video files to determine if they are anime content. Detecti
|
|||||||
| Field | Description |
|
| Field | Description |
|
||||||
|-------|-------------|
|
|-------|-------------|
|
||||||
| `anime` | Boolean indicating if content is anime |
|
| `anime` | Boolean indicating if content is anime |
|
||||||
| `titles/jpn` | Japanese title (if detected) |
|
| `titles/jpn/{name}` | `"true"` — Japanese title (if detected) |
|
||||||
| `titles/rom` | Romanized title |
|
| `titles/jpl/{name}` | `"true"` — romaji title |
|
||||||
| `genres` | Adds "Anime" to genres set |
|
| `genres` | Adds "Anime" to genres set |
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|||||||
+19
-8
@@ -8,8 +8,8 @@
|
|||||||
* - "anime" in file path
|
* - "anime" in file path
|
||||||
*
|
*
|
||||||
* Matches old AnimeDetector output:
|
* Matches old AnimeDetector output:
|
||||||
* - titles/jpn (if kana)
|
* - titles/jpn/<name> (if kana) — key-set member, METADATA_KEYS.md §3
|
||||||
* - titles/{romajiIsoCode} (if romaji)
|
* - titles/{romajiIsoCode}/<name> (if romaji)
|
||||||
* - anime
|
* - anime
|
||||||
* - genres (add "Anime")
|
* - genres (add "Anime")
|
||||||
*/
|
*/
|
||||||
@@ -22,6 +22,16 @@ import { MetaCoreClient } from './meta-core-client.js';
|
|||||||
// romajiIsoCode from @metazla/meta-interface is just 'jpl'
|
// romajiIsoCode from @metazla/meta-interface is just 'jpl'
|
||||||
const romajiIsoCode = 'jpl';
|
const romajiIsoCode = 'jpl';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `titles/<lang3>/<name>` key-set member key (METADATA_KEYS.md §3): trimmed,
|
||||||
|
* whitespace collapsed, `/` (the key-set separator) written as U+2215 `∕`.
|
||||||
|
* `undefined` when nothing is left to name.
|
||||||
|
*/
|
||||||
|
export function titleMemberKey(lang3: string, name: string | undefined | null): string | undefined {
|
||||||
|
const clean = (name ?? '').trim().replace(/\s+/g, ' ').replace(/\//g, '\u2215');
|
||||||
|
return clean ? `titles/${lang3}/${clean}` : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export const manifest: PluginManifest = {
|
export const manifest: PluginManifest = {
|
||||||
id: 'anime-detector',
|
id: 'anime-detector',
|
||||||
name: 'Anime Detector',
|
name: 'Anime Detector',
|
||||||
@@ -35,8 +45,9 @@ export const manifest: PluginManifest = {
|
|||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
schema: {
|
schema: {
|
||||||
anime: { label: 'Is Anime', type: 'boolean', readonly: true },
|
anime: { label: 'Is Anime', type: 'boolean', readonly: true },
|
||||||
'titles/jpn': { label: 'Japanese Title', type: 'string' },
|
// Key-set members `titles/<lang3>/<name> = "true"` (METADATA_KEYS.md §3).
|
||||||
[`titles/${romajiIsoCode}`]: { label: 'Romaji Title', type: 'string' },
|
'titles/jpn/*': { label: 'Japanese Titles', type: 'boolean', readonly: true },
|
||||||
|
[`titles/${romajiIsoCode}/*`]: { label: 'Romaji Titles', type: 'boolean', readonly: true },
|
||||||
},
|
},
|
||||||
config: {},
|
config: {},
|
||||||
};
|
};
|
||||||
@@ -110,10 +121,10 @@ export async function process(
|
|||||||
|
|
||||||
// Set Japanese title appropriately
|
// Set Japanese title appropriately
|
||||||
if (isJpn && originalTitle) {
|
if (isJpn && originalTitle) {
|
||||||
if (isKana(originalTitle)) {
|
const lang3 = isKana(originalTitle) ? 'jpn' : romajiIsoCode;
|
||||||
await metaCore.setProperty(cid, 'titles/jpn', originalTitle);
|
const key = titleMemberKey(lang3, originalTitle);
|
||||||
} else {
|
if (key) {
|
||||||
await metaCore.setProperty(cid, `titles/${romajiIsoCode}`, originalTitle);
|
await metaCore.mergeMetadata(cid, { [key]: 'true' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { animeKeywords } from '@metazla/filename-tools';
|
|||||||
// Dynamic import of plugin module
|
// Dynamic import of plugin module
|
||||||
let manifest: typeof import('../src/plugin.js').manifest;
|
let manifest: typeof import('../src/plugin.js').manifest;
|
||||||
let processFile: typeof import('../src/plugin.js').process;
|
let processFile: typeof import('../src/plugin.js').process;
|
||||||
|
let titleMemberKey: typeof import('../src/plugin.js').titleMemberKey;
|
||||||
|
|
||||||
// Mock callback collector
|
// Mock callback collector
|
||||||
interface CallbackResult {
|
interface CallbackResult {
|
||||||
@@ -33,6 +34,7 @@ describe('Anime Detector Plugin Integration Tests', () => {
|
|||||||
const plugin = await import('../src/plugin.js');
|
const plugin = await import('../src/plugin.js');
|
||||||
manifest = plugin.manifest;
|
manifest = plugin.manifest;
|
||||||
processFile = plugin.process;
|
processFile = plugin.process;
|
||||||
|
titleMemberKey = plugin.titleMemberKey;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Manifest', () => {
|
describe('Manifest', () => {
|
||||||
@@ -48,7 +50,14 @@ describe('Anime Detector Plugin Integration Tests', () => {
|
|||||||
|
|
||||||
it('declares correct schema', () => {
|
it('declares correct schema', () => {
|
||||||
expect(manifest.schema).toHaveProperty('anime');
|
expect(manifest.schema).toHaveProperty('anime');
|
||||||
expect(manifest.schema).toHaveProperty('titles/jpn');
|
expect(manifest.schema).toHaveProperty(['titles/jpn/*']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('builds titles key-set member keys', () => {
|
||||||
|
expect(titleMemberKey('jpn', ' ナルト ')).toBe('titles/jpn/ナルト');
|
||||||
|
expect(titleMemberKey('eng', 'Fate/Zero')).toBe('titles/eng/Fate\u2215Zero');
|
||||||
|
expect(titleMemberKey('jpl', 'Sousou no\tFrieren')).toBe('titles/jpl/Sousou no Frieren');
|
||||||
|
expect(titleMemberKey('jpn', ' ')).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user