mirror of
https://github.com/hyungyunlim/obsidian-naver-blog-importer.git
synced 2026-07-22 06:45:11 +00:00
fix: improve naver blog bulk import flow
This commit is contained in:
parent
e9612bd93c
commit
01b51b73ee
9 changed files with 126 additions and 90 deletions
102
main.ts
102
main.ts
|
|
@ -66,7 +66,7 @@ export default class NaverBlogPlugin extends Plugin {
|
|||
this.aiService = new AIService(this.settings);
|
||||
|
||||
// Initialize Blog service
|
||||
this.blogService = new BlogService(this.app, this.settings, this.createMarkdownFile.bind(this) as (post: ProcessedBlogPost) => Promise<void>);
|
||||
this.blogService = new BlogService(this.app, this.settings, this.createMarkdownFile.bind(this) as (post: ProcessedBlogPost) => Promise<unknown>);
|
||||
|
||||
// Initialize Image service
|
||||
this.imageService = new ImageService(this.app, this.settings);
|
||||
|
|
@ -264,9 +264,28 @@ export default class NaverBlogPlugin extends Plugin {
|
|||
return await this.blogService.fetchNaverBlogPosts(blogId, maxPosts);
|
||||
}
|
||||
|
||||
private async ensureFolderExists(folderPath: string): Promise<void> {
|
||||
const normalizedPath = normalizePath(folderPath);
|
||||
if (!normalizedPath) return;
|
||||
|
||||
const parts = normalizedPath.split('/').filter(Boolean);
|
||||
let currentPath = '';
|
||||
|
||||
for (const part of parts) {
|
||||
currentPath = currentPath ? `${currentPath}/${part}` : part;
|
||||
const existing = this.app.vault.getAbstractFileByPath(currentPath);
|
||||
|
||||
if (!existing) {
|
||||
await this.app.vault.createFolder(currentPath);
|
||||
} else if (existing instanceof TFile) {
|
||||
throw new Error(`Cannot create folder "${currentPath}" because a file exists at that path`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async callAI(messages: Array<{role: string, content: string}>, maxTokens: number = 150): Promise<string> {
|
||||
const apiKey = APIClientFactory.getApiKey(this.settings);
|
||||
if (!apiKey) {
|
||||
if (!apiKey && this.settings.aiProvider !== 'ollama') {
|
||||
throw new Error('No API key configured for selected AI provider');
|
||||
}
|
||||
|
||||
|
|
@ -276,6 +295,10 @@ export default class NaverBlogPlugin extends Plugin {
|
|||
return await client.chat(messages, maxTokens, model);
|
||||
}
|
||||
|
||||
private isAIAvailable(): boolean {
|
||||
return this.settings.aiProvider === 'ollama' || APIClientFactory.getApiKey(this.settings).trim().length > 0;
|
||||
}
|
||||
|
||||
getModelName(): string {
|
||||
return APIClientFactory.getModelName(this.settings);
|
||||
}
|
||||
|
|
@ -343,7 +366,7 @@ export default class NaverBlogPlugin extends Plugin {
|
|||
|
||||
|
||||
async generateAITags(title: string, content: string): Promise<string[]> {
|
||||
if (!this.settings.enableAiTags) {
|
||||
if (!this.settings.enableAiTags || !this.isAIAvailable()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
@ -404,7 +427,7 @@ JSON 배열로만 응답하세요. 예: ["리뷰", "기술", "일상"]`
|
|||
}
|
||||
|
||||
async generateAIExcerpt(title: string, content: string): Promise<string> {
|
||||
if (!this.settings.enableAiExcerpt) {
|
||||
if (!this.settings.enableAiExcerpt || !this.isAIAvailable()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
|
|
@ -437,7 +460,8 @@ JSON 배열로만 응답하세요. 예: ["리뷰", "기술", "일상"]`
|
|||
async createMarkdownFile(post: ProcessedBlogPost): Promise<TFile | null> {
|
||||
try {
|
||||
// Generate AI tags and append to original tags (avoid duplicates)
|
||||
if (this.settings.enableAiTags) {
|
||||
const shouldUseAI = this.isAIAvailable();
|
||||
if (this.settings.enableAiTags && shouldUseAI) {
|
||||
const aiTags = await this.generateAITags(post.title, post.content);
|
||||
if (aiTags && aiTags.length > 0) {
|
||||
const existingTags = new Set(post.tags.map(t => t.toLowerCase()));
|
||||
|
|
@ -446,7 +470,7 @@ JSON 배열로만 응답하세요. 예: ["리뷰", "기술", "일상"]`
|
|||
}
|
||||
}
|
||||
|
||||
if (this.settings.enableAiExcerpt) {
|
||||
if (this.settings.enableAiExcerpt && shouldUseAI) {
|
||||
// Add small delay between AI calls to avoid rate limiting
|
||||
if (this.settings.enableAiTags) {
|
||||
await new Promise(resolve => window.setTimeout(resolve, API_DELAYS.betweenPosts));
|
||||
|
|
@ -462,6 +486,8 @@ JSON 배열로만 응답하세요. 예: ["리뷰", "기술", "일상"]`
|
|||
// Blog images/videos go to attachments subfolder inside the blog folder
|
||||
const blogImageFolder = normalizePath(`${folder}/attachments`);
|
||||
|
||||
await this.ensureFolderExists(folder);
|
||||
|
||||
// Process images if enabled
|
||||
let processedContent = post.content;
|
||||
if (this.settings.enableImageDownload) {
|
||||
|
|
@ -479,18 +505,6 @@ JSON 배열로만 응답하세요. 예: ["리뷰", "기술", "일상"]`
|
|||
);
|
||||
}
|
||||
|
||||
// Ensure base folder exists
|
||||
const baseFolderExists = this.app.vault.getAbstractFileByPath(baseFolder);
|
||||
if (!baseFolderExists) {
|
||||
await this.app.vault.createFolder(baseFolder);
|
||||
}
|
||||
|
||||
// Ensure blogId subfolder exists
|
||||
const folderExists = this.app.vault.getAbstractFileByPath(folder);
|
||||
if (!folderExists) {
|
||||
await this.app.vault.createFolder(folder);
|
||||
}
|
||||
|
||||
const filepath = normalizePath(`${folder}/${filename}`);
|
||||
|
||||
// Create frontmatter
|
||||
|
|
@ -511,20 +525,22 @@ JSON 배열로만 응답하세요. 예: ["리뷰", "기술", "일상"]`
|
|||
|
||||
new Notice(`Created: ${filename}`);
|
||||
return createdFile;
|
||||
} catch {
|
||||
} catch (error) {
|
||||
console.error(`Failed to create file for: ${post.title}`, error);
|
||||
new Notice(`Failed to create file for: ${post.title}`);
|
||||
return null;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async createCafeMarkdownFile(post: ProcessedCafePost): Promise<TFile | null> {
|
||||
try {
|
||||
// Generate AI tags and excerpt if enabled
|
||||
if (this.settings.enableAiTags) {
|
||||
const shouldUseAI = this.isAIAvailable();
|
||||
if (this.settings.enableAiTags && shouldUseAI) {
|
||||
post.tags = await this.generateAITags(post.title, post.content);
|
||||
}
|
||||
|
||||
if (this.settings.enableAiExcerpt) {
|
||||
if (this.settings.enableAiExcerpt && shouldUseAI) {
|
||||
if (this.settings.enableAiTags) {
|
||||
await new Promise(resolve => window.setTimeout(resolve, API_DELAYS.betweenPosts));
|
||||
}
|
||||
|
|
@ -540,6 +556,8 @@ JSON 배열로만 응답하세요. 예: ["리뷰", "기술", "일상"]`
|
|||
// Cafe images go to attachments subfolder inside the cafe folder
|
||||
const cafeImageFolder = normalizePath(`${folder}/attachments`);
|
||||
|
||||
await this.ensureFolderExists(folder);
|
||||
|
||||
// Process images if enabled (use cafeSettings if available, otherwise use blog's enableImageDownload)
|
||||
let processedContent = post.content;
|
||||
const shouldDownloadImages = this.settings.cafeSettings?.downloadCafeImages ?? this.settings.enableImageDownload;
|
||||
|
|
@ -559,18 +577,6 @@ JSON 배열로만 응답하세요. 예: ["리뷰", "기술", "일상"]`
|
|||
);
|
||||
}
|
||||
|
||||
// Ensure base folder exists
|
||||
const baseFolderExists = this.app.vault.getAbstractFileByPath(baseFolder);
|
||||
if (!baseFolderExists) {
|
||||
await this.app.vault.createFolder(baseFolder);
|
||||
}
|
||||
|
||||
// Ensure cafeName subfolder exists
|
||||
const folderExists = this.app.vault.getAbstractFileByPath(folder);
|
||||
if (!folderExists) {
|
||||
await this.app.vault.createFolder(folder);
|
||||
}
|
||||
|
||||
const filepath = normalizePath(`${folder}/${filename}`);
|
||||
|
||||
// Create frontmatter for cafe post
|
||||
|
|
@ -597,9 +603,10 @@ JSON 배열로만 응답하세요. 예: ["리뷰", "기술", "일상"]`
|
|||
|
||||
new Notice(`Created: ${filename}`);
|
||||
return createdFile;
|
||||
} catch {
|
||||
} catch (error) {
|
||||
console.error(`Failed to create file for: ${post.title}`, error);
|
||||
new Notice(`Failed to create file for: ${post.title}`);
|
||||
return null;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -644,7 +651,7 @@ JSON 배열로만 응답하세요. 예: ["리뷰", "기술", "일상"]`
|
|||
async createBrunchMarkdownFile(post: ProcessedBrunchPost): Promise<TFile | null> {
|
||||
try {
|
||||
// Generate AI tags if enabled and no original tags
|
||||
if (this.settings.enableAiTags && (!post.originalTags || post.originalTags.length === 0)) {
|
||||
if (this.settings.enableAiTags && this.isAIAvailable() && (!post.originalTags || post.originalTags.length === 0)) {
|
||||
post.originalTags = await this.generateAITags(post.title, post.content);
|
||||
}
|
||||
|
||||
|
|
@ -659,6 +666,8 @@ JSON 배열로만 응답하세요. 예: ["리뷰", "기술", "일상"]`
|
|||
// Brunch images go to attachments subfolder
|
||||
const brunchImageFolder = normalizePath(`${folder}/attachments`);
|
||||
|
||||
await this.ensureFolderExists(folder);
|
||||
|
||||
// Process images if enabled
|
||||
let processedContent = post.content;
|
||||
const shouldDownloadImages = this.settings.brunchSettings?.downloadBrunchImages ?? this.settings.enableImageDownload;
|
||||
|
|
@ -687,18 +696,6 @@ JSON 배열로만 응답하세요. 예: ["리뷰", "기술", "일상"]`
|
|||
}
|
||||
}
|
||||
|
||||
// Ensure base folder exists
|
||||
const baseFolderExists = this.app.vault.getAbstractFileByPath(baseFolder);
|
||||
if (!baseFolderExists) {
|
||||
await this.app.vault.createFolder(baseFolder);
|
||||
}
|
||||
|
||||
// Ensure username subfolder exists
|
||||
const folderExists = this.app.vault.getAbstractFileByPath(folder);
|
||||
if (!folderExists) {
|
||||
await this.app.vault.createFolder(folder);
|
||||
}
|
||||
|
||||
const filepath = normalizePath(`${folder}/${filename}`);
|
||||
|
||||
// Create frontmatter
|
||||
|
|
@ -718,9 +715,10 @@ JSON 배열로만 응답하세요. 예: ["리뷰", "기술", "일상"]`
|
|||
|
||||
new Notice(`Created: ${filename}`);
|
||||
return createdFile;
|
||||
} catch {
|
||||
} catch (error) {
|
||||
console.error(`Failed to create file for: ${post.title}`, error);
|
||||
new Notice(`Failed to create file for: ${post.title}`);
|
||||
return null;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1019,4 +1017,4 @@ JSON 배열로만 응답하세요. 예: ["리뷰", "기술", "일상"]`
|
|||
new Notice(this.i18n.t('notices.delete_failed', { error: message }), NOTICE_TIMEOUTS.medium);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "naver-blog-importer",
|
||||
"name": "Naver Blog Importer",
|
||||
"version": "1.10.2",
|
||||
"version": "1.10.3",
|
||||
"minAppVersion": "1.8.7",
|
||||
"description": "Import posts from Naver Blog, Cafe, News, and Kakao Brunch with AI-powered features, subscription management, and comprehensive content parsing.",
|
||||
"author": "hyungyunlim",
|
||||
|
|
|
|||
|
|
@ -40,6 +40,11 @@ export interface NaverBlogPost {
|
|||
originalTags: string[];
|
||||
}
|
||||
|
||||
export interface NaverBlogFetchPostsOptions {
|
||||
excludeLogNos?: Set<string>;
|
||||
onProgress?: (current: number, total: number, post: Omit<NaverBlogPost, 'content' | 'blogId' | 'originalTags'>) => void;
|
||||
}
|
||||
|
||||
export class NaverBlogFetcher {
|
||||
private blogId: string;
|
||||
|
||||
|
|
@ -80,7 +85,7 @@ export class NaverBlogFetcher {
|
|||
}
|
||||
}
|
||||
|
||||
async fetchPosts(maxPosts?: number): Promise<NaverBlogPost[]> {
|
||||
async fetchPosts(maxPosts?: number, options: NaverBlogFetchPostsOptions = {}): Promise<NaverBlogPost[]> {
|
||||
try {
|
||||
// Get blog post list
|
||||
let posts = await this.getPostList(maxPosts);
|
||||
|
|
@ -114,6 +119,10 @@ export class NaverBlogFetcher {
|
|||
posts = posts.slice(0, maxPosts);
|
||||
}
|
||||
|
||||
if (options.excludeLogNos && options.excludeLogNos.size > 0) {
|
||||
posts = posts.filter(post => !options.excludeLogNos?.has(post.logNo));
|
||||
}
|
||||
|
||||
// Fetch content for each post
|
||||
const postsWithContent: NaverBlogPost[] = [];
|
||||
|
||||
|
|
@ -121,6 +130,7 @@ export class NaverBlogFetcher {
|
|||
const post = posts[i];
|
||||
|
||||
try {
|
||||
options.onProgress?.(i + 1, posts.length, post);
|
||||
const parsed = await this.fetchPostContent(post.logNo);
|
||||
postsWithContent.push({
|
||||
title: parsed.title !== 'Untitled' ? parsed.title : post.title,
|
||||
|
|
@ -136,7 +146,7 @@ export class NaverBlogFetcher {
|
|||
|
||||
|
||||
// Add delay to be respectful to the server
|
||||
await this.delay(1000);
|
||||
await this.delay(250);
|
||||
} catch (error) {
|
||||
|
||||
// Create error post for failed fetch
|
||||
|
|
@ -155,7 +165,7 @@ export class NaverBlogFetcher {
|
|||
|
||||
|
||||
// Add delay to be respectful to the server
|
||||
await this.delay(500);
|
||||
await this.delay(250);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -191,7 +201,8 @@ export class NaverBlogFetcher {
|
|||
// Try multiple pages to get more posts
|
||||
let currentPage = 1;
|
||||
let hasMore = true;
|
||||
const maxPages = maxPosts ? Math.min(Math.ceil(maxPosts / 30), 10) : 20; // Limit pages based on maxPosts, default to 20 pages (600 posts max)
|
||||
const postsPerPage = 10;
|
||||
const maxPages = maxPosts ? Math.ceil(maxPosts / postsPerPage) : 100; // Default cap: 100 pages (1000 posts max)
|
||||
const postLimit = maxPosts || 1000; // Default to 1000 if no limit specified
|
||||
|
||||
while (hasMore && currentPage <= maxPages && posts.length < postLimit) {
|
||||
|
|
@ -220,13 +231,15 @@ export class NaverBlogFetcher {
|
|||
if (pagePosts.length > 0) {
|
||||
|
||||
// Add only new posts (avoid duplicates)
|
||||
let newPostCount = 0;
|
||||
for (const post of pagePosts) {
|
||||
if (!posts.find(p => p.logNo === post.logNo)) {
|
||||
posts.push(post);
|
||||
newPostCount++;
|
||||
}
|
||||
}
|
||||
|
||||
foundPostsOnPage = true;
|
||||
foundPostsOnPage = newPostCount > 0;
|
||||
break; // Found posts with this URL pattern, no need to try others
|
||||
}
|
||||
}
|
||||
|
|
@ -2028,4 +2041,4 @@ export interface NaverBlogProfile {
|
|||
nickname: string;
|
||||
profileImageUrl?: string;
|
||||
bio?: string;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "obsidian-naver-blog-importer",
|
||||
"version": "1.10.2",
|
||||
"version": "1.10.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "obsidian-naver-blog-importer",
|
||||
"version": "1.10.2",
|
||||
"version": "1.10.3",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "obsidian-naver-blog-importer",
|
||||
"version": "1.10.2",
|
||||
"version": "1.10.3",
|
||||
"description": "Import posts from Naver Blog directly into Obsidian",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export class BlogService {
|
|||
constructor(
|
||||
private app: App,
|
||||
private settings: NaverBlogSettings,
|
||||
private createMarkdownFile: (post: ProcessedBlogPost) => Promise<void>
|
||||
private createMarkdownFile: (post: ProcessedBlogPost) => Promise<unknown>
|
||||
) {}
|
||||
|
||||
async fetchNaverBlogPosts(blogId: string, maxPosts?: number, options?: SyncOptions): Promise<ProcessedBlogPost[]> {
|
||||
|
|
@ -31,8 +31,16 @@ export class BlogService {
|
|||
// Use settings value if maxPosts is not provided and settings value is > 0
|
||||
const effectiveMaxPosts = maxPosts || (this.settings.postImportLimit > 0 ? this.settings.postImportLimit : undefined);
|
||||
|
||||
const existingLogNos = this.settings.enableDuplicateCheck ? this.getExistingLogNos() : undefined;
|
||||
const fetcher = new NaverBlogFetcher(blogId);
|
||||
const posts = await fetcher.fetchPosts(effectiveMaxPosts);
|
||||
const posts = await fetcher.fetchPosts(effectiveMaxPosts, {
|
||||
excludeLogNos: existingLogNos,
|
||||
onProgress: silent ? undefined : (current, total, post) => {
|
||||
if (total > 1) {
|
||||
new Notice(`Fetching post content (${current}/${total}): ${post.title || post.logNo}`, 2500);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Hide the persistent notice
|
||||
if (fetchNotice) {
|
||||
|
|
@ -40,24 +48,20 @@ export class BlogService {
|
|||
fetchNotice = null;
|
||||
}
|
||||
|
||||
// Filter out duplicates if enabled
|
||||
let filteredPosts = posts;
|
||||
if (this.settings.enableDuplicateCheck) {
|
||||
const existingLogNos = this.getExistingLogNos();
|
||||
filteredPosts = posts.filter(post => !existingLogNos.has(post.logNo));
|
||||
if (!silent) {
|
||||
new Notice(`Found ${posts.length} posts, ${filteredPosts.length} new posts after duplicate check`, 4000);
|
||||
new Notice(`Found ${posts.length} new posts after duplicate check`, 4000);
|
||||
}
|
||||
} else if (!silent) {
|
||||
new Notice(`Found ${posts.length} posts`, 4000);
|
||||
}
|
||||
|
||||
if (!silent) {
|
||||
new Notice(`Processing ${filteredPosts.length} posts...`, 3000);
|
||||
new Notice(`Processing ${posts.length} posts...`, 3000);
|
||||
}
|
||||
|
||||
// Convert to ProcessedBlogPost - use original tags from blog
|
||||
const processedPosts: ProcessedBlogPost[] = filteredPosts.map(post => ({
|
||||
const processedPosts: ProcessedBlogPost[] = posts.map(post => ({
|
||||
...post,
|
||||
title: post.title.replace(/^\[.*?\]\s*/, '').replace(/\s*\[.*?\]$/, '').trim(), // Remove [] brackets from title start/end
|
||||
tags: post.originalTags || [],
|
||||
|
|
@ -134,8 +138,10 @@ export class BlogService {
|
|||
const postProgress = `(${i + 1}/${totalBlogs}) post (${j + 1}/${posts.length})`;
|
||||
new Notice(`Creating ${postProgress}: ${post.title}`, 3000);
|
||||
}
|
||||
await this.createMarkdownFile(post);
|
||||
result.newPosts++;
|
||||
const createdFile = await this.createMarkdownFile(post);
|
||||
if (createdFile) {
|
||||
result.newPosts++;
|
||||
}
|
||||
} catch {
|
||||
result.errors++;
|
||||
}
|
||||
|
|
@ -198,4 +204,4 @@ export class BlogService {
|
|||
updateSettings(newSettings: NaverBlogSettings) {
|
||||
this.settings = newSettings;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -598,12 +598,15 @@ export class NaverBlogImportModal extends Modal {
|
|||
excerpt: post.content.substring(0, 150) + '...'
|
||||
});
|
||||
|
||||
if (!createdFile) {
|
||||
new Notice(`Already imported: "${post.title}"`, NOTICE_TIMEOUTS.medium);
|
||||
return;
|
||||
}
|
||||
|
||||
new Notice(`✅ Imported: "${post.title}"`, NOTICE_TIMEOUTS.medium);
|
||||
|
||||
// Open the created file
|
||||
if (createdFile) {
|
||||
await this.openFile(createdFile);
|
||||
}
|
||||
await this.openFile(createdFile);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
new Notice(`❌ Import failed: ${message}`, NOTICE_TIMEOUTS.medium);
|
||||
|
|
@ -658,6 +661,7 @@ export class NaverBlogImportModal extends Modal {
|
|||
let successCount = 0;
|
||||
let errorCount = 0;
|
||||
let errorLogCount = 0;
|
||||
let skippedCount = 0;
|
||||
let lastCreatedFile: TFile | null = null;
|
||||
const totalPosts = posts.length;
|
||||
|
||||
|
|
@ -674,14 +678,16 @@ export class NaverBlogImportModal extends Modal {
|
|||
|
||||
if (createdFile) {
|
||||
lastCreatedFile = createdFile;
|
||||
}
|
||||
|
||||
if (isErrorPost) {
|
||||
errorLogCount++;
|
||||
if (isErrorPost) {
|
||||
errorLogCount++;
|
||||
} else {
|
||||
successCount++;
|
||||
}
|
||||
} else {
|
||||
successCount++;
|
||||
skippedCount++;
|
||||
}
|
||||
} catch {
|
||||
} catch (error) {
|
||||
console.error(`Failed to create file for ${post.logNo}:`, error);
|
||||
// Failed to create markdown file for this post
|
||||
errorCount++;
|
||||
}
|
||||
|
|
@ -695,9 +701,10 @@ export class NaverBlogImportModal extends Modal {
|
|||
`Import complete: ${successCount} successful`;
|
||||
|
||||
if (errorLogCount > 0) summary += `, ${errorLogCount} error logs`;
|
||||
if (skippedCount > 0) summary += `, ${skippedCount} skipped`;
|
||||
if (errorCount > 0) summary += `, ${errorCount} errors`;
|
||||
|
||||
const processed = successCount + errorLogCount + errorCount;
|
||||
const processed = successCount + errorLogCount + skippedCount + errorCount;
|
||||
summary += ` (${processed}/${totalPosts})`;
|
||||
|
||||
if (!importCancelled && errorCount === 0) summary += ' ✅';
|
||||
|
|
|
|||
|
|
@ -551,12 +551,20 @@ export class NaverBlogSettingTab extends PluginSettingTab {
|
|||
const posts = await this.plugin.fetchNaverBlogPosts(blogId, currentCount);
|
||||
|
||||
let successCount = 0;
|
||||
let skippedCount = 0;
|
||||
let errorCount = 0;
|
||||
for (const post of posts) {
|
||||
try {
|
||||
await this.plugin.createMarkdownFile(post);
|
||||
successCount++;
|
||||
} catch {
|
||||
const createdFile = await this.plugin.createMarkdownFile(post);
|
||||
if (createdFile) {
|
||||
successCount++;
|
||||
} else {
|
||||
skippedCount++;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to sync post ${post.logNo}:`, error);
|
||||
// Skip failed post
|
||||
errorCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -570,7 +578,10 @@ export class NaverBlogSettingTab extends PluginSettingTab {
|
|||
// Refresh display to show updated metadata
|
||||
this.displaySubscriptions(containerEl);
|
||||
|
||||
new Notice(`Synced ${successCount} posts from ${subscription?.blogName || blogId}`);
|
||||
let summary = `Synced ${successCount} posts from ${subscription?.blogName || blogId}`;
|
||||
if (skippedCount > 0) summary += `, ${skippedCount} skipped`;
|
||||
if (errorCount > 0) summary += `, ${errorCount} errors`;
|
||||
new Notice(summary);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
new Notice(`Failed to sync ${blogId}: ${message}`);
|
||||
|
|
@ -955,4 +966,4 @@ export class NaverBlogSettingTab extends PluginSettingTab {
|
|||
hideDropdown() {
|
||||
// This will be overridden in setupFolderDropdown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,5 +35,6 @@
|
|||
"1.9.0": "0.15.0",
|
||||
"1.10.0": "0.15.0",
|
||||
"1.10.1": "0.15.0",
|
||||
"1.10.2": "1.8.7"
|
||||
"1.10.2": "1.8.7",
|
||||
"1.10.3": "1.8.7"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue