diff --git a/src/cli.ts b/src/cli.ts index cc5c8c7..5408d0e 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -20,12 +20,16 @@ const DRY_RUN = !!getOption("dry-run") ?? false; const REPORT = (getOption("report") as string) ?? "text"; const PLATFORMS = ((getOption("platforms") as string)?.split(",") as PlatformId[]) ?? undefined; -const PLATFORM = (getOption("platform") as string as PlatformId) ?? undefined; const FOLDERS = (getOption("folders") as string)?.split(",") ?? undefined; -const FOLDER = (getOption("folder") as string) ?? undefined; const DATE = (getOption("date") as string) ?? undefined; const STATUS = (getOption("status") as PostStatus) ?? undefined; +let PLATFORM = (getOption("platform") as string as PlatformId) ?? undefined; +let FOLDER = (getOption("folder") as string) ?? undefined; +const POST = (getOption("post") as string) ?? undefined; +if (POST) { + [FOLDER, PLATFORM] = POST.split(":") as [string, PlatformId]; +} // utilities function getOption(key: string): boolean | string | null { @@ -237,12 +241,12 @@ async function main() { `${cmd} get-platforms [--platforms=xxx,xxx]`, `${cmd} get-folder --folder=xxx`, `${cmd} get-folders [--folders=xxx,xxx]`, - `${cmd} get-post --folder=xxx --platform=xxx`, + `${cmd} get-post --post=xxx:xxx`, `${cmd} get-posts [--status=xxx] [--folders=xxx,xxx] [--platforms=xxx,xxx] `, - `${cmd} prepare-post --folder=xxx --platform=xxx`, - `${cmd} schedule-post --folder=xxx --platform=xxx --date=xxxx-xx-xx `, + `${cmd} prepare-post --post=xxx:xxx`, + `${cmd} schedule-post --post=xxx:xxx --date=xxxx-xx-xx `, `${cmd} schedule-posts [--folders=xxx,xxx] [--platforms=xxx,xxx] --date=xxxx-xx-xx`, - `${cmd} publish-post --folder=xxx --platform=xxx [--dry-run]`, + `${cmd} publish-post --post=xxx:xxx [--dry-run]`, `${cmd} publish-posts [--folders=xxx,xxx] [--platforms=xxx,xxx]`, "\n# feed planning:", `${cmd} prepare-posts [--folders=xxx,xxx] [--platforms=xxx,xxx]`, diff --git a/src/models/Folder.ts b/src/models/Folder.ts index ecce2e0..1690520 100644 --- a/src/models/Folder.ts +++ b/src/models/Folder.ts @@ -17,7 +17,7 @@ export default class Folder { files?: FileInfo[]; constructor(path: string) { - this.id = path; + this.id = path.replace(/^\//, "").split("/").slice(1).join("/"); this.path = path; } diff --git a/src/models/Platform.ts b/src/models/Platform.ts index 5d428a4..0841424 100644 --- a/src/models/Platform.ts +++ b/src/models/Platform.ts @@ -16,7 +16,8 @@ export default class Platform { active: boolean = false; id: PlatformId = PlatformId.UNKNOWN; defaultBody: string = "Fairpost feed"; - postFile: string = "post.json"; + assetsFolder: string = "_fairpost"; + postFileName: string = "post.json"; /** * Return a small report for this feed @@ -71,15 +72,6 @@ export default class Platform { return false; } - /** - * getAssetsFolderName - * @returns the relative path to a folder used - * to store assets for a post of this platform - */ - assetsFolder(): string { - return "_" + this.id; - } - /** * getPostFilePath * @param folder the folder for the new or existing post @@ -87,7 +79,7 @@ export default class Platform { * to store data for a post of this platform */ getPostFilePath(folder: Folder): string { - return folder.path + "/" + this.assetsFolder() + "/" + this.postFile; + return folder.path + "/" + this.assetsFolder + "/" + this.postFileName; } /** @@ -152,7 +144,7 @@ export default class Platform { post.purgeFiles(); const files = await folder.getFiles(); files.forEach((file) => { - if (!post.ignoreFiles.includes(file.name)) { + if (!post.ignoreFiles?.includes(file.name)) { post.putFile(file); } }); diff --git a/src/models/Post.ts b/src/models/Post.ts index 50142e3..affc875 100644 --- a/src/models/Post.ts +++ b/src/models/Post.ts @@ -43,8 +43,10 @@ export default class Post { this.scheduled = this.scheduled ? new Date(this.scheduled) : undefined; this.published = this.published ? new Date(this.published) : undefined; this.ignoreFiles = this.ignoreFiles ?? []; + } else { + this.files = []; // allow optional once strict } - const assetsPath = this.getFilePath(platform.assetsFolder()); + const assetsPath = this.getFilePath(platform.assetsFolder); if (!fs.existsSync(assetsPath)) { fs.mkdirSync(assetsPath, { recursive: true }); } @@ -98,7 +100,8 @@ export default class Post { /** * Check body for title, #tags, @mentions and %geo - * and store those in separate fields instead + * and store those in separate fields instead. + * Does not save. */ decompileBody() { const lines = this.body.split("\n"); @@ -159,7 +162,7 @@ export default class Post { } /** - * Create a body containing the given arguments + * Create a body containing the given arguments. * @param parts - any of 'title','body','tags','mentions','geo' * prepending a ! to every part removes those parts from the default array instead. * @returns compiled body @@ -236,6 +239,7 @@ export default class Post { /** * @param group - the name of the group for which to remove the files + * Does not save. */ removeFiles(group: string) { this.files = this.files.filter((file) => file.group !== group); @@ -254,7 +258,8 @@ export default class Post { } /** - * Remove all the files that do not exist (anymore) + * Remove all the files that do not exist (anymore). + * Does not save. */ purgeFiles() { this.getFiles().forEach((file) => { @@ -280,7 +285,8 @@ export default class Post { } /** - * reindex file ordering to remove doubles + * reindex file ordering to remove doubles. + * Does not save. */ reorderFiles() { this.files @@ -307,7 +313,8 @@ export default class Post { } /** - * @param file - the fileinfo to add or replace + * @param file - the fileinfo to add or replace. + * Does not save. */ putFile(file: FileInfo) { const oldFile = this.files.find( @@ -321,13 +328,19 @@ export default class Post { } /** - * @param name the name of the file to remove + * @param name the name of the file to remove. + * Does not save. */ removeFile(name: string) { this.files = this.files.filter((file) => file.name !== name); } /** + * Replace the file info of file `search` for new info + * gathered for file `replace`. Keeps the oldfile order + * and sets replace.original to search.name + * + * Does not save. * @param search - the name of the file to replace * @param replace - the name of the file to replace it with * @returns the info of the replaced file diff --git a/src/platforms/Ayrshare/AsFacebook.ts b/src/platforms/Ayrshare/AsFacebook.ts index b4f18dd..9a7e099 100644 --- a/src/platforms/Ayrshare/AsFacebook.ts +++ b/src/platforms/Ayrshare/AsFacebook.ts @@ -10,6 +10,8 @@ import Post from "../../models/Post"; */ export default class AsFacebook extends Ayrshare { id: PlatformId = PlatformId.ASFACEBOOK; + assetsFolder = "_asfacebook"; + postFileName = "post.json"; constructor() { super(); @@ -21,7 +23,7 @@ export default class AsFacebook extends Ayrshare { // facebook : max 10mb images for (const file of post.getFiles("image")) { const src = file.name; - const dst = this.assetsFolder() + "/facebook-" + src; + const dst = this.assetsFolder + "/facebook-" + src; if (file.size / (1024 * 1024) >= 10) { console.log("Resizing " + src + " for facebook .."); await sharp(post.getFilePath(src)) diff --git a/src/platforms/Ayrshare/AsInstagram.ts b/src/platforms/Ayrshare/AsInstagram.ts index d7ac93e..cb7ef8c 100644 --- a/src/platforms/Ayrshare/AsInstagram.ts +++ b/src/platforms/Ayrshare/AsInstagram.ts @@ -11,6 +11,8 @@ import Post from "../../models/Post"; */ export default class AsInstagram extends Ayrshare { id = PlatformId.ASINSTAGRAM; + assetsFolder = "_asinstagram"; + postFileName = "post.json"; constructor() { super(); @@ -35,7 +37,7 @@ export default class AsInstagram extends Ayrshare { if (file.width > 1440) { Logger.trace("Resizing " + src + " for instagram .."); const dst = - this.assetsFolder() + "/instagram-" + file.basename + ".JPEG"; + this.assetsFolder + "/instagram-" + file.basename + ".JPEG"; await sharp(post.getFilePath(src)) .resize({ width: 1440, diff --git a/src/platforms/Ayrshare/AsLinkedIn.ts b/src/platforms/Ayrshare/AsLinkedIn.ts index 4ff06bf..7e81a62 100644 --- a/src/platforms/Ayrshare/AsLinkedIn.ts +++ b/src/platforms/Ayrshare/AsLinkedIn.ts @@ -11,6 +11,8 @@ import Post from "../../models/Post"; */ export default class AsLinkedIn extends Ayrshare { id = PlatformId.ASLINKEDIN; + assetsFolder = "_aslinkedin"; + postFileName = "post.json"; constructor() { super(); @@ -30,7 +32,7 @@ export default class AsLinkedIn extends Ayrshare { // linkedin: max 5mb images for (const file of post.getFiles("image")) { const src = file.name; - const dst = this.assetsFolder() + "/linkedin-" + src; + const dst = this.assetsFolder + "/linkedin-" + src; if (file.size / (1024 * 1024) >= 5) { Logger.trace("Resizing " + src + " for linkedin .."); await sharp(post.getFilePath(src)) diff --git a/src/platforms/Ayrshare/AsReddit.ts b/src/platforms/Ayrshare/AsReddit.ts index 03139fa..b8cc17a 100644 --- a/src/platforms/Ayrshare/AsReddit.ts +++ b/src/platforms/Ayrshare/AsReddit.ts @@ -9,6 +9,9 @@ import Storage from "../../services/Storage"; */ export default class AsReddit extends Ayrshare { id = PlatformId.ASREDDIT; + assetsFolder = "_asreddit"; + postFileName = "post.json"; + SUBREDDIT: string; constructor() { diff --git a/src/platforms/Ayrshare/AsTikTok.ts b/src/platforms/Ayrshare/AsTikTok.ts index 8527bf0..3149a4e 100644 --- a/src/platforms/Ayrshare/AsTikTok.ts +++ b/src/platforms/Ayrshare/AsTikTok.ts @@ -8,6 +8,8 @@ import Post from "../../models/Post"; */ export default class AsTikTok extends Ayrshare { id = PlatformId.ASTIKTOK; + assetsFolder = "_astiktok"; + postFileName = "post.json"; constructor() { super(); diff --git a/src/platforms/Ayrshare/AsTwitter.ts b/src/platforms/Ayrshare/AsTwitter.ts index e7d14bf..eac5d31 100644 --- a/src/platforms/Ayrshare/AsTwitter.ts +++ b/src/platforms/Ayrshare/AsTwitter.ts @@ -11,6 +11,8 @@ import Post from "../../models/Post"; */ export default class AsTwitter extends Ayrshare { id = PlatformId.ASTWITTER; + assetsFolder = "_astwitter"; + postFileName = "post.json"; constructor() { super(); @@ -26,7 +28,7 @@ export default class AsTwitter extends Ayrshare { // twitter: max 5mb images for (const file of post.getFiles("image")) { const src = file.name; - const dst = this.assetsFolder() + "/twitter-" + src; + const dst = this.assetsFolder + "/twitter-" + src; if (file.size / (1024 * 1024) >= 5) { Logger.trace("Resizing " + src + " for twitter .."); await sharp(post.getFilePath(src)) diff --git a/src/platforms/Ayrshare/AsYouTube.ts b/src/platforms/Ayrshare/AsYouTube.ts index d4b16f2..5ce793a 100644 --- a/src/platforms/Ayrshare/AsYouTube.ts +++ b/src/platforms/Ayrshare/AsYouTube.ts @@ -8,6 +8,8 @@ import Post from "../../models/Post"; */ export default class AsYouTube extends Ayrshare { id = PlatformId.ASYOUTUBE; + assetsFolder = "_asyoutube"; + postFileName = "post.json"; constructor() { super(); diff --git a/src/platforms/Facebook/Facebook.ts b/src/platforms/Facebook/Facebook.ts index 67be349..8bc41a7 100644 --- a/src/platforms/Facebook/Facebook.ts +++ b/src/platforms/Facebook/Facebook.ts @@ -20,6 +20,9 @@ import Storage from "../../services/Storage"; */ export default class Facebook extends Platform { id: PlatformId = PlatformId.FACEBOOK; + assetsFolder = "_facebook"; + postFileName = "post.json"; + api: FacebookApi; auth: FacebookAuth; @@ -55,7 +58,7 @@ export default class Facebook extends Platform { if (file.size / (1024 * 1024) >= 4) { Logger.trace("Resizing " + file.name + " for facebook .."); const src = file.name; - const dst = this.assetsFolder() + "/facebook-" + file.name; + const dst = this.assetsFolder + "/facebook-" + file.name; await sharp(post.getFilePath(src)) .resize({ width: 1200, diff --git a/src/platforms/Instagram/Instagram.ts b/src/platforms/Instagram/Instagram.ts index b79db24..e79f66d 100644 --- a/src/platforms/Instagram/Instagram.ts +++ b/src/platforms/Instagram/Instagram.ts @@ -19,6 +19,9 @@ import Post from "../../models/Post"; */ export default class Instagram extends Platform { id: PlatformId = PlatformId.INSTAGRAM; + assetsFolder = "_instagram"; + postFileName = "post.json"; + api: InstagramApi; auth: InstagramAuth; @@ -62,7 +65,7 @@ export default class Instagram extends Platform { if (file.width > 1440) { const src = file.name; const dst = - this.assetsFolder() + "/instagram-" + file.basename + ".JPEG"; + this.assetsFolder + "/instagram-" + file.basename + ".JPEG"; Logger.trace("Resizing " + src + " for instagram .."); await sharp(post.getFilePath(src)) .resize({ diff --git a/src/platforms/LinkedIn/LinkedIn.ts b/src/platforms/LinkedIn/LinkedIn.ts index 352f191..00a6011 100644 --- a/src/platforms/LinkedIn/LinkedIn.ts +++ b/src/platforms/LinkedIn/LinkedIn.ts @@ -15,6 +15,9 @@ import Storage from "../../services/Storage"; export default class LinkedIn extends Platform { id: PlatformId = PlatformId.LINKEDIN; + assetsFolder = "_linkedin"; + postFileName = "post.json"; + api: LinkedInApi; auth: LinkedInAuth; @@ -66,7 +69,7 @@ export default class LinkedIn extends Platform { // linkedin: max 5mb images for (const file of post.getFiles("image")) { const src = file.name; - const dst = this.assetsFolder() + "/linkedin-" + src; + const dst = this.assetsFolder + "/linkedin-" + src; if (file.size / (1024 * 1024) >= 5) { Logger.trace("Resizing " + src + " for linkedin .."); await sharp(post.getFilePath(src)) diff --git a/src/platforms/Reddit/Reddit.ts b/src/platforms/Reddit/Reddit.ts index 2a6f3fd..acb21da 100644 --- a/src/platforms/Reddit/Reddit.ts +++ b/src/platforms/Reddit/Reddit.ts @@ -17,6 +17,8 @@ import { XMLParser } from "fast-xml-parser"; */ export default class Reddit extends Platform { id = PlatformId.REDDIT; + assetsFolder = "_reddit"; + postFileName = "post.json"; SUBREDDIT: string; api: RedditApi; @@ -68,7 +70,7 @@ export default class Reddit extends Platform { const src = file.name; if (file.width > 3000) { Logger.trace("Resizing " + src + " for reddit .."); - const dst = this.assetsFolder() + "/reddit-" + file.basename + ".jpg"; + const dst = this.assetsFolder + "/reddit-" + file.basename + ".jpg"; await sharp(post.getFilePath(src)) .resize({ width: 3000, diff --git a/src/platforms/Twitter/Twitter.ts b/src/platforms/Twitter/Twitter.ts index e51fc98..cc2a411 100644 --- a/src/platforms/Twitter/Twitter.ts +++ b/src/platforms/Twitter/Twitter.ts @@ -14,6 +14,9 @@ import TwitterAuth from "./TwitterAuth"; */ export default class Twitter extends Platform { id = PlatformId.TWITTER; + assetsFolder = "_twitter"; + postFileName = "post.json"; + auth: TwitterAuth; constructor() { @@ -61,7 +64,7 @@ export default class Twitter extends Platform { // twitter: max 5mb images for (const file of post.getFiles("image")) { const src = file.name; - const dst = this.assetsFolder() + "/twitter-" + src; + const dst = this.assetsFolder + "/twitter-" + src; if (file.size / (1024 * 1024) >= 5) { Logger.trace("Resizing " + src + " for twitter .."); await sharp(post.getFilePath(src))