Add library/genres commands and recommendation skills
- bin/audiobooks: add library [limit] [--full] and genres commands - bin/jellyfin: add library [limit] [--full] and genres commands - skills/book-recommender: new skill for audiobook recommendations - skills/media-recommender: new skill for movie/show recommendations
This commit is contained in:
parent
503f88820d
commit
df0693f01d
4 changed files with 112 additions and 1 deletions
|
|
@ -143,6 +143,47 @@ async function cmdStats() {
|
|||
console.log(`in-progress:${inProgress}\tfinished:${finished}\ttotal-listened:${formatDuration(totalListened)}`);
|
||||
}
|
||||
|
||||
const LIBRARY_ID = '197852a2-2602-4cb9-985c-70f9df5867a8';
|
||||
|
||||
async function cmdLibrary(args) {
|
||||
const full = args.includes('--full');
|
||||
const limitArg = args.find(a => a !== '--full');
|
||||
const endpoint = `/api/libraries/${LIBRARY_ID}/items?limit=0&sort=media.metadata.title`;
|
||||
const data = await api(endpoint);
|
||||
const items = data.results || [];
|
||||
const limit = limitArg ? parseInt(limitArg, 10) : items.length;
|
||||
|
||||
console.log('TITLE\tAUTHOR\tSERIES\tNARRATOR\tGENRES\tDURATION\tYEAR\tDESCRIPTION');
|
||||
for (const item of items.slice(0, limit)) {
|
||||
const meta = item.media?.metadata || {};
|
||||
const title = meta.title || 'Unknown';
|
||||
const author = meta.authorName || '-';
|
||||
const series = meta.seriesName || '-';
|
||||
const narrator = meta.narratorName || '-';
|
||||
const genres = (meta.genres || []).join(',') || '-';
|
||||
const duration = formatDuration(item.media?.duration || 0);
|
||||
const year = meta.publishedYear || '-';
|
||||
const desc = meta.description || '';
|
||||
const descOut = full ? desc.replace(/[\t\n\r]/g, ' ') : (desc.length > 100 ? desc.slice(0, 100).replace(/[\t\n\r]/g, ' ') + '...' : desc.replace(/[\t\n\r]/g, ' ')) || '-';
|
||||
console.log(`${title}\t${author}\t${series}\t${narrator}\t${genres}\t${duration}\t${year}\t${descOut}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function cmdGenres() {
|
||||
const data = await api(`/api/libraries/${LIBRARY_ID}/items?limit=0`);
|
||||
const items = data.results || [];
|
||||
const counts = {};
|
||||
for (const item of items) {
|
||||
for (const g of (item.media?.metadata?.genres || [])) {
|
||||
counts[g] = (counts[g] || 0) + 1;
|
||||
}
|
||||
}
|
||||
const sorted = Object.entries(counts).sort((a, b) => b[1] - a[1]);
|
||||
for (const [genre, count] of sorted) {
|
||||
console.log(`${genre}\t${count}`);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Main ---
|
||||
|
||||
const [cmd, ...args] = process.argv.slice(2);
|
||||
|
|
@ -154,6 +195,8 @@ const run = async () => {
|
|||
case 'finished': await cmdFinished(args); break;
|
||||
case 'stats': await cmdStats(); break;
|
||||
case 'libraries': await cmdLibraries(); break;
|
||||
case 'library': await cmdLibrary(args); break;
|
||||
case 'genres': await cmdGenres(); break;
|
||||
default:
|
||||
console.log(`Usage: audiobooks <command>
|
||||
|
||||
|
|
@ -162,7 +205,9 @@ Commands:
|
|||
recent [limit] Recently active books (default: 5)
|
||||
finished [limit] Finished books (default: 10)
|
||||
stats Listening stats overview
|
||||
libraries List libraries`);
|
||||
libraries List libraries
|
||||
library [limit] All books with metadata (--full for full descriptions)
|
||||
genres List genres with counts`);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue