mirror of
https://github.com/long2icc/sonicnote-sync.git
synced 2026-07-22 07:45:14 +00:00
feat: 更新版本至1.1.4,新增文件属性配置和自定义属性功能
This commit is contained in:
parent
45e38ca58f
commit
e322bea2c2
10 changed files with 180 additions and 29 deletions
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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.
BIN
release/sonicnote-sync-v1.1.4.zip
Normal file
BIN
release/sonicnote-sync-v1.1.4.zip
Normal file
Binary file not shown.
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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: '账号' });
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
33
src/types.ts
33
src/types.ts
|
|
@ -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: '',
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue