start = async (host, secret, settings, headers) => {
        settings = init(Object.assign({
            process: 'nexrender-worker',
            stopOnError: false,
            logger: console,
            handleInterruption: false,
        }, settings))

        settingsRef = settings;
        active = true;

        settings.logger.log('starting nexrender-worker with following settings:')
        Object.keys(settings).forEach(key => {
            settings.logger.log(` - ${key}: ${settings[key]}`)
        })

        if (typeof settings.tagSelector == 'string') {
            settings.tagSelector = settings.tagSelector.replace(/[^a-z0-9, ]/gi, '')
        }
        // if there is no setting for how many empty queues to tolerate, make one from the
        // environment variable, or the default (which is zero)
        if (!(typeof settings.tolerateEmptyQueues == 'number')) {
            settings.tolerateEmptyQueues = NEXRENDER_TOLERATE_EMPTY_QUEUES;
        }

        headers = headers || {};
        headers['user-agent'] = ('nexrender-worker/' + pkg.version + ' ' + (headers['user-agent'] || '')).trim();

        client = createClient({ host, secret, headers, name: settings.name });

        settings.track('Worker Started', {
            worker_tags_set: !!settings.tagSelector,
            worker_setting_tolerate_empty_queues: settings.tolerateEmptyQueues,
            worker_setting_exit_on_empty_queue: settings.exitOnEmptyQueue,
            worker_setting_polling: settings.polling,
            worker_setting_stop_on_error: settings.stopOnError,
        })

        if(settings.stopAtTime) {
            let stopTimeParts = settings.stopAtTime.split(':'); // split the hour and minute
            let now = new Date(); // get current date object

            stop_datetime = new Date(); // new date object for stopping time
            stop_datetime.setHours(stopTimeParts[0], stopTimeParts[1], 0, 0); // set the stop time

            if(stop_datetime.getTime() <= now.getTime()){
                stop_datetime.setDate(stop_datetime.getDate() + 1); // if it's past the stop time, move it to next day
            }

            if(settings.stopDays) {
                let stopDaysList = settings.stopDays.split(',').map(Number); // convert string weekdays into integer values
                while(!stopDaysList.includes(stop_datetime.getDay())) {
                    stop_datetime.setDate(stop_datetime.getDate() + 1); // if stop_datetime's weekday is not in the list, add one day
                }
            }
        }

        // Set up interruption handlers if enabled
        if (settings.handleInterruption) {
            process.on('SIGINT', handleInterruption);
            process.on('SIGTERM', handleInterruption);
            settingsRef.logger.log('Interruption handling enabled.');
        }

        do {
            currentJob = await nextJob(client, settings);

            // if the worker has been deactivated, exit this loop
            if (!active) break;

            settings.track('Worker Job Started', {
                job_id: currentJob.uid, // anonymized internally
            })

            currentJob.state = 'started';
            currentJob.startedAt = new Date()

            try {
                await client.updateJob(currentJob.uid, currentJob)
            } catch (err) {
                console.log(`[${currentJob.uid}] error while updating job state to ${currentJob.state}. Job abandoned.`)
                console.log(`[${currentJob.uid}] error stack: ${err.stack}`)
                continue;
            }

            try {
                currentJob.onRenderProgress = ((c, s) => async (job) => {
                    try {
                        /* send render progress to our server */
                        await c.updateJob(job.uid, getRenderingStatus(job));

                        if (s.onRenderProgress) {
                            s.onRenderProgress(job);
                        }
                    } catch (err) {
                        if (s.stopOnError) {
                            throw err;
                        } else {
                            console.log(`[${job.uid}] error updating job state occurred: ${err.stack}`)
                        }
                    }
                })(client, settings);
                currentJob.onRenderError = ((c, s, job) => (_, err) => {
                    job.error = [].concat(job.error || [], [err.toString()]);

                    if (s.onRenderError) {
                        s.onRenderError(job, err);
                    }

                    /* send render progress to our server */
                    c.updateJob(job.uid, getRenderingStatus(job));
                })(client, settings, currentJob);

                currentJob = await render(currentJob, settings); {
                    currentJob.state = 'finished';
                    currentJob.finishedAt = new Date();
                    if (settings.onFinished) {
                        settings.onFinished(currentJob);
                    }
                }

                settings.track('Worker Job Finished', { job_id: currentJob.uid })

                await client.updateJob(currentJob.uid, getRenderingStatus(currentJob))
            } catch (err) {
                currentJob.error = [].concat(currentJob.error || [], [err.toString()]);
                currentJob.errorAt = new Date();
                currentJob.state = 'error';

                settings.track('Worker Job Error', { job_id: currentJob.uid });

                if (settings.onError) {
                    settings.onError(currentJob, err);
                }

                try {
                    await client.updateJob(currentJob.uid, getRenderingStatus(currentJob))
                }
                catch (e) {
                    console.log(`[${currentJob.uid}] error while updating job state to ${currentJob.state}. Job abandoned.`)
                    console.log(`[${currentJob.uid}] error stack: ${e.stack}`)
                }

                if (settings.stopOnError) {
                    throw err;
                } else {
                    console.log(`[${currentJob.uid}] error occurred: ${err.stack}`)
                    console.log(`[${currentJob.uid}] render proccess stopped with error...`)
                    console.log(`[${currentJob.uid}] continue listening next job...`)
                }
            }

            if (settings.waitBetweenJobs) {
                await delay(settings.waitBetweenJobs);
            }
        } while (active)

        // Clean up interruption handlers
        if (settings.handleInterruption) {
            process.removeListener('SIGINT', handleInterruption);
            process.removeListener('SIGTERM', handleInterruption);
        }
    }
Starts worker "thread" of continious loop
of fetching queued projects and rendering them
@param  {String} host
@param  {String} secret
@param  {Object} settings
@return {Promise}