1.2.0 • Published 4 years ago

alice-asset-manager v1.2.0

Weekly downloads
4
License
MIT
Repository
github
Last release
4 years ago

alice-asset-manager

npm license

Node.js API для загрузки изображений и звуков в навык Алисы.

Позволяет:

  • загружать/удалять изображения и звуки
  • в режиме синхронизации загружать только измененные файлы и удалять неиспользуемые файлы с сервера
  • просматривать загруженные изображения/звуки на реальном устройстве через встроенный навык

Содержание

Установка

npm i alice-asset-manager --save-dev

Изображения

new ImageManager()

Создает инстанс менеджера изображений.

Параметры:

  /**
   * @param {string} token OAuth-токен
   * @param {string} skillId идентификатор навыка
   */

Как получить token и skillId - описано в документации.

Пример:

const { ImageManager } = require('alice-asset-manager');

const imageManager = new ImageManager({
  token: 'TOKEN',
  skillId: 'SKILL_ID',
});

.getQuota()

Получить данные о занятом месте.

await imageManager.getQuota();
/*
{
  total: 104857600,
  used: 379850
}
*/

.upload()

Загрузить изображение из файла.

Параметры:

  /**
   * @param {string} filePath путь до файла
   * @returns {Promise}
   */

Пример:

await imageManager.upload('images/test.jpg');
/*
{
  id: '213044/aef2a365f198b4435611',
  size: 8596,
  createdAt: '2019-12-09T06:05:35.035Z',
  url: 'https://avatars.mds.yandex.net/get-dialogs-skill-card/213044/aef2a365f198b4435611/orig'
}
*/

.getItems()

Получить список всех изображений на сервере.

await imageManager.getItems();
/*
[
  {
    id: '213044/aef2a365f198b4435611',
    origUrl: null,
    size: 8596,
    createdAt: '2019-12-09T06:05:35.035Z',
    url: 'https://avatars.mds.yandex.net/get-dialogs-skill-card/213044/aef2a365f198b4435611/orig'
  }
  ...
]
*/

.getItem()

Получить данные об отдельном изображении.

Параметры:

  /**
   * @param {string} imageId
   * @returns {Promise}
   */

Пример:

await imageManager.getItem('213044/aef2a365f198b4435611');
/*
{
  id: '213044/aef2a365f198b4435611',
  origUrl: null,
  size: 8596,
  createdAt: '2019-12-09T06:05:35.035Z',
  url: 'https://avatars.mds.yandex.net/get-dialogs-skill-card/213044/aef2a365f198b4435611/orig'
}
*/

.getUrl()

Получить ссылку на изображение.

Параметры:

  /**
   * @param {string} imageId
   * @returns {string}
   */

Пример:

imageManager.getUrl('213044/aef2a365f198b4435611');
/*
'https://avatars.mds.yandex.net/get-dialogs-skill-card/213044/aef2a365f198b4435611/orig'
*/

.delete()

Удалить изображение с сервера.

Параметры:

  /**
   * @param {string} imageId
   * @returns {Promise}
   */

Пример:

await imageManager.delete('213044/aef2a365f198b4435611');

.uploadChanged()

Загрузить новые и измененные изображения на сервер.

Параметры:

  /**
   * @param {string} pattern путь/паттерн до папки с изображениями
   * @param {string} dbFile путь до файла с данными о загрузках
   * @param {function} [getLocalId] функция вычисления localId по имени файла
   * @param {function} [transform] функция обработки файлов (buffer, filepath) => buffer
   * @param {boolean} [dryRun=false] запуск без фактической загрузки файлов
   * @returns {Promise}
   */

Как это работает:

При каждом вызове uploadChanged() сохраняет результаты загрузки во вспомогательный файл и при последующих вызовах использует сохраненную информацию, чтобы определить файлы для загрузки.

Чтобы избежать повторной загрузки файла при переименовании, для каждого файла вычисляется localId - уникальный идентификатор, который должен сохраняться при переименовании. По умолчанию localId ищется в квадратных скобках [] в имени файла:

image[foo].png -> foo

Пример:

В папке images лежат два изображения my_image_1[alice].png и my_image_2[bob].png.

Их localId следующие:

my_image_1[alice].png -> alice
my_image_2[bob].png -> bob

Запускаем первую зарузку - на сервер загрузятся оба файла:

await imageManager.uploadChanged({
  pattern: 'images/*.png', // путь/паттерн до папки с изображениями
  dbFile: 'images.json',   // путь до файла с данными о загрузках (изначально файла нет)
});
/*
uploaded: 'images/my_image_1[alice].png', 'images/my_image_2[bob].png'
skipped: 0
*/

В images.json запишется примерно следующее:

{
  "ids": {
    "alice": "213044/aef2a365f198b4435611",
    "bob": "834732/sdg3s44fjh234524j2h4"
  },
  "meta": {
    "alice": {
      "file": "images/my_image_1[alice].png",
      "url": "https://avatars.mds.yandex.net/get-dialogs-skill-card/213044/aef2a365f198b4435611/orig",
      "mtimeMs": 1576249136829.0754
    },
    "bob": {
      "file": "images/my_image_2[bob].png",
      "url": "https://avatars.mds.yandex.net/get-dialogs-skill-card/834732/sdg3s44fjh234524j2h4/orig",
      "mtimeMs": 1576249136830.0842
    }
  }
}

Файл images.json удобно использовать в коде вашего навыка, вставляя изображения по их localId. Главный плюс в том, что при изменении изображения и получении нового image_id на сервере, в коде навыка ничего менять не нужно - новый image_id подтянется по localId автоматически:

const images = require('./images.json').ids;
// ...
response.card.image_id = images.alice;

Если повторно вызвать uploadChanged() ничего не меняя, то загрузок не произойдет, т.к. файлы не изменились и уже загружены на сервер:

await imageManager.uploadChanged({
  pattern: 'images/*.png',
  dbFile: 'images.json',
});
/*
uploaded: 0
skipped: 'images/my_image_1[alice].png', 'images/my_image_2[bob].png'
*/

Если изменить в фоторедакторе один из файлов (например my_image_1[alice].png), то при вызове uploadChanged() загрузится только этот измененный файл:

await imageManager.uploadChanged({
  pattern: 'images/*.png',
  dbFile: 'images.json',
});
/*
uploaded: 'images/my_image_1[alice].png'
skipped: 'images/my_image_2[bob].png'
*/

Информация в images.json также обновится - туда запишется новый image_id для my_image_1[alice].png. Использование в коде навыка останется прежним:

const images = require('./images.json').ids;
// ...
response.card.image_id = images.alice;

Для обработки загружаемых файлов (например изменения размеров изображений) можно использовать параметр transform. Вот готовый пример кода для подгона изображений под размер 776х344 с использованием библиотеки Jimp:

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()

Удалить неиспользуемые изображения с сервера.

Параметры:

  /**
   * @param {string} dbFile путь до файла с данными о загрузках, созданный методом uploadChanged()
   * @param {boolean} [dryRun=false] запуск без фактического удаления изображений
   * @returns {Promise}
   */

Работает только в связке с .uploadChanged(). При частых изменениях файлов и многократных вызовах .uploadChanged() на сервере накапливаются неиспользуемые изображения. Их нужно периодически удалять, чтобы освободить место. Метод .deleteUnused() сравнивает то, что записано в dbFile с тем что лежит на сервере, и удаляет с сервера лишнее.

Пример:

В навыке используются два изображения my_image_1[alice].png и my_image_2[bob].png. Они были загружены на сервер через .uploadChanged() и информация сохранена в dbFile: 'images.json'.

В новой версии навыка файл my_image_1[alice].png не используется и был удален. После вызова .uploadChanged() в images.json останется только запись про второй файл images/my_image_2[bob].png:

{
  "ids": {
    "bob": "834732/sdg3s44fjh234524j2h4"
  },
  "meta": {
    "bob": {
      "file": "images/my_image_2[bob].png",
      "url": "https://avatars.mds.yandex.net/get-dialogs-skill-card/834732/sdg3s44fjh234524j2h4/orig",
      "mtimeMs": 1576249136830.0842
    }
  }
}

Но на сервере все еще лежат два файла. Это нужно для того, чтобы текущая версия навыка продолжала нормально работать. Когда новая версия навыка выкатится, запись про my_image_1[alice].png можно удалить с сервера:

await imageManager.deleteUnused({
  dbFile: 'images.json',
});
/*
{
  deleted: [ '213044/aef2a365f198b4435611' ],
  used: [ 'images/my_image_2[bob].png' ]
}
*/

Чтобы предварительно посмотреть, какие изображения будут удалены с сервера - можно вызвать метод с параметром dryRun: true:

await imageManager.deleteUnused({
  dbFile: 'images.json',
  dryRun: true
});
/*
{
  deleted: [ '213044/aef2a365f198b4435611' ],
  used: [ 'images/my_image_2[bob].png' ]
}
*/

.createViewServer()

Создает HTTP-сервер для просмотра загруженных изображений через навык.

Параметры:

  /**
   * @param {string} [dbFile] путь до файла с данными о загрузках, созданный методом uploadChanged()
   */

Создаваемый HTTP-сервер позволяет быстро посмотреть загруженные изображения на экране смартфона.

Пример:

const server = imageManager.createViewServer();
server.listen(3000);

Когда сервер запустился на 3000 порту, можно используя любой прокси-навык (например alice-dev.vitalets.xyz) проверить все изображения на смартфоне. Изображения в навыке отсортированы по дате загрузки, поэтому в первую очередь будут показаны недавно измененные файлы.

Чтобы рядом с изображениями дополнительно выводилось имя файла и localId, нужно указать в параметрах dbFile:

const server = imageManager.createViewServer({
  dbFile: 'images.json'
});
server.listen(3000);

Чтобы сервер автоматически перезапускался при изменениях images.json можно использовать nodemon:

nodemon ./image-server.js --watch ./images.json

Звуки

new SoundManager()

Создает инстанс менеджера звуков.

Параметры:

  /**
   * @param {string} token OAuth-токен
   * @param {string} skillId идентификатор навыка
   */

Как получить token и skillId - описано в документации.

Пример:

const { SoundManager } = require('alice-asset-manager');

const soundManager = new SoundManager({
  token: 'TOKEN',
  skillId: 'SKILL_ID',
});

.getQuota()

Получить данные о занятом месте.

await soundManager.getQuota();
/*
{
  total: 104857600,
  used: 379850
}
*/

.upload()

Загрузить звук из файла.

Параметры:

  /**
   * @param {string} filePath путь до файла
   * @returns {Promise}
   */

Пример:

await soundManager.upload('images/test.mp3');
/*
{
  id: 'c72463ae-01b5-48a1-a7f2-e657e4594166',
  skillId: 'SKILL_ID',
  size: null,
  originalName: 'test.mp3',
  createdAt: '2019-12-09T06:13:49.595Z',
  isProcessed: false,
  error: null,
  url: 'https://yastatic.net/s3/dialogs/dialogs-upload/sounds/opus/SKILL_ID/c72463ae-01b5-48a1-a7f2-e657e4594166.opus'
}
*/

.getItems()

Получить список всех звуков на сервере.

await soundManager.getItems();
/*
[
  {
    id: 'c72463ae-01b5-48a1-a7f2-e657e4594166',
    skillId: 'SKILL_ID',
    size: 24915,
    originalName: 'test.mp3',
    createdAt: '2019-12-09T06:19:48.317Z',
    isProcessed: true,
    error: null,
    url: 'https://yastatic.net/s3/dialogs/dialogs-upload/sounds/opus/SKILL_ID/c72463ae-01b5-48a1-a7f2-e657e4594166.opus'
  }
  ...
]
*/

.getItem()

Получить данные об отдельном звуке на сервере.

Параметры:

  /**
   * @param {string} soundId
   * @returns {Promise}
   */

Пример:

await soundManager.getItem('c72463ae-01b5-48a1-a7f2-e657e4594166');
/*
{
  id: 'c72463ae-01b5-48a1-a7f2-e657e4594166',
  skillId: 'SKILL_ID',
  size: 24915,
  originalName: 'test.mp3',
  createdAt: '2019-12-09T06:19:48.317Z',
  isProcessed: true,
  error: null,
  url: 'https://yastatic.net/s3/dialogs/dialogs-upload/sounds/opus/SKILL_ID/c72463ae-01b5-48a1-a7f2-e657e4594166.opus'
}
*/

.getUrl()

Получить ссылку на звук.

Параметры:

  /**
   * @param {string} soundId
   * @returns {string}
   */

Пример:

soundManager.getUrl('c72463ae-01b5-48a1-a7f2-e657e4594166');
/*
'https://yastatic.net/s3/dialogs/dialogs-upload/sounds/opus/SKILL_ID/c72463ae-01b5-48a1-a7f2-e657e4594166.opus'
*/

.getTts()

Получить tts для вставки звука в ответ навыка.

Параметры:

  /**
   * @param {string} soundId
   * @returns {string}
   */

Пример:

soundManager.getTts('c72463ae-01b5-48a1-a7f2-e657e4594166');
/*
'<speaker audio="dialogs-upload/SKILL_ID/c72463ae-01b5-48a1-a7f2-e657e4594166.opus">'
*/

.delete()

Удалить звук с сервера.

Параметры:

  /**
   * @param {string} soundId
   * @returns {Promise}
   */

Пример:

await soundManager.delete('213044/aef2a365f198b4435611');

.uploadChanged()

Загрузить новые и измененные звуки на сервер.

Параметры:

  /**
   * @param {string} pattern путь/паттерн до папки со звуками
   * @param {string} dbFile путь до файла с данными о загрузках
   * @param {function} [getLocalId] функция вычисления localId по имени файла
   * @param {boolean} [dryRun=false] запуск без зфактической загрузки файлов
   * @returns {Promise}
   */

Все работает аналогично методу .uploadChanged() для изображений.

.deleteUnused()

Удалить неиспользуемые звуки с сервера.

Параметры:

  /**
   * @param {string} dbFile путь до файла с данными о загрузках, созданный методом uploadChanged()
   * @param {boolean} [dryRun=false] запуск без фактического удаления звуков
   * @returns {Promise}
   */

Все работает аналогично методу .deleteUnused() для изображений.

.createViewServer()

Создает HTTP-сервер для прослушивания загруженных звуков через навык.

Параметры:

  /**
   * @param {string} [dbFile] путь до файла с данными о загрузках, созданный методом uploadChanged()
   */

Все работает аналогично методу .createViewServer() для изображений.

Лицензия

MIT @ Vitaliy Potapov