Skip to content

Commit

Permalink
chore: script to import user tracks to another user (#759)
Browse files Browse the repository at this point in the history
## Goal

Help Robert (and other users) import his playlists to his new account.

## Important

- the list of playlists of the destination user will be overwritten
   => existing tracks (if any) on that profile may end up in old playlists.
- on each imported track, the number of plays, reposts and likes will be reset to zero.

## Usage in development

```sh
$ . ./env-vars-testing-local.sh
$ node scripts/import-from-prod.js adrien # imports 21 tracks from openwhyd.com/adrien --> local id: 4d94501d1f78ac091dbc9b4d
$ node scripts/import-posts.js 4d94501d1f78ac091dbc9b4d 000000000000000000000002 # copy to local id: 000000000000000000000002
```

## Usage in production

```sh
$ . env-vars-local.sh
$ node scripts/import-posts.js 5f0f0d701125f9d3afca1bcc 6591c8dd0890c870388d2508 # imports all posts from https://openwhyd.org/u/5f0f0d701125f9d3afca1bcc
```
  • Loading branch information
adrienjoly committed Jan 4, 2024
1 parent 8abcf9e commit 57c66a6
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 0 deletions.
1 change: 1 addition & 0 deletions env-vars-testing-local.sh
@@ -1,3 +1,4 @@
. ./env-vars-testing.sh
. ./.env-local # helpful to store secrets, e.g. credentials for auth0 development tenant
export MONGODB_PORT=27117
export URL_PREFIX="http://localhost:8080"
118 changes: 118 additions & 0 deletions scripts/import-posts.js
@@ -0,0 +1,118 @@
// @ts-check

// Import tracks from a user into another openwhyd account.
//
// IMPORTANT:
// - the list of playlists of the destination user will be overwritten
// => existing tracks (if any) on that profile may end up in old playlists.
// - on each imported track, the number of plays, reposts and likes will be reset to zero.
//
// Usage in production:
// $ . env-vars-local.sh
// $ node scripts/import-posts.js 5f0f0d701125f9d3afca1bcc 6591c8dd0890c870388d2508 # imports all posts from https://openwhyd.org/u/5f0f0d701125f9d3afca1bcc

const assert = require('node:assert');
const request = require('request');
const mongodb = require('mongodb');

const ObjectId = (id) => new mongodb.ObjectId(id);

// Parameters
const fromUserId = process.argv[2];
const toUserId = process.argv[3];
assert.ok(
fromUserId && toUserId,
'Usage: $ node scripts/import-posts.js <from_user_id> <to_user_id>',
);

// Environment
const {
URL_PREFIX,
MONGODB_HOST,
MONGODB_PORT,
MONGODB_DATABASE,
MONGODB_USER,
MONGODB_PASS,
} = process.env;
const authStr =
MONGODB_USER && MONGODB_PASS
? encodeURIComponent(MONGODB_USER) +
':' +
encodeURIComponent(MONGODB_PASS) +
'@'
: '';
const url = `mongodb://${authStr}${MONGODB_HOST}:${MONGODB_PORT}`;
const dbName = MONGODB_DATABASE;

const connectToDb = ({ url, dbName }) => {
const client = new mongodb.MongoClient(url);
return { db: client.db(dbName), client };
};

const fetchUserProfile = ({ userId }) =>
new Promise((resolve, reject) => {
const url = `${URL_PREFIX}/api/user/${userId}?format=json`;
console.warn(`fetching profile from ${url} ...`);
request(url, (err, _, body) =>
err ? reject(err) : resolve(JSON.parse(body)),
);
});

const updateUser = ({ db, user }) => {
const { _id, ...userData } = user;
return db
.collection('user')
.updateOne({ _id: ObjectId(_id) }, { $set: { ...userData } });
};

/** Returns list of posts, from most recent to oldest */
const fetchUserPosts = ({ userId }) =>
new Promise((resolve, reject) => {
const limit = 99999999;
const url = `${URL_PREFIX}/u/${userId}?format=json&limit=${limit}`;
console.warn(`fetching posts from ${url} ...`);
request(url, (err, _, body) =>
err
? reject(err)
: resolve({
posts: JSON.parse(body),
}),
);
});

const insertPosts = ({ db, posts, postOverrides }) => {
const postsToImport = posts
.reverse() // _id will be regenerated chronologically => sort posts from oldest to most recent
.map(({ _id, ...post }) => ({
...post,
nbP: 0,
nbR: 0,
lov: [],
...postOverrides, // may include uId and uNm
}));
return db.collection('post').insertMany(postsToImport);
};

(async () => {
console.warn(`connecting to ${MONGODB_HOST}:${MONGODB_PORT}/${dbName} ...`);
const { db, client } = await connectToDb({ url, dbName });
const { pl } = await fetchUserProfile({ userId: fromUserId });
const { name: toUserName } = await fetchUserProfile({ userId: toUserId });
console.warn(
`about to import posts of ${pl.length} playlists, from user ${fromUserId} to ${toUserId} (${toUserName})`,
);
await new Promise((resolve) => setTimeout(resolve, 5000)); // give user some time to Ctrl-C
await updateUser({ db, user: { _id: toUserId, pl } });
const { posts } = await fetchUserPosts({ userId: fromUserId });
console.warn(`inserting ${posts.length} posts...`);
await insertPosts({
db,
posts,
postOverrides: { uId: toUserId, uNm: toUserName },
});
client.close();
console.warn(`✅ ${posts.length} posts imported to ${toUserId}`);
})().catch((err) => {
console.error(err);
process.exit(1);
});

0 comments on commit 57c66a6

Please sign in to comment.