file-replicator.ts 4.46 KB
import {FileHandler} from './api/file-handler';
import {File} from './api/file';
import {Util} from './util';
import {EventEmitter} from 'events';

export class FileReplicator extends EventEmitter {

    constructor() {
        super();
    }

    protected _files:Array<File> = [];

    protected _itemValidator: (item:any) => boolean = null; 
    protected _fileHandler:FileHandler = null;  
    protected _retryTimeout:number = 0;
    
    protected _itemKey   = "type";
    protected _itemValue = "asset";
    protected _itemSourceAttribute = "source";
    protected _itemTargetAttribute = "target";

    get files(): Array<File> {
        return this._files;
    }

    set fileHandler (handler:FileHandler) {
        this._fileHandler = handler;
    }

    set retryTimeout (timeout:number) {
        this._retryTimeout = timeout;
    }

    set itemValidator(validator:(item:any) => boolean) {
        this._itemValidator = validator;
    }

    set itemKey(key:string) {
        this._itemKey = key;
    }

    set itemValue(value:string) {
        this._itemValue = value;
    }

    set itemSourceAttribute(sourceAttribute:string) {
        this._itemSourceAttribute = sourceAttribute;
    }

    set itemTargetAttribute(targetAttribute:string) {
        this._itemTargetAttribute = targetAttribute;
    }

    get itemKey() {
        return this._itemKey;
    }

    get itemValue() {
        return this._itemValue;
    }

    get itemSourceAttribute() {
        return this._itemSourceAttribute;
    }

    get itemTargetAttribute() {
        return this._itemTargetAttribute;
    }

    init(files: Array<File> = []) {
        this._files = files;
    }

    /**
     * change from pouchdb replicate
     */
    pushChanges(change:any) {
        let items:Array<any> = [];

        if (change && change.docs && change.docs.length > 0) {
            for (let item of change.docs) {
                if (item[this._itemKey] && item[this._itemKey] === this._itemValue) {
                    items.push(item);
                }
            }
        }
        
        let files = this.prepareFiles(items);

        for (let file of files) {
            this._files.push(file);
        }
    }

    downloadFiles(files:Array<File>, fileHandler:FileHandler, index:number = 0) { 
        if (index >= files.length) {
            return;
        }

        this.emit('start', { progress: 0, index : index, length : files.length });

        fileHandler
            .download(files[index].source, files[index].target)
            .subscribe(
                progress => {
                    this.emit('progress', { progress : progress, index : index, length : files.length })
                } ,
                error => {
                    this.emit('error', { progress : 0, index : index, length : files.length, error: error });
                } ,
                () => {
                    this.emit('complete', { progress : 100 , index : index, length : files.length });
                    this.downloadFiles(files, fileHandler, index+1);
                }
            );
    }

    prepareFiles(items: Array<any>) : Array<File> {
        let output = [];

        for (let item of items) {
            if (item[this._itemSourceAttribute] && (!this._itemValidator || this._itemValidator(item))) {
                let file = { source : item[this._itemSourceAttribute] , target : '' };

                if (item[this._itemTargetAttribute]) {
                    file.target = item[this._itemTargetAttribute];
                } else {
                    file.target = Util.getNameHash(file.source);
                }

                output.push(file);
            }
        }

        return output;
    }

    start() {
        this.on('complete', (event:any) => {
            if ((event.index + 1) >= event.length) {
                this.replicationFinalized(event.index);
            }
        });

        this.on('error', (event:any) => {
            this.replicationFinalized(event.index);
        });        

        this.downloadFiles(this._files, this._fileHandler);
    }

    replicationFinalized(lastIndex:number) {       
        if (lastIndex+1 >= this._files.length) { // all finished
            this._files = [];
            this.emit('final');
        } else if (this._retryTimeout > 0) { // restart after last success            
            this._files.splice(0,lastIndex);
            setTimeout(() => { 
                this.downloadFiles(this._files, this._fileHandler);
            }, this._retryTimeout);
        }        
    }

}