-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.js
126 lines (118 loc) · 3.68 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
const request = require('request');
const fs = require('fs');
const Promise = require('bluebird');
const nodeId3 = require('node-id3');
const path = require('path');
const aws = require('aws-sdk');
const _ = require('lodash/fp');
const winston = require('winston');
const SoundCloud = require('./src/SoundCloud');
const Sanitizer = require('./src/Sanitizer');
const s3 = new aws.S3({ region: 'us-east-1' });
const bucket = process.env.SOUND_SYNC_BUCKET;
const maxSyncedTracks = process.env.MAX_SYNCED_TRACKS;
const listObjects = Promise.promisify(s3.listObjectsV2, { context: s3 });
const upload = Promise.promisify(s3.upload, { context: s3 });
const logger = winston.createLogger({
transports: [
new winston.transports.Console(),
],
});
function downloadArtwork(track, file) {
const thumb = track.artwork_url || track.user.avatar_url;
const url = thumb.replace('-large', '-t500x500');
const options = { url };
logger.info(`Downloading artwork from ${url}`);
return new Promise((resolve, reject) => {
const writeStream = fs.createWriteStream(file);
request.get(options).pipe(writeStream).on('close', () => {
resolve(file);
}).on('error', (error) => reject(error));
});
}
function writeMetadata(track, file, image) {
const tags = {
album: track.user.username,
artist: track.user.full_name,
title: track.title,
genre: track.genre,
year: String(track.release_year || new Date(track.created_at).getFullYear()),
image,
};
nodeId3.write(tags, file);
}
function saveTrack(track, file) {
const name = Sanitizer.getFilename(track);
const stream = fs.createReadStream(file);
return upload({
Bucket: bucket,
Key: `sound-sync/${name}.mp3`,
Body: stream,
});
}
// Lists all music files on s3
function s3ListObjects(token, collection = []) {
return listObjects({
Bucket: bucket,
Prefix: 'sound-sync/',
ContinuationToken: token,
}).then(({ IsTruncated, Contents, NextContinuationToken }) => {
const contents = collection.concat(Contents);
return IsTruncated
? s3ListObjects(NextContinuationToken, contents)
: contents;
});
}
function filterExistingTracks(tracks) {
return s3ListObjects().then((objects) => (
_.flow(
_.filter((track) => (
_.every((data) => {
const name = Sanitizer.getFilename(track);
return (data.Key.indexOf(name) === -1);
})(objects)
)),
_.take(maxSyncedTracks),
)(tracks)
));
}
function sync() {
return SoundCloud.getAllTracks()
.then((tracks) => {
logger.info(`There are ${tracks.length} tracks on SoundCloud`);
return filterExistingTracks(tracks);
})
.then((newTracks) => {
logger.info(`Downloading ${newTracks.length} new tracks`);
return new Promise((resolve, reject) => {
Promise.map(newTracks, (track) => {
const mediaFile = path.join(`/tmp/${track.id}.mp3`);
const imageFile = path.join(`/tmp/${track.id}.jpg`);
return Promise
.all([
downloadArtwork(track, imageFile),
SoundCloud.downloadTrack(track, mediaFile),
])
.then(() => writeMetadata(track, mediaFile, imageFile))
.then(() => {
fs.unlinkSync(imageFile);
return saveTrack(track, mediaFile);
})
.then(() => {
fs.unlinkSync(mediaFile);
logger.info(`[FINISHED] ${track.title}`);
})
.catch(reject);
}, { concurrency: 5 }).then(() => {
resolve();
}).catch(reject);
});
})
.then(() => {
logger.info('All done');
})
.catch((error) => {
logger.error(error);
});
}
module.exports = { sync };