feat: 更新版本至1.1.4,新增文件属性配置和自定义属性功能

This commit is contained in:
huojl 2026-05-16 12:54:59 +08:00
parent 45e38ca58f
commit e322bea2c2
10 changed files with 180 additions and 29 deletions

View file

@ -1,7 +1,7 @@
{
"id": "sonicnote-sync",
"name": "SonicNote Sync",
"version": "1.1.3",
"version": "1.1.4",
"minAppVersion": "0.15.0",
"description": "Sync recordings from SonicNote to Obsidian as Markdown files",
"author": "EasyLinkIn",

View file

@ -1,6 +1,6 @@
{
"name": "sonicnote-sync",
"version": "1.1.3",
"version": "1.1.4",
"description": "Sync recordings from SonicNote to Obsidian as Markdown files",
"main": "main.js",
"scripts": {

View file

@ -133,10 +133,10 @@
<body>
<header class="header">
<div class="version">v1.1.3</div>
<div class="version">v1.1.4</div>
<h1>SonicNote Sync</h1>
<p>将 SonicNote 妙记的录音、转录、总结同步到 Obsidian以 Markdown 文件管理。</p>
<a class="download-btn" href="sonicnote-sync-v1.1.3.zip" download>
<a class="download-btn" href="sonicnote-sync-v1.1.4.zip" download>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
下载插件
</a>
@ -146,6 +146,11 @@
<div class="card">
<h2>更新记录</h2>
<h3>v1.1.4</h3>
<ul>
<li>新增文件属性配置:可按需开关 Frontmatter 中的内置属性字段</li>
<li>新增自定义属性:支持添加自定义键值对写入所有同步文件</li>
</ul>
<h3>v1.1.3</h3>
<ul>
<li>同步时间显示跟随系统时区</li>
@ -176,7 +181,7 @@
<div class="card">
<h2>安装</h2>
<ol>
<li>点击上方按钮下载 <span class="inline-code">sonicnote-sync-v1.1.3.zip</span></li>
<li>点击上方按钮下载 <span class="inline-code">sonicnote-sync-v1.1.4.zip</span></li>
<li>解压 zip 文件,得到以下文件:
<ul style="margin-top:4px;">
<li><span class="inline-code">main.js</span></li>

Binary file not shown.

Binary file not shown.

View file

@ -1,4 +1,4 @@
import { Recording, TranscriptSegment, SummaryData } from './types';
import { Recording, TranscriptSegment, SummaryData, SonicNotePluginSettings } from './types';
export function sanitizeFileName(name: string): string {
return name
@ -30,29 +30,49 @@ function formatTranscriptTime(ms: number): string {
return formatDuration(totalSeconds);
}
export function generateFrontmatter(recording: Recording, syncTime: string): string {
const lines: string[] = [
'---',
`audio_id: "${recording.audioId}"`,
`record_name: "${recording.recordName || ''}"`,
`record_nick_name: "${(recording.recordNickName || '').replace(/"/g, '\\"')}"`,
`duration: "${recording.duration || '0'}"`,
`record_time: "${recording.recordTime || ''}"`,
`record_type: "${recording.recordType || ''}"`,
`device_name: "${recording.deviceName || ''}"`,
];
export function generateFrontmatter(recording: Recording, syncTime: string, settings: SonicNotePluginSettings): string {
const fields = settings.frontmatterFields;
const lines: string[] = ['---'];
if (recording.audioUrl) {
if (fields.audio_id !== false) {
lines.push(`audio_id: "${recording.audioId}"`);
}
if (fields.record_name !== false) {
lines.push(`record_name: "${recording.recordName || ''}"`);
}
if (fields.record_nick_name !== false) {
lines.push(`record_nick_name: "${(recording.recordNickName || '').replace(/"/g, '\\"')}"`);
}
if (fields.duration !== false) {
lines.push(`duration: "${recording.duration || '0'}"`);
}
if (fields.record_time !== false) {
lines.push(`record_time: "${recording.recordTime || ''}"`);
}
if (fields.record_type !== false) {
lines.push(`record_type: "${recording.recordType || ''}"`);
}
if (fields.device_name !== false) {
lines.push(`device_name: "${recording.deviceName || ''}"`);
}
if (fields.audio_url !== false && recording.audioUrl) {
lines.push(`audio_url: "${recording.audioUrl}"`);
}
if (fields.tags !== false) {
lines.push('tags:');
lines.push(' - sonicnote');
const recordTypeLabel = recording.recordType === '00' ? '通话' : '录音';
lines.push(` - ${recordTypeLabel}`);
}
if (fields.sync_time !== false) {
lines.push(`sync_time: "${syncTime}"`);
}
lines.push('tags:');
lines.push(' - sonicnote');
// Custom frontmatter fields
for (const custom of settings.customFrontmatter) {
lines.push(`${custom.key}: "${custom.value.replace(/"/g, '\\"')}"`);
}
const recordTypeLabel = recording.recordType === '00' ? '通话' : '录音';
lines.push(` - ${recordTypeLabel}`);
lines.push(`sync_time: "${syncTime}"`);
lines.push('---');
return lines.join('\n');
}
@ -72,12 +92,13 @@ export function toMarkdown(
transcript: TranscriptSegment[] | null,
summary: SummaryData | null,
note: string,
syncTime: string
syncTime: string,
settings: SonicNotePluginSettings
): string {
const parts: string[] = [];
// Frontmatter
parts.push(generateFrontmatter(recording, syncTime));
parts.push(generateFrontmatter(recording, syncTime, settings));
parts.push('');
// Title

View file

@ -1,6 +1,6 @@
import { App, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
import { SonicNoteApiClient } from './api';
import { SonicNotePluginSettings, DEFAULT_SETTINGS } from './types';
import { SonicNotePluginSettings, DEFAULT_SETTINGS, BUILTIN_FRONTMATTER_FIELDS, CustomFrontmatterField } from './types';
export class SonicNoteSettingTab extends PluginSettingTab {
private api: SonicNoteApiClient;
@ -50,6 +50,97 @@ export class SonicNoteSettingTab extends PluginSettingTab {
await this.saveSettings();
}));
// Frontmatter fields toggles
const fmSection = containerEl.createDiv();
fmSection.createEl('h3', { text: '文件属性' });
fmSection.createEl('p', { text: '选择同步到 Frontmatter 中的属性字段', cls: 'setting-item-description' });
fmSection.style.marginBottom = '12px';
const fmListEl = fmSection.createDiv();
const renderFrontmatterToggles = () => {
fmListEl.empty();
for (const [key, desc] of Object.entries(BUILTIN_FRONTMATTER_FIELDS)) {
const isRequired = key === 'audio_id' || key === 'sync_time';
if (isRequired) {
new Setting(fmListEl)
.setName(`${desc}`)
.setDesc(key)
.addText(text => {
text.setValue('必要属性').setDisabled(true);
text.inputEl.style.width = 'auto';
text.inputEl.style.color = 'var(--text-muted)';
text.inputEl.style.textAlign = 'center';
text.inputEl.style.border = 'none';
text.inputEl.style.background = 'var(--background-secondary)';
text.inputEl.style.borderRadius = '4px';
text.inputEl.style.padding = '2px 8px';
text.inputEl.style.fontSize = '0.8em';
});
} else {
new Setting(fmListEl)
.setName(`${desc}`)
.setDesc(key)
.addToggle(toggle => {
toggle.setValue(settings.frontmatterFields[key] !== false);
toggle.onChange(async (value) => {
settings.frontmatterFields[key] = value;
await this.saveSettings();
});
});
}
}
};
renderFrontmatterToggles();
// Custom frontmatter fields
const customSection = containerEl.createDiv();
customSection.createEl('h3', { text: '自定义属性' });
customSection.createEl('p', { text: '添加自定义属性到所有同步文件的 Frontmatter 中', cls: 'setting-item-description' });
customSection.style.marginBottom = '12px';
const customListEl = customSection.createDiv();
const renderCustomFields = () => {
customListEl.empty();
for (let i = 0; i < settings.customFrontmatter.length; i++) {
const field = settings.customFrontmatter[i];
new Setting(customListEl)
.addText(text => text
.setPlaceholder('属性名')
.setValue(field.key)
.onChange(async (value) => {
field.key = value;
await this.saveSettings();
}))
.addText(text => text
.setPlaceholder('属性值')
.setValue(field.value)
.onChange(async (value) => {
field.value = value;
await this.saveSettings();
}))
.addExtraButton(btn => btn
.setIcon('trash')
.setTooltip('删除')
.onClick(async () => {
settings.customFrontmatter.splice(i, 1);
await this.saveSettings();
renderCustomFields();
}));
}
};
renderCustomFields();
new Setting(customSection)
.setName('添加属性')
.addButton(btn => btn
.setButtonText('+ 添加')
.onClick(async () => {
settings.customFrontmatter.push({ key: '', value: '' });
await this.saveSettings();
renderCustomFields();
}));
// Login status & actions
const loginSection = containerEl.createDiv();
loginSection.createEl('h3', { text: '账号' });

View file

@ -176,6 +176,6 @@ export class SyncService {
if (name === 'note') note = (val as string) || '';
});
return toMarkdown(recording, transcript, summary, note, syncTime);
return toMarkdown(recording, transcript, summary, note, syncTime, settings);
}
}

View file

@ -1,20 +1,53 @@
// ===== 插件设置 =====
export interface CustomFrontmatterField {
key: string;
value: string;
}
export interface SonicNotePluginSettings {
serverUrl: string;
syncFolder: string;
pageSize: number;
includeTranscript: boolean;
frontmatterFields: Record<string, boolean>;
customFrontmatter: CustomFrontmatterField[];
token: string;
phoneNumber: string;
lastSyncTime: string;
}
export const BUILTIN_FRONTMATTER_FIELDS: Record<string, string> = {
audio_id: '录音 ID',
record_name: '录音文件名',
record_nick_name: '录音标题',
duration: '时长',
record_time: '录音时间',
record_type: '录音类型',
device_name: '设备名称',
audio_url: '音频地址',
tags: '标签',
sync_time: '同步时间',
};
export const DEFAULT_SETTINGS: SonicNotePluginSettings = {
serverUrl: 'https://ainote.easylinkin.com:18048/prod-api',
syncFolder: 'SonicNoteSync',
pageSize: 50,
includeTranscript: true,
frontmatterFields: {
audio_id: true,
record_name: true,
record_nick_name: true,
duration: true,
record_time: true,
record_type: true,
device_name: true,
audio_url: true,
tags: true,
sync_time: true,
},
customFrontmatter: [],
token: '',
phoneNumber: '',
lastSyncTime: '',

View file

@ -2,5 +2,6 @@
"1.0.0": "0.15.0",
"1.1.0": "0.15.0",
"1.1.2": "0.15.0",
"1.1.3": "0.15.0"
"1.1.3": "0.15.0",
"1.1.4": "0.15.0"
}