simple uploader script for deluge, assign this script to be called on torrent complete and it will walk a directory and upload it to your google drive
install dependencies:
npm install googleapis async winston lodash mime twilio
{ | |
"TWILIO_SID": "", | |
"TWILIO_TOKEN": "", | |
"NOTIFY_FROM_NUMBER": "", | |
"NOTIFY_TO_NUMBER": "", | |
"GOOGLE_API_CLIENT_ID": "", | |
"GOOGLE_API_CLIENT_SECRET": "", | |
"GOOGLE_API_REFRESH_TOKEN": "", | |
"upload_concurrency": 5, | |
"root_folder": "deluge-completed" | |
} |
#!/usr/bin/node | |
'use strict' | |
var config = require('./config') | |
const FOLDER = 'application/vnd.google-apps.folder' | |
const DEST_FOLDER = config.root_folder | |
var fs = require('fs') | |
var google = require('googleapis') | |
var async = require('async') | |
var winston = require('winston') | |
var _ = require('lodash') | |
var mime = require('mime') | |
var tid = process.argv[2] | |
var tname = process.argv[3] | |
var tpath = process.argv[4] | |
if (tpath.charAt(tpath.length - 1) !== '/') { | |
tpath += '/' | |
} | |
var logger = new (winston.Logger)({ | |
transports: [ | |
new (winston.transports.Console)(), | |
new (winston.transports.File)({ filename: './log' }) | |
] | |
}) | |
logger.info('starting upload!') | |
logger.info(`${tid} / ${tname} / ${tpath}`) | |
var auth = new google.auth.OAuth2( | |
config.GOOGLE_API_CLIENT_ID, | |
config.GOOGLE_API_CLIENT_SECRET | |
) | |
auth.credentials.refresh_token = config.GOOGLE_API_REFRESH_TOKEN | |
auth.refreshAccessToken(function (err, tokens) { | |
if (err) { | |
logger.error(`error retrieving access tokens ${err.error} - '${err.error_description}'`) | |
process.exit(1) | |
return | |
} | |
var rootFolder | |
var drive = google.drive({ | |
version: 'v2', | |
auth: auth | |
}) | |
var uploadQueue = async.queue(function (task, done) { | |
var filename = getLastPart(task.path) | |
logger.info('start uploading: %s', filename) | |
var insertOp = { | |
resource: { | |
title: filename, | |
mimeType: task.type, | |
parents: [ | |
{ id: task.in } | |
] | |
} | |
} | |
if (task.type !== FOLDER) { | |
insertOp.media = { | |
mimeType: task.type, | |
body: fs.createReadStream(task.path) | |
} | |
} | |
drive.files.insert(insertOp, function (err, uploadedFile) { | |
logger.info('finished uploading: %s', filename) | |
done(err, uploadedFile) | |
}) | |
}, config.upload_concurrency) | |
function upload (folder, _in, depth, done) { | |
var uploadedFolder | |
if (depth > 25) { | |
logger.warning('maximum depth reached!!!') | |
done(null) | |
} | |
if (fs.statSync(folder).isFile()) { | |
logger.info('torrent is just a single file: %s', folder) | |
uploadQueue.push({ | |
path: folder, | |
type: mime.lookup(folder), | |
in: _in | |
}, done) | |
} else { | |
logger.info('uploading folder: %s', folder) | |
async.waterfall([ | |
function (cb) { | |
uploadQueue.push({ | |
path: folder, | |
type: FOLDER, | |
in: _in | |
}, cb) | |
}, | |
function (newFolder, cb) { | |
uploadedFolder = newFolder | |
fs.readdir(folder, cb) | |
}, | |
function (fileList, cb) { | |
var folders = _.filter(fileList, function (f) { | |
return fs.statSync(`${folder}/${f}`).isDirectory() | |
}) | |
var files = _.filter(fileList, function (f) { | |
return fs.statSync(`${folder}/${f}`).isFile() | |
}) | |
async.map(files, function (file, cb) { | |
uploadQueue.push({ | |
path: `${folder}/${file}`, | |
type: mime.lookup(file), | |
in: uploadedFolder.id | |
}, cb) | |
}, function (err) { | |
if (err) return cb(err) | |
async.each(folders, function (subFolder, cb) { | |
upload(`${folder}/${subFolder}`, uploadedFolder.id, (depth + 1), cb) | |
}, cb) | |
}) | |
} | |
], done) | |
} | |
} | |
async.series([ | |
function findOrCreateFolder (cb) { | |
// find folder to put stuff in | |
logger.info('finding a folder to put stuff in') | |
/* | |
+----------------------+ | |
| find folder with | | |
| "title=DEST_FOLDER" | | |
+----------------------+ | |
| | |
+-(found one)--+-(didn't find one)-+ | |
| | | |
v v | |
+------------------+ +------------------+ | |
| is it in the | |create new folder | | |
| trash? |---+ | in root |--+ | |
+------------------+ | +------------------+ | | |
| | | | |
+--(yes)-+ +------(no)--------+ | | |
| | | | |
v v | | |
+------------------+ +------------------+ | | |
| remove it from | |return destination| | | |
| trash |-->| folder |<----+ | |
+------------------+ +------------------+ | |
*/ | |
drive.children.list({ | |
folderId: 'root', | |
q: `title="${DEST_FOLDER}"` | |
}, function (err, childList) { | |
if (err) return cb(err) | |
if (childList.items.length > 0) { | |
var rootFolderId = childList.items[0].id | |
drive.files.get({ | |
fileId: rootFolderId, | |
updateViewedDate: true | |
}, function (err, _root_folder) { | |
if (err) { | |
return cb(err) | |
} | |
rootFolder = _root_folder | |
if (rootFolder.labels.trashed) { | |
logger.info(`target folder was in the trash! untrashing...`) | |
drive.files.untrash({ | |
fileId: rootFolder.id | |
}, function (err) { | |
if (err) { | |
return cb(err) | |
} | |
logger.info('cool! target folder out of the trash. moving on...') | |
cb(null) | |
}) | |
return | |
} | |
logger.info(`found one! ${rootFolder.id}`) | |
cb(null) | |
}) | |
return | |
} | |
logger.info('did not find one, creating one now...') | |
drive.files.insert({ | |
resource: { | |
title: DEST_FOLDER, | |
mimeType: FOLDER | |
} | |
}, function (err, _new_folder) { | |
if (err) return cb(err) | |
rootFolder = _new_folder | |
cb(null) | |
}) | |
}) | |
}, | |
function uploadStuff (cb) { | |
upload(`${tpath}${tname}`, rootFolder.id, 0, cb) | |
} | |
], function (err) { | |
if (err) { | |
logger.error('ERROR') | |
logger.error(err) | |
return | |
} | |
twilio.sendMessage({ | |
to: config.NOTIFY_TO_NUMBER, | |
from: config.NOTIFY_FROM_NUMBER, | |
body: `so let it be said, so let it be done! '${tname}' finished downloading!` | |
}, function (err) { | |
if (err) { | |
logger.error('ERROR SENDING NOTIFICATION', err) | |
return | |
} | |
logger.info('sent notification') | |
}) | |
logger.info('successfully finished uploading shit') | |
}) | |
}) | |
function getLastPart (path) { | |
if (path.charAt(path.length - 1) === '/') { | |
path = path.slice(0, -1) | |
} | |
return path.split('/').pop() | |
} |