alice-asset-manager
Advanced tools
Comparing version 1.0.1 to 1.1.0
{ | ||
"name": "alice-asset-manager", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"description": "Node.js API for managing images and sounds in Alice skill", | ||
@@ -24,3 +24,3 @@ "author": { | ||
"test": "mocha test/setup.js test/specs/*.js --timeout=6000", | ||
"test:d": "npm run test -- --bail", | ||
"test:d": "DEBUG=alice-asset-manager npm run test -- --bail", | ||
"toc": "markdown-toc README.md -i", | ||
@@ -41,2 +41,3 @@ "release": "npm run lint && npm test && npm version $VER && npm publish && git push --follow-tags --no-verify", | ||
"dependencies": { | ||
"debug": "^4.1.1", | ||
"form-data": "^3.0.0", | ||
@@ -43,0 +44,0 @@ "glob": "^7.1.6", |
@@ -197,3 +197,4 @@ # alice-asset-manager | ||
* @param {function} [getLocalId] функция вычисления localId по имени файла | ||
* @param {boolean} [dryRun=false] запуск без зфактической загрузки файлов | ||
* @param {function} [transform] функция обработки файлов (buffer, filepath) => buffer | ||
* @param {boolean} [dryRun=false] запуск без фактической загрузки файлов | ||
* @returns {Promise} | ||
@@ -233,6 +234,4 @@ */ | ||
/* | ||
{ | ||
uploaded: [ 'images/my_image_1[alice].png', 'images/my_image_2[bob].png' ], | ||
skipped: [] | ||
} | ||
uploaded: 'images/my_image_1[alice].png', 'images/my_image_2[bob].png' | ||
skipped: 0 | ||
*/ | ||
@@ -265,3 +264,3 @@ ``` | ||
Главный плюс в том, что при изменении изображения и получении нового `image_id` на сервере, | ||
в коде навыка ничего менять не нужно - новый `image_id` подтянется автоматически: | ||
в коде навыка ничего менять не нужно - новый `image_id` подтянется по `localId` автоматически: | ||
```js | ||
@@ -281,6 +280,4 @@ const images = require('./images.json').ids; | ||
/* | ||
{ | ||
uploaded: [], | ||
skipped: [ 'images/my_image_1[alice].png', 'images/my_image_2[bob].png' ] | ||
} | ||
uploaded: 0 | ||
skipped: 'images/my_image_1[alice].png', 'images/my_image_2[bob].png' | ||
*/ | ||
@@ -297,6 +294,4 @@ ``` | ||
/* | ||
{ | ||
uploaded: [ 'images/my_image_1[alice].png' ], | ||
skipped: [ 'images/my_image_2[bob].png' ] | ||
} | ||
uploaded: 'images/my_image_1[alice].png' | ||
skipped: 'images/my_image_2[bob].png' | ||
*/ | ||
@@ -312,2 +307,20 @@ ``` | ||
Для обработки загружаемых файлов (например изменения размеров изображений) можно использовать параметр `transform`. | ||
Вот готовый пример кода для подгона изображений под размер `776х344` с использованием библиотеки [Jimp](https://github.com/oliver-moran/jimp): | ||
```js | ||
await imageManager.uploadChanged({ | ||
pattern: 'images/*.png', | ||
dbFile: 'images.json', | ||
transform: async buffer => { | ||
const image = await Jimp.read(buffer); | ||
return image | ||
.normalize() | ||
.background(0xFFFFFFFF) | ||
.contain(776, 344) | ||
.quality(75) | ||
.getBufferAsync(Jimp.AUTO); | ||
} | ||
}); | ||
``` | ||
### .deleteUnused() | ||
@@ -314,0 +327,0 @@ Удалить неиспользуемые изображения с сервера. |
@@ -1,5 +0,4 @@ | ||
const fs = require('fs-extra'); | ||
const path = require('path'); | ||
const fetch = require('node-fetch'); | ||
const FormData = require('form-data'); | ||
const debug = require('debug')('alice-asset-manager'); | ||
const {throwIf} = require('throw-utils'); | ||
@@ -44,6 +43,12 @@ const {stringify} = require('./utils'); | ||
async upload(filePath) { | ||
/** | ||
* Uploads buffer. | ||
* | ||
* @param {Buffer|Stream} buffer | ||
* @param {string} filename | ||
* @returns {Promise} | ||
*/ | ||
async uploadBuffer(buffer, filename) { | ||
const formData = new FormData(); | ||
const filename = path.basename(filePath); | ||
formData.append('file', fs.createReadStream(filePath), {filename}); | ||
formData.append('file', buffer, { filename }); | ||
return this._request(this._restUrl, { | ||
@@ -69,15 +74,23 @@ method: 'post', | ||
const fullUrl = `${BASE_URL}${url}`; | ||
options.headers = Object.assign({ | ||
Authorization: `OAuth ${this._token}`, | ||
timeout: this._timeout, | ||
}, options.headers); | ||
options = this._buildOptions(options); | ||
debug(options.method, fullUrl); | ||
const response = await fetch(fullUrl, options); | ||
if (response.ok) { | ||
return response.json(); | ||
const json = await response.json(); | ||
debug(response.status, json); | ||
return json; | ||
} else { | ||
const text = await response.text(); | ||
const method = (options.method || 'get').toUpperCase(); | ||
throw new Error(`${response.status} ${text} ${method} ${url}`); | ||
throw new Error(`${response.status} ${text} ${options.method} ${url}`); | ||
} | ||
} | ||
_buildOptions(options) { | ||
options.method = (options.method || 'GET').toUpperCase(); | ||
options.headers = Object.assign({ | ||
Authorization: `OAuth ${this._token}`, | ||
timeout: this._timeout, | ||
}, options.headers); | ||
return options; | ||
} | ||
}; |
/** | ||
* Image manager. | ||
*/ | ||
const fs = require('fs-extra'); | ||
const path = require('path'); | ||
const BaseManager = require('./base-manager'); | ||
@@ -63,3 +65,16 @@ const SmartUploader = require('./smart-uploader'); | ||
async upload(filePath) { | ||
const { image } = await super.upload(filePath); | ||
const buffer = fs.createReadStream(filePath); | ||
const filename = path.basename(filePath); | ||
return this.uploadBuffer(buffer, filename); | ||
} | ||
/** | ||
* Загрузить изображение из буфера. | ||
* | ||
* @param {Buffer|Stream} buffer данные | ||
* @param {string} filename имя файла | ||
* @returns {Promise} | ||
*/ | ||
async uploadBuffer(buffer, filename) { | ||
const { image } = await super.uploadBuffer(buffer, filename); | ||
return { | ||
@@ -97,8 +112,9 @@ ...image, | ||
* @param {function} [getLocalId] функция вычисления localId по имени файла | ||
* @param {function} [transform] функция обработки файлов (buffer, filepath) => buffer | ||
* @param {boolean} [dryRun=false] запуск без фактической загрузки файлов | ||
* @returns {Promise} | ||
* @returns {Promise<Array>} | ||
*/ | ||
async uploadChanged({pattern, dbFile, dryRun, getLocalId}) { | ||
async uploadChanged({ pattern, dbFile, getLocalId, transform, dryRun }) { | ||
const uploader = new SmartUploader(this); | ||
return uploader.uploadChanged({pattern, dbFile, dryRun, getLocalId}); | ||
return uploader.uploadChanged({ pattern, dbFile, getLocalId, transform, dryRun }); | ||
} | ||
@@ -105,0 +121,0 @@ |
@@ -10,6 +10,9 @@ /** | ||
// By default extract localId as '[local_id]' at the end of filename | ||
const getLocalIdDefault = file => { | ||
const matches = path.basename(file).match(/\[(.+?)\]/); | ||
return matches && matches[1]; | ||
const defaults = { | ||
// By default extract localId as '[local_id]' at the end of filename | ||
getLocalId: file => { | ||
const matches = path.basename(file).match(/\[(.+?)\]/); | ||
return matches && matches[1]; | ||
}, | ||
transform: (buffer, filePath) => buffer, // eslint-disable-line no-unused-vars | ||
}; | ||
@@ -36,3 +39,3 @@ | ||
async uploadChanged({pattern, dbFile, dryRun, getLocalId}) { | ||
async uploadChanged({ pattern, dbFile, getLocalId, transform, dryRun }) { | ||
const files = this._getFiles(pattern); | ||
@@ -43,10 +46,5 @@ this._createLocalItems(files, getLocalId); | ||
this._markLocalItemsForUpload(); | ||
if (!dryRun) { | ||
await this._upload(); | ||
this._saveDbFile(); | ||
} | ||
return { | ||
uploaded: this._localItems.filter(item => item.upload).map(item => item.file), | ||
skipped: this._localItems.filter(item => !item.upload).map(item => item.file), | ||
}; | ||
await this._upload({ transform, dryRun }); | ||
this._saveDbFile({ dryRun }); | ||
return this._localItems; | ||
} | ||
@@ -85,4 +83,8 @@ | ||
// загружаем файл, если он не был загружен или изменилась дата модификации или его нет на сервере | ||
if (!id || mtimeMs !== localItem.mtimeMs || !this._hasRemoteItem(id)) { | ||
localItem.upload = true; | ||
if (!id) { | ||
localItem.upload = 'new'; | ||
} else if (mtimeMs !== localItem.mtimeMs) { | ||
localItem.upload = 'changed'; | ||
} else if (!this._hasRemoteItem(id)) { | ||
localItem.upload = 'deleted_on_server'; | ||
} else { | ||
@@ -99,3 +101,3 @@ localItem.id = id; | ||
_createLocalItems(files, getLocalId) { | ||
getLocalId = getLocalId || getLocalIdDefault; | ||
getLocalId = getLocalId || defaults.getLocalId; | ||
this._localItems = files.map(file => { | ||
@@ -114,10 +116,13 @@ const localId = stringify(getLocalId(file)); | ||
async _upload() { | ||
const tasks = this._localItems | ||
.filter(localItem => localItem.upload) | ||
.map(async localItem => { | ||
const { id } = await this._manager.upload(localItem.file); | ||
async _upload({ transform, dryRun }) { | ||
transform = transform || defaults.transform; | ||
const localItemToUpload = this._localItems.filter(localItem => localItem.upload); | ||
for (const localItem of localItemToUpload) { | ||
const buffer = await fs.readFile(localItem.file); | ||
const transformedBuffer = await transform(buffer, localItem.file); | ||
if (!dryRun) { | ||
const {id} = await this._manager.uploadBuffer(transformedBuffer, localItem.file); | ||
localItem.id = id; | ||
}); | ||
await Promise.all(tasks); | ||
} | ||
} | ||
} | ||
@@ -156,10 +161,19 @@ | ||
_saveDbFile() { | ||
_saveDbFile({ dryRun }) { | ||
const newDbFileData = this._buildDbFileData(); | ||
if (!dryRun) { | ||
fs.outputJsonSync(this._dbFile, newDbFileData, {spaces: 2}); | ||
} | ||
} | ||
_buildDbFileData() { | ||
const newDbFileData = {}; | ||
// сортируем по localId, чтобы удобнее было смотреть в dbFile | ||
this._localItems.sort((a, b) => a.localId.localeCompare(b.localId)); | ||
// записываем id-шники в отдельное свойство ids для удобного доступа | ||
newDbFileData.ids = {}; | ||
this._localItems.forEach(({localId, id}) => newDbFileData.ids[localId] = id); | ||
// для звуков записываем еще поле tts, так удобнее использовать их в коде навыка | ||
// для звуков записываем еще поле tts, так удобнее использовать их в коде навыка | ||
if (this._manager.getTts) { | ||
@@ -169,3 +183,4 @@ newDbFileData.tts = {}; | ||
} | ||
// имя файла и дату моюицикации пишем в поле meta | ||
// имя файла и дату модификации пишем в поле meta | ||
newDbFileData.meta = {}; | ||
@@ -176,3 +191,4 @@ this._localItems.forEach(({file, localId, mtimeMs, id}) => { | ||
}); | ||
fs.outputJsonSync(this._dbFile, newDbFileData, {spaces: 2}); | ||
return newDbFileData; | ||
} | ||
@@ -179,0 +195,0 @@ |
/** | ||
* Sound manager. | ||
*/ | ||
const fs = require('fs-extra'); | ||
const path = require('path'); | ||
const BaseManager = require('./base-manager'); | ||
@@ -68,3 +70,16 @@ const SmartUploader = require('./smart-uploader'); | ||
async upload(filePath) { | ||
const { sound } = await super.upload(filePath); | ||
const buffer = fs.createReadStream(filePath); | ||
const filename = path.basename(filePath); | ||
return this.uploadBuffer(buffer, filename); | ||
} | ||
/** | ||
* Загрузить звук из буфера. | ||
* | ||
* @param {Buffer|Stream} buffer данные | ||
* @param {string} filename имя файла | ||
* @returns {Promise} | ||
*/ | ||
async uploadBuffer(buffer, filename) { | ||
const { sound } = await super.uploadBuffer(buffer, filename); | ||
return { | ||
@@ -113,8 +128,9 @@ ...sound, | ||
* @param {function} [getLocalId] функция вычисления localId по имени файла | ||
* @param {function} [transform] функция обработки файлов (buffer, filepath) => buffer | ||
* @param {boolean} [dryRun=false] запуск без фактической загрузки файлов | ||
* @returns {Promise} | ||
* @returns {Promise<Array>} | ||
*/ | ||
async uploadChanged({pattern, dbFile, dryRun, getLocalId}) { | ||
async uploadChanged({ pattern, dbFile, getLocalId, transform, dryRun }) { | ||
const uploader = new SmartUploader(this); | ||
return uploader.uploadChanged({pattern, dbFile, dryRun, getLocalId}); | ||
return uploader.uploadChanged({ pattern, dbFile, getLocalId, transform, dryRun }); | ||
} | ||
@@ -121,0 +137,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
44009
669
633
7
4
+ Addeddebug@^4.1.1
+ Addeddebug@4.3.7(transitive)