diff --git a/packages/preload/interactors/entity-interactor.ts b/packages/preload/interactors/entity-interactor.ts index b3e405bb..ec4ce91a 100644 --- a/packages/preload/interactors/entity-interactor.ts +++ b/packages/preload/interactors/entity-interactor.ts @@ -267,7 +267,7 @@ export class EntityInteractor { const updatePromise = async (entityDrafts: PaperEntityDraft[]) => { const movedEntityDrafts = await Promise.all( entityDrafts.map((entityDraft: PaperEntityDraft) => - this.fileRepository.move(entityDraft) + this.fileRepository.move(entityDraft, true) ) ); diff --git a/packages/preload/repositories/exporter-repository/exporters/bib-exporter.ts b/packages/preload/repositories/exporter-repository/exporters/bib-exporter.ts index e8a00cfd..b7d11f31 100644 --- a/packages/preload/repositories/exporter-repository/exporters/bib-exporter.ts +++ b/packages/preload/repositories/exporter-repository/exporters/bib-exporter.ts @@ -13,7 +13,12 @@ export class BibExporter extends Exporter { for (const entity of entityDrafts) { let citeKey = ""; - const nameArray = entity.authors.split(", ")[0].split(" "); + let nameArray; + if (entity.authors.includes(";")) { + nameArray = entity.authors.split(";")[0].split(" "); + } else { + nameArray = entity.authors.split(", ")[0].split(" "); + } const lastName = nameArray[nameArray.length - 1]; citeKey += lastName.toLowerCase(); citeKey += entity.pubTime; diff --git a/packages/preload/repositories/file-repository/backends/backend.ts b/packages/preload/repositories/file-repository/backends/backend.ts index f4f353d3..f38fdc50 100644 --- a/packages/preload/repositories/file-repository/backends/backend.ts +++ b/packages/preload/repositories/file-repository/backends/backend.ts @@ -8,7 +8,10 @@ export interface FileBackend { check(): void; access(url: string, download: boolean): Promise; - move(entity: PaperEntityDraft): Promise; + move( + entity: PaperEntityDraft, + fourceDelete: boolean + ): Promise; remove(entity: PaperEntityDraft): Promise; removeFile(url: string): Promise; } diff --git a/packages/preload/repositories/file-repository/backends/local-backend.ts b/packages/preload/repositories/file-repository/backends/local-backend.ts index 4d544712..b513b69e 100644 --- a/packages/preload/repositories/file-repository/backends/local-backend.ts +++ b/packages/preload/repositories/file-repository/backends/local-backend.ts @@ -44,7 +44,11 @@ export class LocalFileBackend implements FileBackend { } } - async _move(sourceURL: string, targetURL: string): Promise { + async _move( + sourceURL: string, + targetURL: string, + forceDelete: boolean = false + ): Promise { const _sourceURL = sourceURL.replace("file://", ""); const _targetURL = targetURL.replace("file://", ""); const stat = await fsPromise.lstat(_sourceURL); @@ -54,8 +58,9 @@ export class LocalFileBackend implements FileBackend { try { await fsPromise.copyFile(_sourceURL, _targetURL); if ( - (this.preference.get("deleteSourceFile") as boolean) && - _sourceURL !== _targetURL + ((this.preference.get("deleteSourceFile") as boolean) && + _sourceURL !== _targetURL) || + forceDelete ) { await fsPromise.unlink(sourceURL); } @@ -69,8 +74,12 @@ export class LocalFileBackend implements FileBackend { } } - async move(entity: PaperEntityDraft): Promise { + async move( + entity: PaperEntityDraft, + forceDelete: boolean = false + ): Promise { let title = entity.title.replace(/[^a-zA-Z0-9 ]/g, "").replace(/\s/g, "_"); + let id = entity._id.toString(); if (this.preference.get("renamingFormat") === "short") { title = title .split("_") @@ -83,9 +92,16 @@ export class LocalFileBackend implements FileBackend { }) .filter((c: string) => c && c === c.toUpperCase()) .join(""); + } else if (this.preference.get("renamingFormat") === "authortitle") { + let author = entity.authors.split(",")[0]; + if (author !== entity.authors) { + author = `${author} et al`; + } + title = `${author} - ${title.slice(0, 20)}`; + id = id.slice(-5, -1); } - const targetFileName = title + "_" + entity._id.toString(); + const targetFileName = title + "_" + id; // 1. Move main file. const sourceMainURL = constructFileURL( @@ -100,7 +116,11 @@ export class LocalFileBackend implements FileBackend { false, this.preference.get("appLibFolder") as string ); - const mainSuccess = await this._move(sourceMainURL, targetMainURL); + const mainSuccess = await this._move( + sourceMainURL, + targetMainURL, + forceDelete + ); if (mainSuccess) { entity.mainURL = path.basename(targetMainURL); } else { @@ -122,7 +142,11 @@ export class LocalFileBackend implements FileBackend { sourceSupURL: string, targetSupURL: string ) => { - const supSuccess = await this._move(sourceSupURL, targetSupURL); + const supSuccess = await this._move( + sourceSupURL, + targetSupURL, + forceDelete + ); if (supSuccess) { return path.basename(targetSupURL); } else { diff --git a/packages/preload/repositories/file-repository/backends/webdav-backend.ts b/packages/preload/repositories/file-repository/backends/webdav-backend.ts index efd25807..9b177e6d 100644 --- a/packages/preload/repositories/file-repository/backends/webdav-backend.ts +++ b/packages/preload/repositories/file-repository/backends/webdav-backend.ts @@ -189,19 +189,26 @@ export class WebDavFileBackend implements FileBackend { async _move( sourceURL: string, targetURL: string, - targetCacheURL: string + targetCacheURL: string, + forceDelete: boolean = false ): Promise { try { let success; if (sourceURL.startsWith("file://")) { success = await this._local2localMove(sourceURL, targetCacheURL); success = await this._local2serverMove(sourceURL, targetURL); - if (this.preference.get("deleteSourceFile") as boolean) { + if ( + (this.preference.get("deleteSourceFile") as boolean) || + forceDelete + ) { await fsPromise.unlink(sourceURL); } } else if (sourceURL.startsWith("webdav://")) { success = await this._server2serverMove(sourceURL, targetURL); - if (this.preference.get("deleteSourceFile") as boolean) { + if ( + (this.preference.get("deleteSourceFile") as boolean) || + forceDelete + ) { await this.webdavClient?.deleteFile( sourceURL.replace("webdav://", "/paperlib/") ); @@ -219,9 +226,13 @@ export class WebDavFileBackend implements FileBackend { } } - async move(entity: PaperEntityDraft): Promise { + async move( + entity: PaperEntityDraft, + forceDelete: boolean = false + ): Promise { await this.check(); let title = entity.title.replace(/[^a-zA-Z0-9 ]/g, "").replace(/\s/g, "_"); + let id = entity._id.toString(); if (this.preference.get("renamingFormat") === "short") { title = title .split("_") @@ -234,9 +245,16 @@ export class WebDavFileBackend implements FileBackend { }) .filter((c: string) => c && c === c.toUpperCase()) .join(""); + } else if (this.preference.get("renamingFormat") === "authortitle") { + let author = entity.authors.split(",")[0]; + if (author !== entity.authors) { + author = `${author} et al`; + } + title = `${author} - ${title.slice(0, 20)}`; + id = id.slice(-5, -1); } - const targetFileName = title + "_" + entity._id.toString(); + const targetFileName = title + "_" + id; // 1. Move main file. let sourceMainURL; @@ -268,7 +286,8 @@ export class WebDavFileBackend implements FileBackend { const mainSuccess = await this._move( sourceMainURL, targetMainURL, - targetMainCacheURL + targetMainCacheURL, + forceDelete ); if (mainSuccess) { entity.mainURL = path.basename(targetMainURL); @@ -296,7 +315,8 @@ export class WebDavFileBackend implements FileBackend { const supSuccess = await this._move( sourceSupURL, targetSupURL, - targetSupCacheURL + targetSupCacheURL, + forceDelete ); if (supSuccess) { return path.basename(targetSupURL); diff --git a/packages/preload/repositories/file-repository/file-repository.ts b/packages/preload/repositories/file-repository/file-repository.ts index c6995b33..f7f48e83 100644 --- a/packages/preload/repositories/file-repository/file-repository.ts +++ b/packages/preload/repositories/file-repository/file-repository.ts @@ -31,8 +31,11 @@ export class FileRepository { async access(url: string, download: boolean): Promise { return await this.backend.access(url, download); } - async move(entity: PaperEntityDraft): Promise { - return await this.backend.move(entity); + async move( + entity: PaperEntityDraft, + fourceDelete = false + ): Promise { + return await this.backend.move(entity, fourceDelete); } async remove(entity: PaperEntityDraft): Promise { return await this.backend.remove(entity); diff --git a/packages/preload/repositories/scraper-repository/scrapers/cvf.ts b/packages/preload/repositories/scraper-repository/scrapers/cvf.ts index c74060b7..6c8dd70e 100644 --- a/packages/preload/repositories/scraper-repository/scrapers/cvf.ts +++ b/packages/preload/repositories/scraper-repository/scrapers/cvf.ts @@ -40,6 +40,7 @@ export class CVFScraper extends Scraper { type: string; ENTRYTYPE: string; pages: string; + author: string; }; if (typeof response.year !== "undefined") { const pubTime = response.year; @@ -62,6 +63,17 @@ export class CVFScraper extends Scraper { entityDraft.setValue("pubType", pubType); entityDraft.setValue("publication", publication); entityDraft.setValue("pages", response.pages || ""); + + if (response.author) { + const authorList = response.author.split("and").map((author) => { + const first_last = author + .trim() + .split(",") + .map((v) => v.trim()); + return `${first_last[1]} ${first_last[0]}`; + }); + entityDraft.setValue("authors", authorList.join(", ")); + } } return entityDraft; } diff --git a/packages/preload/repositories/scraper-repository/scrapers/pdf.ts b/packages/preload/repositories/scraper-repository/scrapers/pdf.ts index b54bfd1a..e8883777 100644 --- a/packages/preload/repositories/scraper-repository/scrapers/pdf.ts +++ b/packages/preload/repositories/scraper-repository/scrapers/pdf.ts @@ -69,7 +69,17 @@ export class PDFScraper extends Scraper { const firstPageText = rawResponse.firstPageText; entityDraft.setValue("title", metaData.info.Title); - entityDraft.setValue("authors", metaData.info.Author); + let authors; + if (metaData.info.Author.includes(";")) { + authors = metaData.info.Author.split(";") + .map((author) => { + return author.trim(); + }) + .join(", "); + } else { + authors = metaData.info.Author; + } + entityDraft.setValue("authors", authors); // Extract arXiv ID const arxivIds = firstPageText.match( diff --git a/packages/preload/utils/preference.ts b/packages/preload/utils/preference.ts index 24b13f3e..46c88fe1 100644 --- a/packages/preload/utils/preference.ts +++ b/packages/preload/utils/preference.ts @@ -11,7 +11,7 @@ export interface PreferenceStore { invertColor: boolean; sidebarSortBy: "name" | "count" | "color"; sidebarSortOrder: "asce" | "desc"; - renamingFormat: "full" | "short"; + renamingFormat: "full" | "short" | "authortitle"; enableExportReplacement: boolean; exportReplacement: Array<{ from: string; to: string }>; diff --git a/packages/renderer/src/ui/preference-view/general-view.vue b/packages/renderer/src/ui/preference-view/general-view.vue index 490a41c7..5249ea30 100644 --- a/packages/renderer/src/ui/preference-view/general-view.vue +++ b/packages/renderer/src/ui/preference-view/general-view.vue @@ -56,9 +56,9 @@ const onThemeUpdate = (value: string) => {