Flow and modularization refactor. Added duplicates option and applying limit after fetch.

This commit is contained in:
DebaucheryLibrarian 2024-09-11 05:16:53 +02:00
parent 1b0bd3a5a4
commit df3421639f
13 changed files with 141 additions and 119 deletions

110
app.js
View File

@ -6,55 +6,21 @@ const fs = require('fs-extra');
const yargs = require('yargs').argv; const yargs = require('yargs').argv;
const snoowrap = require('snoowrap'); const snoowrap = require('snoowrap');
const curateSubmissions = require('./curate/submissions.js');
const curateUser = require('./curate/user.js');
const methods = require('./methods/methods.js');
const interpolate = require('./interpolate.js');
const fetchItem = require('./fetchItem.js');
const fetchContent = require('./fetchContent.js');
const save = require('./save.js');
const textToStream = require('./textToStream.js');
const reddit = new snoowrap(config.reddit.api); const reddit = new snoowrap(config.reddit.api);
function saveProfileDetails(user) { const curateSubmissions = require('./curate/submissions.js');
if(config.library.profile.image) { const curateUser = require('./curate/user.js');
// pass profile image as item to interpolate extension variable
const filepath = interpolate(config.library.profile.image, user, null, {
url: user.profile.image
});
fetchItem(user.profile.image).then(stream => save(filepath, stream)).catch(error => { const methods = require('./methods/methods.js');
console.log('\x1b[33m%s\x1b[0m', `Could not save profile image for '${user.name}': ${error}`); const interpolate = require('./interpolate.js');
});
}
if(config.library.profile.description) { const fetchInfo = require('./fetch/info.js');
if(user.profile.description) { const fetchContent = require('./fetch/content.js');
const filepath = interpolate(config.library.profile.description, user);
const stream = textToStream(user.profile.description);
save(filepath, stream).catch(error => { const save = require('./save/save.js');
console.log('\x1b[33m%s\x1b[0m', `Could not save profile description for '${user.name}': ${error}`); const saveProfileDetails = require('./save/profileDetails.js');
});
} else {
console.log('\x1b[33m%s\x1b[0m', `No profile description for '${user.name}'`);
}
}
};
function getSubmissions(users, sort, limit) { const limit = yargs.limit || config.fetch.limit;
return users.reduce((chain, user) => {
return chain.then(acc => {
return reddit.getUser(user).getSubmissions({
sort: sort,
limit: limit
}).then(submissions => {
return acc.concat(submissions);
});
});
}, Promise.resolve([]));
};
if(!yargs.user && typeof yargs.users !== 'string') { if(!yargs.user && typeof yargs.users !== 'string') {
return console.log('\x1b[31m%s\x1b[0m', 'Please supply at least one user with --user=[user], or multiple users with --users=[user1,user2] or --user=[user1] --user=[user2]'); return console.log('\x1b[31m%s\x1b[0m', 'Please supply at least one user with --user=[user], or multiple users with --users=[user1,user2] or --user=[user1] --user=[user2]');
@ -62,50 +28,28 @@ if(!yargs.user && typeof yargs.users !== 'string') {
const users = yargs.users ? yargs.users.split(',') : [].concat(yargs.user); const users = yargs.users ? yargs.users.split(',') : [].concat(yargs.user);
users.forEach(user => { users.forEach(username => {
return Promise.resolve().then(() => { return Promise.resolve().then(() => {
// get reddit profile return reddit.getUser(username).fetch().then(curateUser);
return reddit.getUser(user).fetch().then(curateUser);
}).then(user => { }).then(user => {
return saveProfileDetails(user); return saveProfileDetails(user);
// get submissions }).then(user => {
}).catch(error => { return reddit.getUser(username).getSubmissions({
return console.log('\x1b[33m%s\x1b[0m', error); sort: yargs.sort || config.fetch.sort
}); }).then(submissions => ({
}); user,
submissions
/*
Promise.resolve().then(() => {
if(yargs.user || yargs.users) {
const users = yargs.users ? yargs.users.split(',') : [].concat(yargs.user);
return Promise.resolve().then(() => {
if(config.library.profile) {
return getProfiles(users);
}
}).then(() => {
return getSubmissions(users, yargs.sort || config.reddit.sort, yargs.limit === undefined ? config.reddit.limit : yargs.limit);
});
}
return Promise.reject('Please supply at least one user with one or multiple --user, or --users!');
}).then(submissions => {
return Promise.all(curate(submissions).reduce((acc, post) => {
if(post.host && methods[post.host.method]) {
acc = acc.concat(methods[post.host.method](post).then(content => {
post.content = content;
return post;
})); }));
} else { }).then(({user, submissions}) => {
console.log('\x1b[33m%s\x1b[0m', `"${post.title}": '${post.url}' not supported :(`); const posts = curateSubmissions(submissions).slice(0, limit);
}
return acc; return fetchInfo(posts).then(info => ({
}, [])); user,
}).then(posts => { posts
return fetchContent(posts); }));
}).then(({user, posts}) => {
return fetchContent(posts, user);
}).catch(error => { }).catch(error => {
return console.log('\x1b[31m%s\x1b[0m', error); return console.log(error);
});
}); });
*/

View File

@ -10,8 +10,8 @@ module.exports = {
video: '$postDate - $albumId - $postTitle/$itemIndex - $itemId$ext' video: '$postDate - $albumId - $postTitle/$itemIndex - $itemId$ext'
}, },
profile: { profile: {
image: 'profile$ext', image: '$userCreated - profile$ext',
description: 'profile ($userVerified$userVerifiedEmail$userGold)' description: '$userCreated - profile ($userVerified$userVerifiedEmail$userGold$profileOver18)'
}, },
booleans: { booleans: {
extracted: 'extracted-', extracted: 'extracted-',
@ -25,9 +25,12 @@ module.exports = {
indexOffset: 1, indexOffset: 1,
slashSubstitute: '#', slashSubstitute: '#',
}, },
reddit: { fetch: {
sort: 'top', sort: 'top',
limit: 1000, limit: 1000,
ignoreDuplicates: true
},
reddit: {
api: { api: {
userAgent: 'wat', userAgent: 'wat',
clientId: 'VPquALMpTGl3ag', clientId: 'VPquALMpTGl3ag',

View File

@ -1,10 +1,19 @@
'use strict'; 'use strict';
const config = require('config');
const dissectLink = require('../dissectLink.js'); const dissectLink = require('../dissectLink.js');
function curateSubmissions(submissions) { function curateSubmissions(submissions) {
return submissions.map((submission, index) => { const processed = new Set();
return {
return submissions.reduce((acc, submission, index) => {
if(config.fetch.ignoreDuplicates && processed.has(submission.url)) {
console.log('\x1b[33m%s\x1b[0m', `Ignoring cross-post or repost '${submission.title}' - ${submission.url}`);
return acc;
}
const curatedSubmission = {
id: submission.id, id: submission.id,
index: index, index: index,
title: submission.title, title: submission.title,
@ -16,7 +25,11 @@ function curateSubmissions(submissions) {
subreddit: submission.subreddit.display_name, subreddit: submission.subreddit.display_name,
host: dissectLink(submission.url) host: dissectLink(submission.url)
}; };
});
processed.add(submission.url);
return acc.concat(curatedSubmission);
}, []);
}; };
module.exports = curateSubmissions; module.exports = curateSubmissions;

View File

@ -3,8 +3,6 @@
const path = require('path'); const path = require('path');
function curateUser(user) { function curateUser(user) {
console.log(user);
return { return {
id: user.id, id: user.id,
name: user.name, name: user.name,

View File

@ -4,12 +4,12 @@ const fs = require('fs-extra');
const path = require('path'); const path = require('path');
const config = require('config'); const config = require('config');
const fetchItem = require('./fetchItem'); const fetchItem = require('./item.js');
const save = require('./save.js'); const interpolate = require('../interpolate.js');
const interpolate = require('./interpolate.js'); const save = require('../save/save.js');
const textToStream = require('./textToStream.js'); const textToStream = require('../save/textToStream.js');
module.exports = function(posts) { module.exports = function(posts, user) {
return Promise.all(posts.map(post => { return Promise.all(posts.map(post => {
return Promise.resolve().then(() => { return Promise.resolve().then(() => {
return Promise.all(post.content.items.map((item, index) => { return Promise.all(post.content.items.map((item, index) => {
@ -26,7 +26,7 @@ module.exports = function(posts) {
}).then(items => { }).then(items => {
return Promise.all(items.map(item => { return Promise.all(items.map(item => {
const type = item.type.split('/')[0]; const type = item.type.split('/')[0];
const filepath = post.content.album ? interpolate(config.library.album[type], post.user, post, item) : interpolate(config.library[type], post.user, post, item); const filepath = post.content.album ? interpolate(config.library.album[type], user, post, item) : interpolate(config.library[type], user, post, item);
return Promise.resolve().then(() => { return Promise.resolve().then(() => {
return fs.ensureDir(path.dirname(filepath)); return fs.ensureDir(path.dirname(filepath));

21
fetch/info.js Normal file
View File

@ -0,0 +1,21 @@
'use strict';
const methods = require('../methods/methods.js');
function fetchInfo(posts) {
return Promise.all(posts.reduce((acc, post) => {
if(post.host && methods[post.host.method]) {
acc = acc.concat(methods[post.host.method](post).then(content => {
post.content = content;
return post;
}));
} else {
console.log('\x1b[33m%s\x1b[0m', `Ignoring unsupported content '${post.title}' - ${post.url}`);
}
return acc;
}, []));
};
module.exports = fetchInfo;

View File

@ -64,7 +64,7 @@ function interpolate(pattern, user, post, item) {
$itemDate: dateFns.format(item.datetime, dateFormat), $itemDate: dateFns.format(item.datetime, dateFormat),
$itemIndex: item.index + config.library.indexOffset, $itemIndex: item.index + config.library.indexOffset,
$extracted: item.extracted ? config.library.booleans.extracted : '', $extracted: item.extracted ? config.library.booleans.extracted : '',
$ext: item.type ? extensions[item.type] : path.extname(url.parse(item.url).pathname) $ext: item.extension || (item.type ? extensions[item.type] : path.extname(url.parse(item.url).pathname))
}); });
} }

View File

@ -10,7 +10,7 @@ function imgurAlbum(post) {
'Authorization': `Client-ID ${config.methods.imgur.clientId}` 'Authorization': `Client-ID ${config.methods.imgur.clientId}`
} }
}).then(res => res.json()).then(res => { }).then(res => res.json()).then(res => {
const extract = config.patterns.album.extractSingleItem && res.data.images.length === 1; const extract = config.library.album.extractSingleItem && res.data.images.length === 1;
return { return {
album: extract ? null : { album: extract ? null : {

19
save.js
View File

@ -1,19 +0,0 @@
'use strict';
const fs = require('fs-extra');
function save(filepath, stream) {
const file = fs.createWriteStream(filepath);
return new Promise((resolve, reject) => {
stream.pipe(file).on('error', error => {
reject(error);
}).on('finish', () => {
console.log('\x1b[32m%s\x1b[0m', `Saved '${filepath}'`);
resolve(filepath);
});
});
};
module.exports = save;

38
save/profileDetails.js Normal file
View File

@ -0,0 +1,38 @@
'use strict';
const config = require('config');
const interpolate = require('../interpolate.js');
const fetchItem = require('../fetch/item.js');
const textToStream = require('./textToStream.js');
const save = require('./save.js');
function saveProfileDetails(user) {
if(config.library.profile.image) {
const filepath = interpolate(config.library.profile.image, user, null, {
// pass profile image as item to interpolate extension variable
url: user.profile.image
});
fetchItem(user.profile.image).then(stream => save(filepath, stream)).catch(error => {
console.log('\x1b[33m%s\x1b[0m', `Could not save profile image for '${user.name}': ${error}`);
});
}
if(config.library.profile.description) {
if(user.profile.description) {
const filepath = interpolate(config.library.profile.description, user);
const stream = textToStream(user.profile.description);
save(filepath, stream).catch(error => {
console.log('\x1b[33m%s\x1b[0m', `Could not save profile description for '${user.name}': ${error}`);
});
} else {
console.log('\x1b[33m%s\x1b[0m', `No profile description for '${user.name}'`);
}
}
return user;
};
module.exports = saveProfileDetails;

24
save/save.js Normal file
View File

@ -0,0 +1,24 @@
'use strict';
const fs = require('fs-extra');
const path = require('path');
function save(filepath, stream) {
return Promise.resolve().then(() => {
return fs.ensureDir(path.dirname(filepath));
}).then(() => {
const file = fs.createWriteStream(filepath);
return new Promise((resolve, reject) => {
stream.pipe(file).on('error', error => {
reject(error);
}).on('finish', () => {
console.log('\x1b[32m%s\x1b[0m', `Saved '${filepath}'`);
resolve(filepath);
});
});
});
};
module.exports = save;