Skip to content
This repository was archived by the owner on Sep 10, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 18 additions & 19 deletions src/instagram-to-bluesky.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import fs from "fs";

import {
main,
formatDuration,
Expand All @@ -9,7 +7,7 @@
import { BlueskyClient } from "./bluesky/bluesky";
import { ImagesEmbedImpl, VideoEmbedImpl } from "./bluesky/index";
import { logger } from "./logger/logger";
import { InstagramMediaProcessor, ImageMediaProcessResultImpl } from "./media";
import { InstagramMediaProcessor, ImageMediaProcessResultImpl, readJsonFile } from "./media";

import type { InstagramExportedPost } from "./media/InstagramExportedPost";

Expand Down Expand Up @@ -66,6 +64,7 @@
process: mockProcess,
})),
decodeUTF8: jest.fn((x) => x),
readJsonFile: jest.fn(),
ImageMediaProcessResultImpl: actual.ImageMediaProcessResultImpl,
VideoMediaProcessResultImpl: actual.VideoMediaProcessResultImpl
};
Expand Down Expand Up @@ -103,9 +102,9 @@
const mockReadFileSync = (mockValue) => {
return (path) => {
if (path.endsWith('reels.json')) {
return JSON.stringify({"ig_reels_media": mockValue})
return JSON.parse(JSON.stringify({ "ig_reels_media": mockValue }))
}
return JSON.stringify(mockValue)
return JSON.parse(JSON.stringify(mockValue));
}
};

Expand Down Expand Up @@ -135,7 +134,7 @@
],
},
];
(fs.readFileSync as jest.Mock).mockImplementation(mockReadFileSync(mockValue));
(readJsonFile as jest.Mock).mockImplementation(mockReadFileSync(mockValue));

// Reset BlueskyClient mock
jest.mocked(BlueskyClient).mockClear();
Expand Down Expand Up @@ -172,7 +171,7 @@
],
};

(fs.readFileSync as jest.Mock).mockImplementation(mockReadFileSync([mockPost]));
(readJsonFile as jest.Mock).mockImplementation(mockReadFileSync([mockPost]));

await main();

Expand Down Expand Up @@ -200,7 +199,7 @@
],
};

(fs.readFileSync as jest.Mock).mockImplementation(mockReadFileSync([oldPost]));
(readJsonFile as jest.Mock).mockImplementation(mockReadFileSync([oldPost]));

await main();

Expand All @@ -223,7 +222,7 @@
],
};

(fs.readFileSync as jest.Mock).mockImplementation(mockReadFileSync([futurePost]));
(readJsonFile as jest.Mock).mockImplementation(mockReadFileSync([futurePost]));

await main();

Expand All @@ -247,7 +246,7 @@
],
};

(fs.readFileSync as jest.Mock).mockImplementation(mockReadFileSync([exactMinDatePost]));
(readJsonFile as jest.Mock).mockImplementation(mockReadFileSync([exactMinDatePost]));

await main();

Expand Down Expand Up @@ -277,7 +276,7 @@
],
};

(fs.readFileSync as jest.Mock).mockImplementation(mockReadFileSync([exactMaxDatePost]));
(readJsonFile as jest.Mock).mockImplementation(mockReadFileSync([exactMaxDatePost]));

await main();

Expand Down Expand Up @@ -340,7 +339,7 @@
},
];

(fs.readFileSync as jest.Mock).mockImplementation(mockReadFileSync(posts));
(readJsonFile as jest.Mock).mockImplementation(mockReadFileSync(posts));

await main();

Expand Down Expand Up @@ -391,7 +390,7 @@
},
];

(fs.readFileSync as jest.Mock).mockImplementation(mockReadFileSync(posts));
(readJsonFile as jest.Mock).mockImplementation(mockReadFileSync(posts));

await main();

Expand All @@ -416,15 +415,15 @@
media: [{ title: "Invalid Media" }],
};

(fs.readFileSync as jest.Mock).mockImplementation(mockReadFileSync([invalidPost]));
(readJsonFile as jest.Mock).mockImplementation(mockReadFileSync([invalidPost]));

await main();

expect(logger.warn).toHaveBeenCalledWith("Skipping post - No date");
});

test("should handle file reading errors", async () => {
(fs.readFileSync as jest.Mock).mockImplementation(() => {
(readJsonFile as jest.Mock).mockImplementation(() => {
throw new Error("File read error");
});

Expand All @@ -443,7 +442,7 @@
],
};

(fs.readFileSync as jest.Mock).mockImplementation(mockReadFileSync([mockPost]));
(readJsonFile as jest.Mock).mockImplementation(mockReadFileSync([mockPost]));
jest.mocked(BlueskyClient).prototype.createPost = jest
.fn()
.mockRejectedValue(new Error("Post failed"));
Expand All @@ -469,7 +468,7 @@
],
};

(fs.readFileSync as jest.Mock).mockImplementation(mockReadFileSync([mockPost]));
(readJsonFile as jest.Mock).mockImplementation(mockReadFileSync([mockPost]));

await main();

Expand Down Expand Up @@ -507,7 +506,7 @@
],
};

(fs.readFileSync as jest.Mock).mockImplementation(mockReadFileSync([mockPost]));
(readJsonFile as jest.Mock).mockImplementation(mockReadFileSync([mockPost]));
await main();

expect(jest.mocked(BlueskyClient)).toHaveBeenCalled();
Expand Down Expand Up @@ -550,7 +549,7 @@
],
};

(fs.readFileSync as jest.Mock).mockImplementation(mockReadFileSync([mockPost]));
(readJsonFile as jest.Mock).mockImplementation(mockReadFileSync([mockPost]));

const embeddedMedia = mockPost.media.map(() => ({
getType: () => "image",
Expand Down Expand Up @@ -651,7 +650,7 @@
describe("uploadMediaAndEmbed", () => {
test("should handle multiple images correctly", async () => {
const mockBluesky = {
uploadMedia: jest.fn().mockImplementation((_, __) =>

Check warning on line 653 in src/instagram-to-bluesky.test.ts

View workflow job for this annotation

GitHub Actions / lint

'__' is defined but never used

Check warning on line 653 in src/instagram-to-bluesky.test.ts

View workflow job for this annotation

GitHub Actions / lint

'_' is defined but never used
Promise.resolve({
ref: `test-blob-ref-${mockBluesky.uploadMedia.mock.calls.length}`,
mimeType: "image/jpeg",
Expand Down
29 changes: 14 additions & 15 deletions src/instagram-to-bluesky.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import FS from "fs";
import path from "path";

import { BlobRef } from "@atproto/api";
Expand All @@ -20,6 +19,8 @@ import {
decodeUTF8,
InstagramMediaProcessor,
InstagramExportedPost,
readJsonFile,
sortPostsByCreationTime,
} from "./media";

const API_RATE_LIMIT_DELAY = 3000; // https://docs.bsky.app/docs/advanced-guides/rate-limits
Expand Down Expand Up @@ -171,15 +172,18 @@ export async function main() {
);
}

// Read instagram posts JSON file as raw buffer data.
const instaPostsFileBuffer: Buffer = FS.readFileSync(postsJsonPath);
const instaReelsFileBuffer: Buffer = FS.readFileSync(reelsJsonPath);
// Read posts and reels data
const instaPostsData = readJsonFile(postsJsonPath, 'No posts found. The file path may have changed - please update the env to point to the new folder containing posts_1.json');
const reelsJsonData = readJsonFile(reelsJsonPath, 'No reels found. Some accounts don\'t have reels, or the folder may have changed.');

// Decode raw JSON data into an object.
const allInstaPosts: InstagramExportedPost[] = decodeUTF8([].concat(
JSON.parse(instaPostsFileBuffer.toString()),
JSON.parse(instaReelsFileBuffer.toString())['ig_reels_media']
));
// Extract reels data (some users don't have reels)
const instaReelsData = reelsJsonData['ig_reels_media'] || [];

// Decode raw JSON data into an object
const allInstaPosts: InstagramExportedPost[] = decodeUTF8([
...instaPostsData,
...instaReelsData
]);

// Initialize counters for posts and media.
let importedPosts = 0;
Expand All @@ -188,12 +192,7 @@ export async function main() {

// Sort instagram posts by creation timestamp
if (allInstaPosts && allInstaPosts.length > 0) {
const sortedPosts = allInstaPosts.sort((a, b) => {
// Get the first posts media and compare timestamps.
const ad = a.media[0].creation_timestamp;
const bd = b.media[0].creation_timestamp;
return ad - bd;
});
const sortedPosts = allInstaPosts.sort(sortPostsByCreationTime)

// Preprocess posts before transforming into a normalized format.
for (const post of sortedPosts) {
Expand Down
Empty file removed src/media/media.ts
Empty file.
Loading
Loading