0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-11 02:12:21 -05:00

Added thumbnail upload endpoint to Media API

closes https://github.com/TryGhost/Toolbox/issues/120

- Allows to update and upload brand new thumbnail images for previusly uploaded media resources
- The endpoint is available udner alpa flag as part of Admin API at `PUT /media/thumbnail/`
- As an input accepts following parameters:
- *required* `file` field containing an image file
- *required* `url` field containing parent media file URL
- *optional* `ref` as a field to put in an ID to reference the resource on the client side

- The response has following format:
```
{
  media: [{
    url: 'http://127.0.0.1:2369/content/images/1991/11/nicevideo_thumb.png'
    ref: 'unique-id-420'
  }]
}
```
This commit is contained in:
Naz 2021-11-09 16:07:10 +04:00
parent 2a7ef77a7b
commit 61b82e3ae2
7 changed files with 106 additions and 0 deletions

View file

@ -1,3 +1,4 @@
const path = require('path');
const storage = require('../../adapters/storage');
module.exports = {
@ -18,5 +19,24 @@ module.exports = {
thumbnailPath
};
}
},
uploadThumbnail: {
permissions: false,
options: [
'url'
],
async query(frame) {
const mediaStorage = storage.getStorage('media');
const targetDir = path.dirname(mediaStorage.urlToPath(frame.data.url));
// NOTE: need to cleanup otherwise the parent media name won't match thumb name
// due to "unique name" generation during save
if (mediaStorage.exists(frame.file.name, targetDir)) {
await mediaStorage.delete(frame.file.name, targetDir);
}
return await mediaStorage.save(frame.file, targetDir);
}
}
};

View file

@ -35,6 +35,10 @@ module.exports = {
return require('./members');
},
get media() {
return require('./media');
},
get products() {
return require('./products');
},

View file

@ -0,0 +1,8 @@
const path = require('path');
module.exports = {
uploadThumbnail(apiConfig, frame) {
const parentFileName = path.basename(frame.data.url, path.extname(frame.data.url));
frame.file.name = `${parentFileName}_thumb${frame.file.ext}`;
}
};

View file

@ -24,5 +24,14 @@ module.exports = {
ref: frame.data.ref || null
}]
};
},
uploadThumbnail(path, apiConfig, frame) {
return frame.response = {
media: [{
url: getURL(path),
ref: frame.data.ref || null
}]
};
}
};

View file

@ -3,5 +3,9 @@ const limitService = require('../../../../../services/limits');
module.exports = {
async upload(apiConfig, frame) {
await limitService.errorIfIsOverLimit('uploads', {currentCount: frame.file.size});
},
async uploadThumbnail(apiConfig, frame) {
await limitService.errorIfIsOverLimit('uploads', {currentCount: frame.file.size});
}
};

View file

@ -244,6 +244,13 @@ module.exports = function apiRoutes() {
apiMw.upload.mediaValidation({type: 'media'}),
http(api.media.upload)
);
router.put('/media/thumbnail/',
labs.enabledMiddleware('mediaAPI'),
mw.authAdminApi,
apiMw.upload.single('file'),
apiMw.upload.validation({type: 'images'}),
http(api.media.uploadThumbnail)
);
// ## files
router.post('/files/upload',

View file

@ -87,5 +87,59 @@ describe('Media API', function () {
});
describe('media/thumbnail/:url', function () {
it('Can update existing thumbnail', async function () {
const res = await request.post(localUtils.API.getApiQuery('media/upload'))
.set('Origin', config.get('url'))
.expect('Content-Type', /json/)
.field('purpose', 'video')
.field('ref', 'https://ghost.org/sample_640x360.mp4')
.attach('file', path.join(__dirname, '/../../utils/fixtures/media/sample_640x360.mp4'))
.attach('thumbnail', path.join(__dirname, '/../../utils/fixtures/images/ghost-logo.png'))
.expect(201);
res.body.media[0].ref.should.equal('https://ghost.org/sample_640x360.mp4');
media.push(res.body.media[0].url.replace(config.get('url'), ''));
media.push(res.body.media[0].thumbnail_url.replace(config.get('url'), ''));
const thumbnailRes = await request.put(localUtils.API.getApiQuery(`media/thumbnail/`))
.set('Origin', config.get('url'))
.expect('Content-Type', /json/)
.field('url', res.body.media[0].url)
.field('ref', 'updated_thumbnail')
.attach('file', path.join(__dirname, '/../../utils/fixtures/images/ghosticon.jpg'))
.expect(200);
const thumbnailUrl = res.body.media[0].url.replace('.mp4', '_thumb.jpg');
thumbnailRes.body.media[0].url.should.equal(thumbnailUrl);
thumbnailRes.body.media[0].ref.should.equal('updated_thumbnail');
media.push(thumbnailRes.body.media[0].url.replace(config.get('url'), ''));
});
it('Can create new thumbnail based on parent media URL without existing thumbnail', async function () {
const res = await request.post(localUtils.API.getApiQuery('media/upload'))
.set('Origin', config.get('url'))
.expect('Content-Type', /json/)
.field('purpose', 'video')
.field('ref', 'https://ghost.org/sample_640x360.mp4')
.attach('file', path.join(__dirname, '/../../utils/fixtures/media/sample_640x360.mp4'))
.expect(201);
media.push(res.body.media[0].url.replace(config.get('url'), ''));
const thumbnailRes = await request.put(localUtils.API.getApiQuery(`media/thumbnail/`))
.set('Origin', config.get('url'))
.expect('Content-Type', /json/)
.field('url', res.body.media[0].url)
.field('ref', 'updated_thumbnail_2')
.attach('file', path.join(__dirname, '/../../utils/fixtures/images/ghosticon.jpg'))
.expect(200);
const thumbnailUrl = res.body.media[0].url.replace('.mp4', '_thumb.jpg');
thumbnailRes.body.media[0].url.should.equal(thumbnailUrl);
thumbnailRes.body.media[0].ref.should.equal('updated_thumbnail_2');
media.push(thumbnailRes.body.media[0].url.replace(config.get('url'), ''));
});
});
});