feat(watchlist): note parsing (frontmatter, My Notes, tmdb ref)
This commit is contained in:
parent
cade7104db
commit
732ec8adf6
2 changed files with 93 additions and 0 deletions
36
packages/obsidian/src/watchlist/parse.ts
Normal file
36
packages/obsidian/src/watchlist/parse.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
export interface ParsedNote {
|
||||
frontmatter: Record<string, string>;
|
||||
body: string;
|
||||
}
|
||||
|
||||
export function parseNote(content: string): ParsedNote {
|
||||
const m = /^---\n([\s\S]*?)\n---([\s\S]*)$/.exec(content);
|
||||
const frontmatter: Record<string, string> = {};
|
||||
let body = content;
|
||||
if (m) {
|
||||
body = m[2];
|
||||
for (const line of m[1].split('\n')) {
|
||||
const mm = /^([A-Za-z0-9_]+):\s*(.*)$/.exec(line);
|
||||
if (mm) frontmatter[mm[1]] = mm[2].trim();
|
||||
}
|
||||
}
|
||||
return { frontmatter, body };
|
||||
}
|
||||
|
||||
export function extractMyNotes(body: string): string {
|
||||
const m = /##\s*My Notes\s*([\s\S]*)$/.exec(body ?? '');
|
||||
return m ? m[1].trim() : '';
|
||||
}
|
||||
|
||||
function stripQuotes(s: string | undefined): string {
|
||||
return (s ?? '').trim().replace(/^"|"$/g, '');
|
||||
}
|
||||
|
||||
export function noteTmdbRef(fm: Record<string, string>): { tmdbId: string; isMovie: boolean } | null {
|
||||
const tid = stripQuotes(fm['tmdb_id']);
|
||||
if (tid) return { tmdbId: tid, isMovie: stripQuotes(fm['media_type']) === 'Movie' };
|
||||
const rawId = stripQuotes(fm['id']);
|
||||
const ds = (fm['dataSource'] ?? '').trim();
|
||||
if (rawId && ds.startsWith('TMDB')) return { tmdbId: rawId, isMovie: ds.includes('Movie') };
|
||||
return null;
|
||||
}
|
||||
57
tests/watchlist-parse.test.ts
Normal file
57
tests/watchlist-parse.test.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { describe, expect, test } from 'bun:test';
|
||||
import { parseNote, extractMyNotes, noteTmdbRef } from 'packages/obsidian/src/watchlist/parse';
|
||||
|
||||
const NOTE = `---
|
||||
type: watchlist_item
|
||||
media_type: TV Series
|
||||
tmdb_id: 84958
|
||||
last_air_date: 2023-11-09
|
||||
watch_status: Watched
|
||||
---
|
||||
|
||||
# Loki
|
||||
|
||||
## My Notes
|
||||
|
||||
great finale
|
||||
`;
|
||||
|
||||
describe('parseNote', () => {
|
||||
test('splits frontmatter and body', () => {
|
||||
const { frontmatter, body } = parseNote(NOTE);
|
||||
expect(frontmatter['tmdb_id']).toBe('84958');
|
||||
expect(frontmatter['media_type']).toBe('TV Series');
|
||||
expect(body).toContain('# Loki');
|
||||
});
|
||||
test('no frontmatter → empty fm, full body', () => {
|
||||
const { frontmatter, body } = parseNote('# Just a heading');
|
||||
expect(Object.keys(frontmatter).length).toBe(0);
|
||||
expect(body).toBe('# Just a heading');
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractMyNotes', () => {
|
||||
test('extracts trailing section', () => {
|
||||
expect(extractMyNotes(parseNote(NOTE).body)).toBe('great finale');
|
||||
});
|
||||
test('missing section → empty', () => {
|
||||
expect(extractMyNotes('# T\n\ncontent')).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('noteTmdbRef', () => {
|
||||
test('canonical note', () => {
|
||||
expect(noteTmdbRef({ tmdb_id: '84958', media_type: 'TV Series' })).toEqual({ tmdbId: '84958', isMovie: false });
|
||||
expect(noteTmdbRef({ tmdb_id: '693134', media_type: 'Movie' })).toEqual({ tmdbId: '693134', isMovie: true });
|
||||
});
|
||||
test('quoted values stripped', () => {
|
||||
expect(noteTmdbRef({ tmdb_id: '"84958"', media_type: '"TV Series"' })).toEqual({ tmdbId: '84958', isMovie: false });
|
||||
});
|
||||
test('raw Media DB note fallback (id + dataSource)', () => {
|
||||
expect(noteTmdbRef({ id: '693134', dataSource: 'TMDBMovieAPI' })).toEqual({ tmdbId: '693134', isMovie: true });
|
||||
expect(noteTmdbRef({ id: '84958', dataSource: 'TMDBSeriesAPI' })).toEqual({ tmdbId: '84958', isMovie: false });
|
||||
});
|
||||
test('no id → null', () => {
|
||||
expect(noteTmdbRef({ type: 'list' })).toBeNull();
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue