UNPKG

anytv-node-youku-uploader

Version:

A simple nodejs library for youku authentication and upload.

300 lines (237 loc) 8.57 kB
'use strict'; import md5file from 'md5-file'; import request from 'request'; import cudl from 'cuddle'; import path from 'path'; import dns from 'dns'; import fs from 'fs'; export default class YoukuUploader { constructor (config, _metadata, _access_token) { this.UPLOAD_TOKEN_URL = 'https://openapi.youku.com/v2/uploads/create.json'; this.UPLOAD_COMMIT_URL = 'https://openapi.youku.com/v2/uploads/commit.json'; this.metadata = _metadata; this.client_id = config.client_id this.access_token = _access_token; this.progress_callback = function() {}; this.filepath = this.metadata.filepath; this.client_secret = config.client_secret; this.slice_size = config.slice_size || 10240; this.metadata.file_md5 = md5file(this.metadata.filepath); this.metadata.file_size = fs.statSync(this.metadata.filepath).size; this.metadata.file_ext = path.extname(this.metadata.filepath).slice(1); } set_slice_size (size) { this.slice_size = size; } get_upload_token (callback) { this.metadata.client_id = this.client_id; this.metadata.access_token = this.access_token; // save token before calling user callback cudl.get .to(this.UPLOAD_TOKEN_URL) .send(this.metadata) .then(this.save_upload_token.bind(this, callback)); } save_upload_token (callback, err, result, request) { if (err) { console.log('Error here', err); return callback(err, result, request); } // save token for easier access this.token_info = result; callback(null, result, request); } create_file (callback) { if (!this.base_url) { //resolve dns because youku wants ip instead of domain return dns.resolve4(this.token_info.upload_server_uri, (err, result) => { if (err) { return callback(err); } const params = {upload_token: this.token_info.upload_token}; this.upload_ip = result[Math.floor(Math.random()*result.length)]; this.base_url = `http://${this.upload_ip}/gupload/`; this.create_file(callback); }); } const params = { ext: this.metadata.file_ext, file_size: this.metadata.file_size, upload_token: this.token_info.upload_token, slice_length: this.slice_size }; cudl.post .to(this.base_url + 'create_file') .send(params) .then(callback); } upload (callback) { const start = () => { this.get_upload_token(create_file); }; const create_file = (err, result, request) => { if (err) { return callback(err); } if (this.video_id_callback) { this.video_id_callback(result); } this.create_file(create_slice); }; const create_slice = (err, result) => { if (err) { console.log('Error creating file'); return callback(err); } this.new_slice(upload_slice); }; const upload_slice = (err, result, args, request) => { if (err) { console.log('Error creating new slice'); return callback(err); } this.upload_slice(result, (err, result) => { if (err) { console.log('Error uploading slice'); console.log(err); return callback(err); } this.checker = setInterval(() => { if (this.finished) { this.commit(callback); clearInterval(this.checker); } }, 1000); }); this.check((err, result) => { if (err) { return callback(err); } this.uploader_ip = result.upload_server_ip; this.finished = result.finished; }) }; start(); } new_slice (callback) { cudl.get .to(this.base_url + 'new_slice') .send({ upload_token: this.token_info.upload_token }) .then((err, result, request) => { if (err) { return callback(err); } this.slice_upload_meta = result; callback(err, result, request); }); } upload_slice (upload_meta, callback) { let buffer; let _fd; const start = () => { if (typeof upload_meta === 'string') { upload_meta = JSON.parse(upload_meta); } buffer = new Buffer(upload_meta.length); fs.open(this.metadata.filepath, 'r', read_file); }; const read_file = (err, fd) => { _fd = fd; // read offset and length based on youku return fs.read(fd, buffer, 0, upload_meta.length, upload_meta.offset, call_api); }; const call_api = (err, bytesRead) => { if (err) { return callback(err); } const params = { upload_token: this.token_info.upload_token, slice_task_id: upload_meta.slice_task_id, offset: upload_meta.offset, length: upload_meta.length, hash: this.hash(buffer.toString('binary')) }; request({ url: this.base_url + 'upload_slice', qs: params, method: 'POST', body: buffer, headers: { 'Accept': 'application/json' } }, process_slice ); }; const process_slice = (err, response, body) => { if (err) { return callback(err); } if (typeof body === 'string') { body = JSON.parse(body); } fs.close(_fd); // continue uploading slices while slice_task_id is not 0 (done) if (body.slice_task_id != 0) { upload_meta.offset = body.offset; upload_meta.length = body.length; upload_meta.slice_task_id = body.slice_task_id; return this.upload_slice(upload_meta, callback); } // call callback when all slices are uploaded callback(null, body); }; start(); } check (callback) { const params = {upload_token: this.token_info.upload_token}; cudl.get .to(this.base_url + 'check') .send(params) .then((err, result) => { if (err) { return callback(err); } if (typeof result === 'string') { result = JSON.parse(result); } if (result.status) { switch (result.status) { case 1: // case 1 is `done` in their API return callback(null, result); default: // while not done, pass the progress this.progress_callback(result); setTimeout(() => { this.check(callback); }, 1000); } } }); } commit (callback) { const params = { access_token: this.access_token, client_id: this.client_id, upload_token: this.token_info.upload_token, upload_server_ip: this.uploader_ip }; cudl.get .to(this.UPLOAD_COMMIT_URL) .send(params) .then(callback); } on_video_id (callback) { this.video_id_callback = callback; } on_progress (callback) { this.progress_callback = callback; } hash (string, algo) { return require('crypto') .createHash(algo || 'md5') .update('' + string, 'ascii') .digest('hex'); } }