0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-24 23:48:13 -05:00

Added media file support to the zip importer

refs https://github.com/TryGhost/Toolbox/issues/523

- When migrating or importing ZIP files into Ghost there's often a need to include more than just post content and images.
- When media files are present in the imported zip file they are now copied across and processed along with the rest of import files: json, images, csvs, etc.
- The importer also searches for use of the media files in the imported "posts" substituting the links with local ones
- The media importer recognizes media files inside of "media" or "content/media" folders present in the zip. The supported media file extensions are same as for media upload widget:
".mp4", ".webm", ".ogv", ".mp3", ".wav", ".ogg", ".m4a"

with following content-types:
 "video/mp4", "video/webm", "video/ogg", "audio/mpeg", "audio/vnd.wav", "audio/wave", "audio/wav", "audio/x-wav", "audio/ogg", "audio/mp4", "audio/x-m4a"
This commit is contained in:
Naz 2023-03-02 13:53:06 +08:00
parent 49553e5942
commit 2361669bb6
No known key found for this signature in database
3 changed files with 47 additions and 15 deletions

View file

@ -64,10 +64,15 @@ class ImportManager {
type: 'images',
store: imageStorage
});
const mediaImporter = new ContentFileImporter({
type: 'media',
store: mediaStorage
});
/**
* @type {Importer[]} importers
*/
this.importers = [imageImporter, RevueImporter, DataImporter];
this.importers = [imageImporter, mediaImporter, RevueImporter, DataImporter];
/**
* @type {Handler[]}

View file

@ -15,14 +15,21 @@ replaceImage = function (markdown, image) {
return markdown.replace(regex, image.newPath);
};
preProcessPosts = function (data, image) {
/**
* @param {Object} data
* @param {Object[]} data.posts
* @param {Object} contentFile
* @param {String} contentFile.originalPath
* @param {String} contentFile.newPath
*/
preProcessPosts = function (data, contentFile) {
_.each(data.posts, function (post) {
post.markdown = replaceImage(post.markdown, image);
post.markdown = replaceImage(post.markdown, contentFile);
if (post.html) {
post.html = replaceImage(post.html, image);
post.html = replaceImage(post.html, contentFile);
}
if (post.feature_image) {
post.feature_image = replaceImage(post.feature_image, image);
post.feature_image = replaceImage(post.feature_image, contentFile);
}
});
};
@ -56,7 +63,7 @@ class ContentFileImporter {
/**
*
* @param {Object} deps
* @param {'images'} deps.type - importer type
* @param {'images' | 'media'} deps.type - importer type
* @param {import('ghost-storage-base')} deps.store
*/
constructor(deps) {
@ -65,15 +72,30 @@ class ContentFileImporter {
}
preProcess(importData) {
if (importData.images && importData.data) {
_.each(importData.images, function (image) {
preProcessPosts(importData.data.data, image);
preProcessTags(importData.data.data, image);
preProcessUsers(importData.data.data, image);
});
if (this.type === 'images') {
if (importData.images && importData.data){
_.each(importData.images, function (image) {
preProcessPosts(importData.data.data, image);
preProcessTags(importData.data.data, image);
preProcessUsers(importData.data.data, image);
});
}
importData.preProcessedByImage = true;
}
// @NOTE: the type === 'media' check does not belong here and should be abstracted away
// to make this importer more generic
if (this.type === 'media') {
if (importData.media && importData.data) {
_.each(importData.media, function (file) {
preProcessPosts(importData.data.data, file);
});
}
importData.preProcessedByMedia = true;
}
importData.preProcessedByImage = true;
return importData;
}

View file

@ -30,7 +30,7 @@ describe('Importer', function () {
describe('ImportManager', function () {
it('has the correct interface', function () {
ImportManager.handlers.should.be.instanceof(Array).and.have.lengthOf(5);
ImportManager.importers.should.be.instanceof(Array).and.have.lengthOf(3);
ImportManager.importers.should.be.instanceof(Array).and.have.lengthOf(4);
ImportManager.loadFile.should.be.instanceof(Function);
ImportManager.preProcess.should.be.instanceof(Function);
ImportManager.doImport.should.be.instanceof(Function);
@ -391,7 +391,11 @@ describe('Importer', function () {
describe('preProcess', function () {
// preProcess can modify the data prior to importing
it('calls the DataImporter preProcess method', function (done) {
const input = {data: {}, images: []};
const input = {
data: {},
images: [],
media: []
};
// pass a copy so that input doesn't get modified
const inputCopy = _.cloneDeep(input);
@ -412,6 +416,7 @@ describe('Importer', function () {
output.should.not.equal(input);
output.should.have.property('preProcessedByData', true);
output.should.have.property('preProcessedByImage', true);
output.should.have.property('preProcessedByMedia', true);
done();
}).catch(done);
});