Make Seance into separate Object
Now, the Seance object is what everyone operates with. That way, we can do fancy stuff like emitting events, etc. if the need so arises (as it will, very soon; trust me).
This commit is contained in:
parent
e0b5ab31b4
commit
0d9ecfd8f5
2 changed files with 412 additions and 398 deletions
135
functions.js
135
functions.js
|
@ -11,30 +11,39 @@ const Rembrandt = require('rembrandt')
|
||||||
|
|
||||||
const config = require('./config')
|
const config = require('./config')
|
||||||
|
|
||||||
const MEDIUM_IMG_CDN = 'https://miro.medium.com/fit/c/'
|
class Seance {
|
||||||
|
constructor(...args) {
|
||||||
|
this.MEDIUM_IMG_CDN = 'https://miro.medium.com/fit/c/'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const ghostAdmin = new GhostAdminAPI({
|
this.ghostAdmin = new GhostAdminAPI({
|
||||||
url: config.ghost.url,
|
url: config.ghost.url,
|
||||||
version: config.ghost.version,
|
version: config.ghost.version,
|
||||||
key: config.ghost.admin_key,
|
key: config.ghost.admin_key,
|
||||||
})
|
})
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.error('Your Ghost isn\'t configured. Please run `seance setup` to fix this!')
|
console.error('Your Ghost isn\'t configured. Please run `seance setup` to fix this!')
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
|
/**
|
||||||
* function [fetchFromMedium]
|
* function [fetchFromMedium]
|
||||||
* @returns [string] status
|
* @returns [string] status
|
||||||
*/
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
const fetchFromMedium = async (mediumUrl) => {
|
async fetchFromMedium (mediumUrl) {
|
||||||
console.info(`Fetching: ${mediumUrl}`);
|
console.info(`Fetching: ${mediumUrl}`);
|
||||||
output = path.join(process.env.PWD, 'content')
|
var output = path.join(process.env.PWD, 'content')
|
||||||
|
|
||||||
|
var json
|
||||||
|
|
||||||
|
json = await this.fetchMediumJSON(mediumUrl)
|
||||||
|
|
||||||
// use mediumexporter's getPost function to fetch a Medium post
|
// use mediumexporter's getPost function to fetch a Medium post
|
||||||
const post = await getPost(mediumUrl, {
|
const post = await getPost(mediumUrl, {
|
||||||
returnObject: true,
|
returnObject: true,
|
||||||
output: output,
|
output: output,
|
||||||
|
postJSON: json
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
return {
|
return {
|
||||||
error: err,
|
error: err,
|
||||||
|
@ -43,7 +52,7 @@ const fetchFromMedium = async (mediumUrl) => {
|
||||||
|
|
||||||
// set output folder path
|
// set output folder path
|
||||||
// this is based on what mediumexporter chooses as the output folder
|
// this is based on what mediumexporter chooses as the output folder
|
||||||
outputFolder = path.join(output, post.slug)
|
var outputFolder = path.join(output, post.slug)
|
||||||
|
|
||||||
console.info(`Saving to: ${outputFolder}`)
|
console.info(`Saving to: ${outputFolder}`)
|
||||||
|
|
||||||
|
@ -78,17 +87,18 @@ const fetchFromMedium = async (mediumUrl) => {
|
||||||
// write metadata to output folder
|
// write metadata to output folder
|
||||||
fs.writeFileSync(path.join(outputFolder, 'metadata.json'), metadata)
|
fs.writeFileSync(path.join(outputFolder, 'metadata.json'), metadata)
|
||||||
return post
|
return post
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
|
/**
|
||||||
* function [pushToGhost]
|
* function [pushToGhost]
|
||||||
* @returns [string] status
|
* @returns [string] status
|
||||||
*/
|
*/
|
||||||
const pushToGhost = async (postSlug) => {
|
async pushToGhost (postSlug) {
|
||||||
console.info('Pushing: ' + postSlug);
|
console.info('Pushing: ' + postSlug);
|
||||||
|
|
||||||
// Decide working path
|
// Decide working path
|
||||||
postFolder = path.resolve('content/' + postSlug)
|
var postFolder = path.resolve('content/' + postSlug)
|
||||||
|
|
||||||
// Verify file exists
|
// Verify file exists
|
||||||
if (!fs.existsSync(postFolder)) {
|
if (!fs.existsSync(postFolder)) {
|
||||||
|
@ -107,7 +117,7 @@ const pushToGhost = async (postSlug) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decide WebDAV upload path
|
// Decide WebDAV upload path
|
||||||
current_date = new Date()
|
var current_date = new Date()
|
||||||
|
|
||||||
const uploadPath = path.join(
|
const uploadPath = path.join(
|
||||||
current_date.getUTCFullYear().toString(),
|
current_date.getUTCFullYear().toString(),
|
||||||
|
@ -125,7 +135,7 @@ const pushToGhost = async (postSlug) => {
|
||||||
// load metadata file
|
// load metadata file
|
||||||
console.debug('Loading metadata')
|
console.debug('Loading metadata')
|
||||||
|
|
||||||
postMetaFile = path.join(postFolder, 'metadata.json')
|
var postMetaFile = path.join(postFolder, 'metadata.json')
|
||||||
let postMeta = await JSON.parse(fs.readFileSync(postMetaFile))
|
let postMeta = await JSON.parse(fs.readFileSync(postMetaFile))
|
||||||
|
|
||||||
// Process lines
|
// Process lines
|
||||||
|
@ -167,14 +177,14 @@ const pushToGhost = async (postSlug) => {
|
||||||
console.warn('Skipping missing image: ' + imageName)
|
console.warn('Skipping missing image: ' + imageName)
|
||||||
} else {
|
} else {
|
||||||
// check for separator image
|
// check for separator image
|
||||||
var isScissors = await checkScissors(imagePath)
|
var isScissors = await this.checkScissors(imagePath)
|
||||||
if (isScissors) {
|
if (isScissors) {
|
||||||
newLine = '\n---\n'
|
newLine = '\n---\n'
|
||||||
} else {
|
} else {
|
||||||
// upload pic to server
|
// upload pic to server
|
||||||
console.debug(`Adding to upload queue: ${imageName}`)
|
console.debug(`Adding to upload queue: ${imageName}`)
|
||||||
uploadedImages.push(imageName)
|
uploadedImages.push(imageName)
|
||||||
uploadDav(davPath, imagePath)
|
this.uploadDav(davPath, imagePath)
|
||||||
|
|
||||||
newLine = '![' + imageAlt + '](' + uploadedPath + '/' + imageName + ')'
|
newLine = '![' + imageAlt + '](' + uploadedPath + '/' + imageName + ')'
|
||||||
}
|
}
|
||||||
|
@ -201,7 +211,7 @@ const pushToGhost = async (postSlug) => {
|
||||||
console.warn(`Skipping feature image "${imageName}": file not found`)
|
console.warn(`Skipping feature image "${imageName}": file not found`)
|
||||||
} else {
|
} else {
|
||||||
console.log(`Uploading feature image: ${imageName}`)
|
console.log(`Uploading feature image: ${imageName}`)
|
||||||
uploadDav(davPath, imagePath)
|
this.uploadDav(davPath, imagePath)
|
||||||
featuredImagePath = uploadedPath + '/' + imageName
|
featuredImagePath = uploadedPath + '/' + imageName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,12 +227,12 @@ const pushToGhost = async (postSlug) => {
|
||||||
// Uploads will continue in paralell though
|
// Uploads will continue in paralell though
|
||||||
console.debug('Adding to Ghost')
|
console.debug('Adding to Ghost')
|
||||||
|
|
||||||
ghostAdmin.posts.add({
|
this.ghostAdmin.posts.add({
|
||||||
title: postMeta.title,
|
title: postMeta.title,
|
||||||
custom_excerpt: postMeta.subtitle || null,
|
custom_excerpt: postMeta.subtitle || null,
|
||||||
tags: postMeta.tags,
|
tags: postMeta.tags,
|
||||||
authors: users,
|
authors: users,
|
||||||
html: markdown.toHTML(fs.readFileSync(postOutput, mode='utf-8')),
|
html: markdown.toHTML(fs.readFileSync(postOutput, 'utf-8')),
|
||||||
feature_image: featuredImagePath
|
feature_image: featuredImagePath
|
||||||
}, {source: 'html'})
|
}, {source: 'html'})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
@ -232,31 +242,43 @@ const pushToGhost = async (postSlug) => {
|
||||||
}
|
}
|
||||||
console.log('Post conveyed successfully.')
|
console.log('Post conveyed successfully.')
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
|
/**
|
||||||
* function [mediumToGhost]
|
* function [mediumToGhost]
|
||||||
* @returns [string] status
|
* @returns [string] status
|
||||||
*/
|
*/
|
||||||
const mediumToGhost = (mediumUrl) => {
|
mediumToGhost (mediumUrl) {
|
||||||
console.info('Copying: ' + mediumUrl);
|
console.info('Copying: ' + mediumUrl);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
async function fetchMediumJSON(mediumUrl) {
|
async fetchMediumJSON(mediumUrl) {
|
||||||
console.debug(`Fetching: ${mediumUrl}`)
|
var json
|
||||||
|
var text
|
||||||
|
|
||||||
|
if (mediumUrl.match(/^http/i)) {
|
||||||
|
// add ?json attribute
|
||||||
|
mediumUrl = mediumUrl.replace(/#.*$/, '')
|
||||||
|
mediumUrl= `${mediumUrl}?format=json`
|
||||||
const response = await fetch(mediumUrl)
|
const response = await fetch(mediumUrl)
|
||||||
const text = await response.text()
|
text = await response.text()
|
||||||
const json = await JSON.parse(text.substr(text.indexOf('{')))
|
} else {
|
||||||
return json;
|
throw { error: 'URL must be a Medium URL' }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
json = await JSON.parse(text.substr(text.indexOf('{')))
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* function [checkScissors]
|
* function [checkScissors]
|
||||||
* @returns [boolean] matchStatus
|
* @returns [boolean] matchStatus
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const checkScissors = async (imagePath) => {
|
async checkScissors (imagePath) {
|
||||||
// Decide "separator" image
|
// Decide "separator" image
|
||||||
// If set, images matching this will be ignored and replaced
|
// If set, images matching this will be ignored and replaced
|
||||||
// with a horizontal-rule ("---" in markdown) instead.
|
// with a horizontal-rule ("---" in markdown) instead.
|
||||||
|
@ -284,13 +306,13 @@ const checkScissors = async (imagePath) => {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* function [createUser]
|
* function [createUser]
|
||||||
* @returns [object] ghost data json
|
* @returns [object] ghost data json
|
||||||
*/
|
*/
|
||||||
const generateUserData = async (mediumUsername, email) => {
|
async generateUserData (mediumUsername, email) {
|
||||||
console.debug('Creating: @' + mediumUsername + '(email: ' + email + ')');
|
console.debug('Creating: @' + mediumUsername + '(email: ' + email + ')');
|
||||||
const mediumUrl = `https://medium.com/@${mediumUsername}/?format=json`;
|
const mediumUrl = `https://medium.com/@${mediumUsername}/?format=json`;
|
||||||
const json = await fetchMediumJSON(mediumUrl);
|
const json = await fetchMediumJSON(mediumUrl);
|
||||||
|
@ -320,7 +342,7 @@ const generateUserData = async (mediumUsername, email) => {
|
||||||
|
|
||||||
console.log("Uploading to server")
|
console.log("Uploading to server")
|
||||||
|
|
||||||
await uploadDav(path.join(config.webdav.path_prefix,'avatars'),
|
await this.uploadDav(path.join(config.webdav.path_prefix,'avatars'),
|
||||||
filePath)
|
filePath)
|
||||||
|
|
||||||
// Generate Ghost JSON
|
// Generate Ghost JSON
|
||||||
|
@ -345,9 +367,9 @@ const generateUserData = async (mediumUsername, email) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return(JSON.stringify(ghostData))
|
return(JSON.stringify(ghostData))
|
||||||
};
|
};
|
||||||
|
|
||||||
const createDirIfNotExist = async (client, folder) => {
|
async createDirIfNotExist (client, folder) {
|
||||||
// recursively create subfolders if they don't exist.
|
// recursively create subfolders if they don't exist.
|
||||||
|
|
||||||
//safety: don't touch directories outside WEBDAV_PATH_PREFIX
|
//safety: don't touch directories outside WEBDAV_PATH_PREFIX
|
||||||
|
@ -370,7 +392,7 @@ const createDirIfNotExist = async (client, folder) => {
|
||||||
console.debug(`Noting missing subdirectory: ${folder}`)
|
console.debug(`Noting missing subdirectory: ${folder}`)
|
||||||
|
|
||||||
// first, create the parent directory (if required)
|
// first, create the parent directory (if required)
|
||||||
if (!await createDirIfNotExist(client, path.dirname(folder))) {
|
if (!await this.createDirIfNotExist(client, path.dirname(folder))) {
|
||||||
// if not created, we fail too :-/
|
// if not created, we fail too :-/
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -403,16 +425,16 @@ const createDirIfNotExist = async (client, folder) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* function [uploadDav]
|
* function [uploadDav]
|
||||||
* @returns [string] status
|
* @returns [string] status
|
||||||
*/
|
*/
|
||||||
const uploadDav = async (dirPath, filePath) => {
|
async uploadDav (dirPath, filePath) {
|
||||||
|
|
||||||
// connect to webdav
|
// connect to webdav
|
||||||
client = createClient(
|
const client = createClient(
|
||||||
config.webdav.server_url,
|
config.webdav.server_url,
|
||||||
{
|
{
|
||||||
username: config.webdav.username,
|
username: config.webdav.username,
|
||||||
|
@ -422,29 +444,26 @@ const uploadDav = async (dirPath, filePath) => {
|
||||||
|
|
||||||
// create directory if not exists
|
// create directory if not exists
|
||||||
console.debug(`[dav-upload] Loading ${dirPath}`)
|
console.debug(`[dav-upload] Loading ${dirPath}`)
|
||||||
if (!await createDirIfNotExist(client, dirPath)) {
|
if (!await this.createDirIfNotExist(client, dirPath)) {
|
||||||
console.error(`[dav-upload] Could not upload ${path.basename(filePath)} :(`)
|
console.error(`[dav-upload] Could not upload ${path.basename(filePath)} :(`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// upload a file
|
// upload a file
|
||||||
console.debug('Uploading file')
|
console.debug('Uploading file')
|
||||||
outStream = client.createWriteStream(
|
const outStream = client.createWriteStream(
|
||||||
path.join(dirPath, path.basename(filePath))
|
path.join(dirPath, path.basename(filePath))
|
||||||
)
|
)
|
||||||
outStream.on('finish', () => console.debug('Uploaded successfully.'))
|
outStream.on('finish', () => console.debug('Uploaded successfully.'))
|
||||||
|
|
||||||
inStream = fs.createReadStream(filePath)
|
const inStream = fs.createReadStream(filePath)
|
||||||
.pipe(outStream)
|
.pipe(outStream)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
fetchFromMedium,
|
Seance
|
||||||
pushToGhost,
|
|
||||||
mediumToGhost,
|
|
||||||
generateUserData,
|
|
||||||
checkScissors,
|
|
||||||
uploadDav
|
|
||||||
}
|
}
|
||||||
|
|
19
index.js
19
index.js
|
@ -11,12 +11,7 @@ const yaml = require('js-yaml')
|
||||||
const config = require('./config')
|
const config = require('./config')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
fetchFromMedium,
|
Seance,
|
||||||
pushToGhost,
|
|
||||||
mediumToGhost,
|
|
||||||
generateUserData,
|
|
||||||
checkScissors,
|
|
||||||
uploadDav,
|
|
||||||
} = require ('./functions')
|
} = require ('./functions')
|
||||||
|
|
||||||
program
|
program
|
||||||
|
@ -120,7 +115,7 @@ program.command('fetch-medium <post_url>')
|
||||||
.alias('fetch')
|
.alias('fetch')
|
||||||
.description('fetch a Medium post')
|
.description('fetch a Medium post')
|
||||||
.action((post_url) => {
|
.action((post_url) => {
|
||||||
fetchFromMedium(post_url)
|
new Seance().fetchFromMedium(post_url)
|
||||||
.then((post) => {
|
.then((post) => {
|
||||||
console.info(`"${post.title}" fetched successfully.`)
|
console.info(`"${post.title}" fetched successfully.`)
|
||||||
})
|
})
|
||||||
|
@ -130,20 +125,20 @@ program.command('push-ghost <file>')
|
||||||
.alias('push')
|
.alias('push')
|
||||||
.description('push a downloaded Medium post to Ghost')
|
.description('push a downloaded Medium post to Ghost')
|
||||||
.action((file) => {
|
.action((file) => {
|
||||||
pushToGhost(file);
|
new Seance().pushToGhost(file);
|
||||||
});
|
});
|
||||||
|
|
||||||
program.command('medium-to-ghost <mediumUrl>')
|
program.command('medium-to-ghost <mediumUrl>')
|
||||||
.alias('import')
|
.alias('import')
|
||||||
.description('copy a Medium file over to Ghost')
|
.description('copy a Medium file over to Ghost')
|
||||||
.action((mediumUrl) => {
|
.action((mediumUrl) => {
|
||||||
pushToGhost(mediumUrl);
|
new Seance().pushToGhost(mediumUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
program.command('create-user <username> <email>')
|
program.command('create-user <username> <email>')
|
||||||
.description('create ghost-import.json to import Medium user to Ghost')
|
.description('create ghost-import.json to import Medium user to Ghost')
|
||||||
.action(async (username, email) => {
|
.action(async (username, email) => {
|
||||||
const jsonOut = await generateUserData(username, email)
|
const jsonOut = await new Seance().generateUserData(username, email)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log(`Error: ${err.error}`)
|
console.log(`Error: ${err.error}`)
|
||||||
return
|
return
|
||||||
|
@ -162,13 +157,13 @@ program.command('webdav-test <file>')
|
||||||
current_date.getUTCMonth().toString(),
|
current_date.getUTCMonth().toString(),
|
||||||
'test' // TODO: replace with article slug
|
'test' // TODO: replace with article slug
|
||||||
)
|
)
|
||||||
uploadDav(dir_path, file);
|
new Seance().uploadDav(dir_path, file);
|
||||||
});
|
});
|
||||||
|
|
||||||
program.command('check-scissors <file>')
|
program.command('check-scissors <file>')
|
||||||
.description('[test command] check if an image matches the set separator')
|
.description('[test command] check if an image matches the set separator')
|
||||||
.action(async (file) => {
|
.action(async (file) => {
|
||||||
console.log(await checkScissors(file))
|
console.log(await new Seance().checkScissors(file))
|
||||||
})
|
})
|
||||||
|
|
||||||
program.parse(process.argv)
|
program.parse(process.argv)
|
||||||
|
|
Loading…
Reference in a new issue