You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-06-15 23:00:36 +02:00
* Allow downloading resources automatically, on demand, or when loading note * Make needToBeFetched calls to return the right number of resources * All: Improved handling of resource downloading and decryption * Desktop: Click on resource to download it (and, optionally, to decrypt it) * Desktop: Better handling of resource state (not downloaded, downloading, encrypted) in front end * Renamed setting to sync.resourceDownloadMode * Download resources when changing setting * tweaks * removed duplicate cs * Better report resource download progress * Make sure resource cache is properly cleared when needed * Also handle manual download for non-image resources * More improvements to logic when downloading and decrypting resources
This commit is contained in:
@ -32,15 +32,14 @@ class Resource extends BaseItem {
|
||||
return imageMimeTypes.indexOf(type.toLowerCase()) >= 0;
|
||||
}
|
||||
|
||||
static needToBeFetched(limit = null) {
|
||||
let sql = 'SELECT * FROM resources WHERE id IN (SELECT resource_id FROM resource_local_states WHERE fetch_status = ?) ORDER BY updated_time DESC';
|
||||
if (limit !== null) sql += ' LIMIT ' + limit;
|
||||
return this.modelSelectAll(sql, [Resource.FETCH_STATUS_IDLE]);
|
||||
}
|
||||
|
||||
static async needToBeFetchedCount() {
|
||||
const r = await this.db().selectOne('SELECT count(*) as total FROM resource_local_states WHERE fetch_status = ?', [Resource.FETCH_STATUS_IDLE]);
|
||||
return r ? r['total'] : 0;
|
||||
static needToBeFetched(resourceDownloadMode = null, limit = null) {
|
||||
let sql = ['SELECT * FROM resources WHERE encryption_applied = 0 AND id IN (SELECT resource_id FROM resource_local_states WHERE fetch_status = ?)'];
|
||||
if (resourceDownloadMode !== 'always') {
|
||||
sql.push('AND resources.id IN (SELECT resource_id FROM resources_to_download)');
|
||||
}
|
||||
sql.push('ORDER BY updated_time DESC');
|
||||
if (limit !== null) sql.push('LIMIT ' + limit);
|
||||
return this.modelSelectAll(sql.join(' '), [Resource.FETCH_STATUS_IDLE]);
|
||||
}
|
||||
|
||||
static async resetStartedFetchStatus() {
|
||||
@ -52,13 +51,6 @@ class Resource extends BaseItem {
|
||||
return Resource.fsDriver_;
|
||||
}
|
||||
|
||||
static filename(resource, encryptedBlob = false) {
|
||||
let extension = encryptedBlob ? 'crypted' : resource.file_extension;
|
||||
if (!extension) extension = resource.mime ? mime.toFileExtension(resource.mime) : '';
|
||||
extension = extension ? ('.' + extension) : '';
|
||||
return resource.id + extension;
|
||||
}
|
||||
|
||||
static friendlyFilename(resource) {
|
||||
let output = safeFilename(resource.title); // Make sure not to allow spaces or any special characters as it's not supported in HTTP headers
|
||||
if (!output) output = resource.id;
|
||||
@ -76,6 +68,13 @@ class Resource extends BaseItem {
|
||||
return Setting.value('resourceDirName');
|
||||
}
|
||||
|
||||
static filename(resource, encryptedBlob = false) {
|
||||
let extension = encryptedBlob ? 'crypted' : resource.file_extension;
|
||||
if (!extension) extension = resource.mime ? mime.toFileExtension(resource.mime) : '';
|
||||
extension = extension ? ('.' + extension) : '';
|
||||
return resource.id + extension;
|
||||
}
|
||||
|
||||
static relativePath(resource, encryptedBlob = false) {
|
||||
return Setting.value('resourceDirName') + '/' + this.filename(resource, encryptedBlob);
|
||||
}
|
||||
@ -96,6 +95,13 @@ class Resource extends BaseItem {
|
||||
const decryptedItem = item.encryption_cipher_text ? await super.decrypt(item) : Object.assign({}, item);
|
||||
if (!decryptedItem.encryption_blob_encrypted) return decryptedItem;
|
||||
|
||||
const localState = await this.localState(item);
|
||||
if (localState.fetch_status !== Resource.FETCH_STATUS_DONE) {
|
||||
// Not an error - it means the blob has not been downloaded yet.
|
||||
// It will be decrypted later on, once downloaded.
|
||||
return decryptedItem;
|
||||
}
|
||||
|
||||
const plainTextPath = this.fullPath(decryptedItem);
|
||||
const encryptedPath = this.fullPath(decryptedItem, true);
|
||||
const noExtPath = pathUtils.dirname(encryptedPath) + '/' + pathUtils.filename(encryptedPath);
|
||||
@ -109,12 +115,7 @@ class Resource extends BaseItem {
|
||||
}
|
||||
|
||||
try {
|
||||
// const stat = await this.fsDriver().stat(encryptedPath);
|
||||
await this.encryptionService().decryptFile(encryptedPath, plainTextPath, {
|
||||
// onProgress: (progress) => {
|
||||
// console.info('Decryption: ', progress.doneSize / stat.size);
|
||||
// },
|
||||
});
|
||||
await this.encryptionService().decryptFile(encryptedPath, plainTextPath);
|
||||
} catch (error) {
|
||||
if (error.code === 'invalidIdentifier') {
|
||||
// As the identifier is invalid it most likely means that this is not encrypted data
|
||||
@ -149,12 +150,7 @@ class Resource extends BaseItem {
|
||||
if (resource.encryption_blob_encrypted) return { path: encryptedPath, resource: resource };
|
||||
|
||||
try {
|
||||
// const stat = await this.fsDriver().stat(plainTextPath);
|
||||
await this.encryptionService().encryptFile(plainTextPath, encryptedPath, {
|
||||
// onProgress: (progress) => {
|
||||
// console.info(progress.doneSize / stat.size);
|
||||
// },
|
||||
});
|
||||
await this.encryptionService().encryptFile(plainTextPath, encryptedPath);
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOENT') throw new JoplinError('File not found:' + error.toString(), 'fileNotFound');
|
||||
throw error;
|
||||
@ -244,6 +240,20 @@ class Resource extends BaseItem {
|
||||
await ResourceLocalState.batchDelete(ids);
|
||||
}
|
||||
|
||||
static async markForDownload(resourceId) {
|
||||
// Insert the row only if it's not already there
|
||||
const t = Date.now();
|
||||
await this.db().exec('INSERT INTO resources_to_download (resource_id, updated_time, created_time) SELECT ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM resources_to_download WHERE resource_id = ?)', [resourceId, t, t, resourceId]);
|
||||
}
|
||||
|
||||
static async downloadedButEncryptedBlobCount() {
|
||||
const r = await this.db().selectOne('SELECT count(*) as total FROM resource_local_states WHERE fetch_status = ? AND resource_id IN (SELECT id FROM resources WHERE encryption_blob_encrypted = 1)', [
|
||||
Resource.FETCH_STATUS_DONE,
|
||||
]);
|
||||
|
||||
return r ? r.total : 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Resource.IMAGE_MAX_DIMENSION = 1920;
|
||||
|
Reference in New Issue
Block a user