From 342923f50fc2c5a4ab76c6ae4fb99f5795080d51 Mon Sep 17 00:00:00 2001 From: AuroraHuang22 Date: Tue, 31 Oct 2023 14:57:42 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=A8=20Get=20epub=20metadata=20&=20cov?= =?UTF-8?q?erInfo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/IscnRegisterForm.vue | 21 ++++++ components/IscnUploadForm.vue | 97 +++++++++++++++++++++++- package.json | 1 + pages/new/index.vue | 7 ++ yarn.lock | 129 +++++++++++++++++++++++++++++++- 5 files changed, 249 insertions(+), 6 deletions(-) diff --git a/components/IscnRegisterForm.vue b/components/IscnRegisterForm.vue index e707cfdb..fd8bb6e5 100644 --- a/components/IscnRegisterForm.vue +++ b/components/IscnRegisterForm.vue @@ -740,6 +740,7 @@ export enum AuthorDialogType { export default class IscnRegisterForm extends Vue { @Prop({ default: [] }) readonly fileRecords!: any[] @Prop({ default: [] }) readonly uploadArweaveList!: any[] + @Prop() readonly epubMetadata!: any | null @Prop(String) readonly ipfsHash!: string @Prop(String) readonly arweaveId!: string @@ -835,6 +836,7 @@ export default class IscnRegisterForm extends Vue { currentAuthorDialogType: AuthorDialogType = AuthorDialogType.stakeholder sameAsList: any = [] + language: string = '' get ipfsHashList() { const list = [] @@ -1080,6 +1082,16 @@ export default class IscnRegisterForm extends Vue { if (this.arweaveId){ this.uploadArweaveIdList.push(this.arweaveId) } + + if (this.epubMetadata) { + console.log('epubMetadata', this.epubMetadata) + this.name = this.epubMetadata.title; + this.description = this.extractText(this.epubMetadata.description); + this.author.name = this.epubMetadata.author; + this.language = this.epubMetadata.language + if (this.author.name) { this.authors.push(this.author) } + } + this.uploadStatus = 'loading' // ISCN Fee needs Arweave fee to calculate await this.calculateISCNFee() @@ -1425,5 +1437,14 @@ export default class IscnRegisterForm extends Vue { this.displayImageSrc = this.fileRecords[index].fileData this.displayExifInfo = this.fileRecords[index].exifInfo } + + // eslint-disable-next-line class-methods-use-this + extractText(htmlString: string) { + if (!htmlString) return '' + const div = document.createElement('div'); + div.innerHTML = htmlString; + div.innerHTML = div.innerHTML.replace(//gi, "\n"); + return div.textContent || div.innerText; + } } diff --git a/components/IscnUploadForm.vue b/components/IscnUploadForm.vue index bf81a94b..8e89d4b7 100644 --- a/components/IscnUploadForm.vue +++ b/components/IscnUploadForm.vue @@ -260,6 +260,7 @@ import { namespace } from 'vuex-class' import exifr from 'exifr' import Hash from 'ipfs-only-hash' import BigNumber from 'bignumber.js' +import ePub from 'epubjs'; import { OfflineSigner } from '@cosmjs/proto-signing' @@ -314,8 +315,6 @@ export default class UploadForm extends Vue { string, { transactionHash?: string, arweaveId?: string } >() - - likerId: string = '' likerIdsAddresses: (string | void)[] = [] error: string = '' @@ -325,6 +324,8 @@ export default class UploadForm extends Vue { signDialogError = '' balance = new BigNumber(0) + epubMetadataList: any[] = [] + get formClasses() { return [ 'flex', @@ -412,6 +413,10 @@ export default class UploadForm extends Vue { } } + mounted() { + this.epubMetadataList = [] + } + async onFileUpload(event: DragEvent) { logTrackerEvent(this, 'ISCNCreate', 'SelectFile', '', 1) this.isSizeExceeded = false @@ -478,6 +483,10 @@ export default class UploadForm extends Vue { console.error(err) } } + if (file.type === 'application/epub+zip') { + // eslint-disable-next-line no-await-in-loop + await this.processEPub({ buffer:fileBytes }) + } } } else { this.isSizeExceeded = true @@ -487,6 +496,84 @@ export default class UploadForm extends Vue { } } + // eslint-disable-next-line class-methods-use-this + async processEPub({ buffer }: { buffer: ArrayBuffer }) { + try { + const Book = ePub(buffer); + await Book.ready; + const epubMetadata: any = {}; + + // Get metadata + const { metadata } = Book.packaging; + if (metadata) { + epubMetadata.title = metadata.title; + epubMetadata.author = metadata.creator; + epubMetadata.language = metadata.language; + epubMetadata.description = metadata.description; + } + + // Get tags + const opfFilePath = await (Book.path as any).path; + const opfContent = await Book.archive.getText(opfFilePath); + const parser = new DOMParser(); + const opfDocument = parser.parseFromString(opfContent, 'application/xml'); + const dcSubjectElements = opfDocument.querySelectorAll('dc\\:subject, subject'); + const subjects: string[] = []; + dcSubjectElements.forEach((element) => { + const subject = element.textContent; + subject && subjects.push(subject); + }); + epubMetadata.tags = subjects; + + // Get cover file + const coverUrl = (Book as any).cover; + if (!coverUrl) { + this.epubMetadataList.push(epubMetadata); + return; + } + + const blobData = await Book.archive.getBlob(coverUrl); + if (blobData) { + const coverFile = new File([blobData], `${metadata.title}_cover.jpeg`, { + type: 'image/jpeg', + }); + + const fileBytes = (await fileToArrayBuffer(coverFile)) as unknown as ArrayBuffer; + if (fileBytes) { + const [ + fileSHA256, + imageType, + ipfsHash, + // eslint-disable-next-line no-await-in-loop + ] = await Promise.all([ + digestFileSHA256(fileBytes), + readImageType(fileBytes), + Hash.of(Buffer.from(fileBytes)), + ]); + + const fileRecord: any = { + fileName: coverFile.name, + fileSize: coverFile.size, + fileType: coverFile.type, + fileBlob: coverFile, + fileData: coverFile, + ipfsHash, + fileSHA256, + isFileImage: !!imageType, + }; + + epubMetadata.fileSHA256 = fileSHA256; + this.epubMetadataList.push(epubMetadata); + this.fileRecords.push(fileRecord); + } + } else { + this.epubMetadataList.push(epubMetadata); + } + } catch (err) { + console.error(err); + } +} + onEnterURL() { if ( !( @@ -621,6 +708,10 @@ export default class UploadForm extends Vue { if (arweaveId) { const uploadedData = this.sentArweaveTransactionHashes.get(records.ipfsHash) || {}; this.sentArweaveTransactionHashes.set(records.ipfsHash, { ...uploadedData, arweaveId }); + if (tempRecord.fileName === 'cover.jpeg') { + const metadata = this.epubMetadataList.find((file: any) => file.ipfsHash === records.ipfsHash) + metadata.arweaveId = arweaveId + } this.$emit('arweaveUploaded', { arweaveId }) this.isOpenSignDialog = false } else { @@ -675,7 +766,7 @@ export default class UploadForm extends Vue { } const uploadArweaveIdList = Array.from(this.sentArweaveTransactionHashes.values()).map(entry => entry.arweaveId); - this.$emit('submit', { fileRecords: this.fileRecords, arweaveIds: uploadArweaveIdList }) + this.$emit('submit', { fileRecords: this.fileRecords, arweaveIds: uploadArweaveIdList, epubMetadata: this.epubMetadataList[0] }) } handleSignDialogClose() { diff --git a/package.json b/package.json index 6fc46c55..0229f7a3 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "core-js": "^3.15.1", "cosmjs-types": "^0.8.0", "easyqrcodejs": "^4.4.6", + "epubjs": "^0.3.93", "exifr": "^7.1.2", "express": "^4.17.1", "fast-json-stable-stringify": "^2.1.0", diff --git a/pages/new/index.vue b/pages/new/index.vue index c72946b7..8cb5fb1f 100644 --- a/pages/new/index.vue +++ b/pages/new/index.vue @@ -42,6 +42,7 @@ :ipfs-hash="urlIpfsHash" :arweave-id="urlArweaveId" :upload-arweave-list=uploadArweaveList + :epub-metadata="epubMetadata" :step="step" @txBroadcasted="onISCNTxInfo" @@ -135,6 +136,7 @@ export default class NewIndexPage extends Vue { uploadFileRecords: any[] = [] urlFileRecords: any[] = [] + epubMetadata: any | null = null get shouldSkipToMintNFT(): boolean { return this.$route.query.mint === '1' @@ -190,9 +192,11 @@ export default class NewIndexPage extends Vue { onSubmitUpload({ fileRecords, arweaveIds, + epubMetadata, }: { fileRecords: any[] | [] arweaveIds: any[] | [] + epubMetadata: any }) { if (fileRecords && fileRecords.length) { this.uploadFileRecords = [...fileRecords] @@ -200,6 +204,9 @@ export default class NewIndexPage extends Vue { if (arweaveIds && arweaveIds.length) { this.uploadArweaveList = [...arweaveIds] } + if (epubMetadata) { + this.epubMetadata = {...epubMetadata} + } this.state = 'iscn' logTrackerEvent(this, 'ISCNCreate', 'ISCNConfirmFile', '', 1) } diff --git a/yarn.lock b/yarn.lock index d592e15a..59ec1d7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4970,6 +4970,13 @@ resolved "https://registry.yarnpkg.com/@types/less/-/less-3.0.2.tgz#2761d477678c8374cb9897666871662eb1d1115e" integrity sha512-62vfe65cMSzYaWmpmhqCMMNl0khen89w57mByPi1OseGfcV/LV03fO8YVrNj7rFQsRWNJo650WWyh6m7p8vZmA== +"@types/localforage@0.0.34": + version "0.0.34" + resolved "https://registry.yarnpkg.com/@types/localforage/-/localforage-0.0.34.tgz#5e31c32dd8791ec4b9ff3ef47c9cb55b2d0d9438" + integrity sha512-tJxahnjm9dEI1X+hQSC5f2BSd/coZaqbIl1m3TCl0q9SVuC52XcXfV0XmoCU1+PmjyucuVITwoTnN8OlTbEXXA== + dependencies: + localforage "*" + "@types/lodash.chunk@^4.2.6": version "4.2.6" resolved "https://registry.yarnpkg.com/@types/lodash.chunk/-/lodash.chunk-4.2.6.tgz#9d35f05360b0298715d7f3d9efb34dd4f77e5d2a" @@ -6226,6 +6233,11 @@ "@webassemblyjs/wast-parser" "1.9.0" "@xtuc/long" "4.2.2" +"@xmldom/xmldom@^0.7.5": + version "0.7.13" + resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.7.13.tgz#ff34942667a4e19a9f4a0996a76814daac364cf3" + integrity sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g== + "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -8623,6 +8635,11 @@ core-js@^3.15.1: resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.1.tgz#6c08ab88abdf56545045ccf5fd81f47f407e7f1a" integrity sha512-h8VbZYnc9pDzueiS2610IULDkpFFPunHwIpl8yRwFahAEEdSpHlTy3h3z3rKq5h11CaUdBEeRViu9AYvbxiMeg== +core-js@^3.18.3: + version "3.33.2" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.33.2.tgz#312bbf6996a3a517c04c99b9909cdd27138d1ceb" + integrity sha512-XeBzWI6QL3nJQiHmdzbAOiMYqjrb7hwU7A39Qhvd/POSa/t9E1AeZyEZx3fNvp/vtM8zXwhoL0FsiS0hD0pruQ== + core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -9179,6 +9196,14 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + data-urls@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" @@ -9885,6 +9910,21 @@ entities@^4.2.0, entities@^4.3.0: resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.0.tgz#62915f08d67353bb4eb67e3d62641a4059aec656" integrity sha512-/iP1rZrSEJ0DTlPiX+jbzlA3eVkY/e8L8SozroF395fIqE3TYF/Nz7YOMAawta+vLmyJ/hkGNNPcSbMADCCXbg== +epubjs@^0.3.93: + version "0.3.93" + resolved "https://registry.yarnpkg.com/epubjs/-/epubjs-0.3.93.tgz#100c4597db152fc07d5246be38acca928b6b0b22" + integrity sha512-c06pNSdBxcXv3dZSbXAVLE1/pmleRhOT6mXNZo6INKmvuKpYB65MwU/lO7830czCtjIiK9i+KR+3S+p0wtljrw== + dependencies: + "@types/localforage" "0.0.34" + "@xmldom/xmldom" "^0.7.5" + core-js "^3.18.3" + event-emitter "^0.3.5" + jszip "^3.7.1" + localforage "^1.10.0" + lodash "^4.17.21" + marks-pane "^1.0.9" + path-webpack "0.0.3" + err-code@^3.0.0, err-code@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/err-code/-/err-code-3.0.1.tgz#a444c7b992705f2b120ee320b09972eef331c920" @@ -9984,11 +10024,29 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@~0.10.14: + version "0.10.62" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" + integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + next-tick "^1.1.0" + es5-shim@^4.5.13: version "4.5.15" resolved "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.5.15.tgz#6a26869b261854a3b045273f5583c52d390217fe" integrity sha512-FYpuxEjMeDvU4rulKqFdukQyZSTpzhg4ScQHrAosrlVpR6GFyaw14f74yn2+4BugniIS0Frpg7TvwZocU4ZMTw== +es6-iterator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + es6-promise@^4.0.3: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" @@ -10006,6 +10064,14 @@ es6-shim@^0.35.5: resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.6.tgz#d10578301a83af2de58b9eadb7c2c9945f7388a0" integrity sha512-EmTr31wppcaIAgblChZiuN/l9Y7DPyw8Xtbg7fIVngn6zMW+IEBJDJngeKC3x6wr0V/vcA2wqeFnaw1bFJbDdA== +es6-symbol@^3.1.1, es6-symbol@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + escalade@^3.0.2, escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -10344,6 +10410,14 @@ etag@^1.8.1, etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== + dependencies: + d "1" + es5-ext "~0.10.14" + eventemitter3@^4.0.0, eventemitter3@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -10488,6 +10562,13 @@ express@^4.17.1: utils-merge "1.0.1" vary "~1.1.2" +ext@^1.1.2: + version "1.7.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== + dependencies: + type "^2.7.2" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -13693,6 +13774,16 @@ jstransformer@1.0.0: is-promise "^2.0.0" promise "^7.0.1" +jszip@^3.7.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + setimmediate "^1.0.5" + junk@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" @@ -13941,6 +14032,13 @@ lie@3.1.1: dependencies: immediate "~3.0.5" +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + lilconfig@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.3.tgz#68f3005e921dafbd2a2afb48379986aa6d2579fd" @@ -14014,7 +14112,7 @@ loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4 emojis-list "^3.0.0" json5 "^1.0.1" -localforage@^1.8.1: +localforage@*, localforage@^1.10.0, localforage@^1.8.1: version "1.10.0" resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4" integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg== @@ -14321,6 +14419,11 @@ markdown-to-jsx@^7.1.3: resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-7.1.3.tgz#f00bae66c0abe7dd2d274123f84cb6bd2a2c7c6a" integrity sha512-jtQ6VyT7rMT5tPV0g2EJakEnXLiPksnvlYtwQsVVZ611JsWGN8bQ1tVSDX4s6JllfEH6wmsYxNjTUAMrPmNA8w== +marks-pane@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/marks-pane/-/marks-pane-1.0.9.tgz#c0b5ab813384d8cd81faaeb3bbf3397dc809c1b3" + integrity sha512-Ahs4oeG90tbdPWwAJkAAoHg2lRR8lAs9mZXETNPO9hYg3AkjUJBKi1NQ4aaIQZVGrig7c/3NUV1jANl8rFTeMg== + mathml-tag-names@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" @@ -14986,6 +15089,11 @@ nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0: resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61" integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug== +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -15638,7 +15746,7 @@ p5@^1.4.0: resolved "https://registry.yarnpkg.com/p5/-/p5-1.4.0.tgz#d4d0f001c297525831861af5e017cdb5ef517a75" integrity sha512-U888W2ChcIzPhRhnv4FkNhaa4f5BDIWZfLhzvx9ZrQ5KtkZr/+o1UPIicV3yWTRy0HEG23NviHyDR3kgjaJ9wA== -pako@1.0.11, pako@~1.0.5: +pako@1.0.11, pako@~1.0.2, pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== @@ -15850,6 +15958,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +path-webpack@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/path-webpack/-/path-webpack-0.0.3.tgz#ff6dec749eec5a94605c04d5f63fc55607a03a16" + integrity sha512-AmeDxedoo5svf7aB3FYqSAKqMxys014lVKBzy1o/5vv9CtU7U4wgGWL1dA2o6MOzcD53ScN4Jmiq6VbtLz1vIQ== + pbkdf2@^3.0.16, pbkdf2@^3.0.3, pbkdf2@^3.0.9, pbkdf2@^3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" @@ -18601,7 +18714,7 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" -setimmediate@^1.0.4: +setimmediate@^1.0.4, setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= @@ -20086,6 +20199,16 @@ type-tagger@^1.0.0: resolved "https://registry.yarnpkg.com/type-tagger/-/type-tagger-1.0.0.tgz#dc6297e52e17097c1b92b42c16816a18f631e7f4" integrity sha512-FIPqqpmDgdaulCnRoKv1/d3U4xVBUrYn42QXWNP3XYmgfPUDuBUsgFOb9ntT0aIe0UsUP+lknpQ5d9Kn36RssA== +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.7.2: + version "2.7.2" + resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" + integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== + typedarray-to-buffer@3.1.5, typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" From 42086c0d65cb07ef59b452ec68b91140c5864714 Mon Sep 17 00:00:00 2001 From: AuroraHuang22 Date: Wed, 1 Nov 2023 16:11:49 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E2=9C=A8=20Add=20epubMetadata=20to=20thumb?= =?UTF-8?q?nail=20field?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/IscnRegisterForm.vue | 4 +- components/IscnUploadForm.vue | 189 +++++++++++++++++++------------- utils/cosmos/iscn/iscn.type.ts | 2 + 3 files changed, 119 insertions(+), 76 deletions(-) diff --git a/components/IscnRegisterForm.vue b/components/IscnRegisterForm.vue index fd8bb6e5..d818b144 100644 --- a/components/IscnRegisterForm.vue +++ b/components/IscnRegisterForm.vue @@ -1004,6 +1004,8 @@ export default class IscnRegisterForm extends Vue { likerIdsAddresses: this.likerIdsAddresses, authorDescriptions: this.authorDescriptions, contentFingerprints: this.customContentFingerprints, + inLanguage: this.language, + thumbnail: this.epubMetadata.thumbnail, } } @@ -1084,11 +1086,11 @@ export default class IscnRegisterForm extends Vue { } if (this.epubMetadata) { - console.log('epubMetadata', this.epubMetadata) this.name = this.epubMetadata.title; this.description = this.extractText(this.epubMetadata.description); this.author.name = this.epubMetadata.author; this.language = this.epubMetadata.language + this.tags = this.epubMetadata.tags if (this.author.name) { this.authors.push(this.author) } } diff --git a/components/IscnUploadForm.vue b/components/IscnUploadForm.vue index 8e89d4b7..bdc8c72e 100644 --- a/components/IscnUploadForm.vue +++ b/components/IscnUploadForm.vue @@ -264,7 +264,7 @@ import ePub from 'epubjs'; import { OfflineSigner } from '@cosmjs/proto-signing' -import { IS_CHAIN_UPGRADING, UPLOAD_FILESIZE_MAX } from '~/constant' +import { IS_CHAIN_UPGRADING, UPLOAD_FILESIZE_MAX, IPFS_VIEW_GATEWAY_URL } from '~/constant' import { logTrackerEvent } from '~/utils/logger' import { estimateBundlrFilePrice, uploadSingleFileToBundlr } from '~/utils/arweave/v2' import { @@ -485,7 +485,7 @@ export default class UploadForm extends Vue { } if (file.type === 'application/epub+zip') { // eslint-disable-next-line no-await-in-loop - await this.processEPub({ buffer:fileBytes }) + await this.processEPub({ buffer: fileBytes, file }) } } } else { @@ -496,83 +496,112 @@ export default class UploadForm extends Vue { } } - // eslint-disable-next-line class-methods-use-this - async processEPub({ buffer }: { buffer: ArrayBuffer }) { - try { - const Book = ePub(buffer); - await Book.ready; - const epubMetadata: any = {}; - - // Get metadata - const { metadata } = Book.packaging; - if (metadata) { - epubMetadata.title = metadata.title; - epubMetadata.author = metadata.creator; - epubMetadata.language = metadata.language; - epubMetadata.description = metadata.description; - } + async processEPub({ buffer, file }: { buffer: ArrayBuffer; file: File }) { + try { + const Book = ePub(buffer) + await Book.ready + const epubMetadata: any = {} + + // Get metadata + const { metadata } = Book.packaging + if (metadata) { + epubMetadata.epubFileName = file.name + epubMetadata.title = metadata.title + epubMetadata.author = metadata.creator + epubMetadata.language = this.formatLanguage(metadata.language) + epubMetadata.description = metadata.description + } - // Get tags - const opfFilePath = await (Book.path as any).path; - const opfContent = await Book.archive.getText(opfFilePath); - const parser = new DOMParser(); - const opfDocument = parser.parseFromString(opfContent, 'application/xml'); - const dcSubjectElements = opfDocument.querySelectorAll('dc\\:subject, subject'); - const subjects: string[] = []; - dcSubjectElements.forEach((element) => { - const subject = element.textContent; - subject && subjects.push(subject); - }); - epubMetadata.tags = subjects; + // Get tags + const opfFilePath = await (Book.path as any).path + const opfContent = await Book.archive.getText(opfFilePath) + const parser = new DOMParser() + const opfDocument = parser.parseFromString(opfContent, 'application/xml') + const dcSubjectElements = opfDocument.querySelectorAll( + 'dc\\:subject, subject', + ) + const subjects: string[] = [] + dcSubjectElements.forEach((element) => { + const subject = element.textContent + subject && subjects.push(subject) + }) + epubMetadata.tags = subjects + + // Get cover file + const coverUrl = (Book as any).cover + if (!coverUrl) { + this.epubMetadataList.push(epubMetadata) + return + } - // Get cover file - const coverUrl = (Book as any).cover; - if (!coverUrl) { - this.epubMetadataList.push(epubMetadata); - return; - } + const blobData = await Book.archive.getBlob(coverUrl) + if (blobData) { + const coverFile = new File([blobData], `${metadata.title}_cover.jpeg`, { + type: 'image/jpeg', + }) + + const fileBytes = (await fileToArrayBuffer( + coverFile, + )) as unknown as ArrayBuffer + if (fileBytes) { + const [ + fileSHA256, + imageType, + ipfsHash, + // eslint-disable-next-line no-await-in-loop + ] = await Promise.all([ + digestFileSHA256(fileBytes), + readImageType(fileBytes), + Hash.of(Buffer.from(fileBytes)), + ]) + + const fileRecord: any = { + fileName: coverFile.name, + fileSize: coverFile.size, + fileType: coverFile.type, + fileBlob: coverFile, + ipfsHash, + fileSHA256, + isFileImage: !!imageType, + } - const blobData = await Book.archive.getBlob(coverUrl); - if (blobData) { - const coverFile = new File([blobData], `${metadata.title}_cover.jpeg`, { - type: 'image/jpeg', - }); + epubMetadata.ipfsHash = ipfsHash + epubMetadata.thumbnail = { + "@type": "ImageObject", + url: `${IPFS_VIEW_GATEWAY_URL}${ipfsHash}`, + name: `${file.name}_cover`, + description: `${file.name}_cover`, + encodingFormat: "image/jpeg", + }; - const fileBytes = (await fileToArrayBuffer(coverFile)) as unknown as ArrayBuffer; - if (fileBytes) { - const [ - fileSHA256, - imageType, - ipfsHash, - // eslint-disable-next-line no-await-in-loop - ] = await Promise.all([ - digestFileSHA256(fileBytes), - readImageType(fileBytes), - Hash.of(Buffer.from(fileBytes)), - ]); - - const fileRecord: any = { - fileName: coverFile.name, - fileSize: coverFile.size, - fileType: coverFile.type, - fileBlob: coverFile, - fileData: coverFile, - ipfsHash, - fileSHA256, - isFileImage: !!imageType, - }; - - epubMetadata.fileSHA256 = fileSHA256; - this.epubMetadataList.push(epubMetadata); - this.fileRecords.push(fileRecord); + const reader = new FileReader() + reader.onload = (e) => { + if (!e.target) return + fileRecord.fileData = e.target.result as string + } + reader.readAsDataURL(coverFile) + this.epubMetadataList = [ + ...this.epubMetadataList, + epubMetadata, + ] + this.fileRecords.push(fileRecord) + } } - } else { - this.epubMetadataList.push(epubMetadata); + } catch (err) { + console.error(err) } - } catch (err) { - console.error(err); } -} + + // eslint-disable-next-line class-methods-use-this + formatLanguage(language: string) { + if (language && language.toLowerCase().startsWith('en')) { + return 'en' + } + if (language && language.toLowerCase().startsWith('zh')) { + return 'zh' + } + return language + } onEnterURL() { if ( @@ -595,7 +624,13 @@ export default class UploadForm extends Vue { } handleDeleteFile(index: number) { - this.fileRecords.splice(index, 1) + const deletedFile = this.fileRecords[index]; + this.fileRecords.splice(index, 1); + + const indexToDelete = this.epubMetadataList.findIndex(item => item.epubFileName === deletedFile.fileName); + if (indexToDelete !== -1) { + this.epubMetadataList.splice(indexToDelete, 1); + } } handleClickExifInfo(index: number) { @@ -633,6 +668,10 @@ export default class UploadForm extends Vue { } if (arweaveId) { this.sentArweaveTransactionHashes.set(ipfsHash, { transactionHash: '', arweaveId }); + const metadata = this.epubMetadataList.find((data: any) => data.ipfsHash === ipfsHash) + if (metadata) { + metadata.thumbnail.contentUrl = `https://arweave.net/${arweaveId}`; + } } if (!this.arweaveFeeTargetAddress) { this.arweaveFeeTargetAddress = address; @@ -709,8 +748,8 @@ export default class UploadForm extends Vue { const uploadedData = this.sentArweaveTransactionHashes.get(records.ipfsHash) || {}; this.sentArweaveTransactionHashes.set(records.ipfsHash, { ...uploadedData, arweaveId }); if (tempRecord.fileName === 'cover.jpeg') { - const metadata = this.epubMetadataList.find((file: any) => file.ipfsHash === records.ipfsHash) - metadata.arweaveId = arweaveId + const metadata = this.epubMetadataList.find((file: any) => file.ipfsHash === records.thumbnail.url) + metadata.thumbnail.contentUrl = `https://arweave.net/${arweaveId}` } this.$emit('arweaveUploaded', { arweaveId }) this.isOpenSignDialog = false diff --git a/utils/cosmos/iscn/iscn.type.ts b/utils/cosmos/iscn/iscn.type.ts index d450ead7..377bdffa 100644 --- a/utils/cosmos/iscn/iscn.type.ts +++ b/utils/cosmos/iscn/iscn.type.ts @@ -24,6 +24,8 @@ export interface ISCNRegisterPayload { stakeholders?: any[]; recordNotes?: string; memo?: string; + inLanguage?: string; + thumbnail?: any; } export interface ISCNRecordWithID extends ISCNRecord { id: string;