Showing
16 changed files
with
634 additions
and
0 deletions
.gitignore
0 → 100644
package.json
0 → 100644
| 1 | +{ | ||
| 2 | + "name": "digsig-player-service", | ||
| 3 | + "version": "1.0.0", | ||
| 4 | + "description": "", | ||
| 5 | + "main": "src/main.ts", | ||
| 6 | + "scripts": { | ||
| 7 | + "pretest": "tsc --target es5 --outDir .tmp spec/index.ts", | ||
| 8 | + "test": "jasmine .tmp/spec/index.js", | ||
| 9 | + "posttest": "rm -R .tmp" | ||
| 10 | + }, | ||
| 11 | + "author": "Stefan Huber <stefan.huber@beyondit.at>", | ||
| 12 | + "license": "ISC", | ||
| 13 | + "devDependencies": { | ||
| 14 | + "@types/es6-promise": "0.0.32", | ||
| 15 | + "@types/jasmine": "^2.5.41", | ||
| 16 | + "@types/node": "^7.0.0", | ||
| 17 | + "jasmine": "^2.5.3", | ||
| 18 | + "typescript": "^2.1.5" | ||
| 19 | + } | ||
| 20 | +} |
spec/dummy-program-repository.ts
0 → 100644
| 1 | +import {ProgramRepository} from '../src/program-repository'; | ||
| 2 | +import Util from '../src/util'; | ||
| 3 | + | ||
| 4 | +export default class DummyProgramRepository implements ProgramRepository { | ||
| 5 | + | ||
| 6 | + findById(id:string) : Promise<any> { | ||
| 7 | + return null; | ||
| 8 | + } | ||
| 9 | + | ||
| 10 | + findByIds(ids:Array<string>) : Promise<Array<any>> { | ||
| 11 | + return null; | ||
| 12 | + } | ||
| 13 | + | ||
| 14 | + findByType(type:string) : Promise<Array<any>> { | ||
| 15 | + return null; | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + replicate() : Promise<void> { | ||
| 19 | + return null; | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
spec/index.ts
0 → 100644
spec/player.spec.ts
0 → 100644
| 1 | +import Player from '../src/player'; | ||
| 2 | +import Util from '../src/util'; | ||
| 3 | +import DummyProgramRepository from './dummy-program-repository'; | ||
| 4 | + | ||
| 5 | +describe('Player', () => { | ||
| 6 | + | ||
| 7 | + let player = new Player(); | ||
| 8 | + | ||
| 9 | + it('should trigger function after milliseconds', (done) => { | ||
| 10 | + player.state = 'start'; | ||
| 11 | + player.trigger(() => { expect(true).toBe(true); done(); }, 10); | ||
| 12 | + }); | ||
| 13 | + | ||
| 14 | +}); | ||
| 15 | + | ||
| 16 | +describe('Player repository triggers', () => { | ||
| 17 | + | ||
| 18 | + let player = new Player(); | ||
| 19 | + let dummyProgramRepository = new DummyProgramRepository(); | ||
| 20 | + | ||
| 21 | + beforeEach(() => { | ||
| 22 | + spyOn(player, "trigger").and.returnValue(null); | ||
| 23 | + player.programRepository = dummyProgramRepository; | ||
| 24 | + }); | ||
| 25 | + | ||
| 26 | + it('should trigger program item retrieval', (done) => { | ||
| 27 | + spyOn(dummyProgramRepository, "replicate").and.callFake(() => { | ||
| 28 | + return new Promise<any> ((resolve, reject) => { | ||
| 29 | + resolve(); | ||
| 30 | + }); | ||
| 31 | + }); | ||
| 32 | + | ||
| 33 | + player.triggerReplication().then(() => { | ||
| 34 | + | ||
| 35 | + expect(player.trigger).toHaveBeenCalledWith(player.triggerProgramItemId, Util.calculateNextMinute()); | ||
| 36 | + done(); | ||
| 37 | + }); | ||
| 38 | + }); | ||
| 39 | + | ||
| 40 | + it('should trigger replication', (done) => { | ||
| 41 | + spyOn(dummyProgramRepository, "replicate").and.callFake(() => { | ||
| 42 | + return new Promise<any> ((resolve, reject) => { | ||
| 43 | + reject(); | ||
| 44 | + }); | ||
| 45 | + }); | ||
| 46 | + | ||
| 47 | + player.triggerReplication().then(() => { | ||
| 48 | + expect(player.trigger).toHaveBeenCalledWith(player.triggerReplication, player.replicationRetry); | ||
| 49 | + done(); | ||
| 50 | + }); | ||
| 51 | + }); | ||
| 52 | + | ||
| 53 | +}); |
spec/program-item-factory.spec.ts
0 → 100644
| 1 | +import ProgramItemFactory from '../src/program-item/program-item-factory'; | ||
| 2 | +import DummyProgramRepository from './dummy-program-repository'; | ||
| 3 | +import { PROGRAM_ITEM_TYPE_VIDEO, PROGRAM_ITEM_TYPE_SLIDESHOW } from '../src/program-item/program-item'; | ||
| 4 | + | ||
| 5 | +describe('Program Item Factory', () => { | ||
| 6 | + | ||
| 7 | + let dummyProgramRepository = new DummyProgramRepository(); | ||
| 8 | + let programItemFactory = new ProgramItemFactory(); | ||
| 9 | + programItemFactory.basePath = '/basepath/'; | ||
| 10 | + | ||
| 11 | + beforeEach(() => { | ||
| 12 | + | ||
| 13 | + spyOn(dummyProgramRepository, "findById").and.callFake(() => { | ||
| 14 | + return new Promise<any> ((resolve, reject) => { | ||
| 15 | + resolve({ | ||
| 16 | + "_id" : "sample-video-id" , | ||
| 17 | + "type" : "asset" , | ||
| 18 | + "filename" : "somefilename.mp4" , | ||
| 19 | + "url" : "https://somewhere.com/blabalbalx" | ||
| 20 | + }); | ||
| 21 | + }); | ||
| 22 | + }); | ||
| 23 | + | ||
| 24 | + spyOn(dummyProgramRepository, "findByIds").and.callFake(() => { | ||
| 25 | + return new Promise<any> ((resolve, reject) => { | ||
| 26 | + resolve([ | ||
| 27 | + { | ||
| 28 | + "_id" : "image-id-1" , | ||
| 29 | + "type" : "asset" , | ||
| 30 | + "filename" : "somefilename1.jpg" , | ||
| 31 | + "url" : "https://somewhere.com/1" | ||
| 32 | + }, | ||
| 33 | + { | ||
| 34 | + "_id" : "image-id-2" , | ||
| 35 | + "type" : "asset" , | ||
| 36 | + "filename" : "somefilename2.jpg" , | ||
| 37 | + "url" : "https://somewhere.com/2" | ||
| 38 | + }, | ||
| 39 | + { | ||
| 40 | + "_id" : "image-id-3" , | ||
| 41 | + "type" : "asset" , | ||
| 42 | + "filename" : "somefilename3.jpg" , | ||
| 43 | + "url" : "https://somewhere.com/3" | ||
| 44 | + }, | ||
| 45 | + ]); | ||
| 46 | + }); | ||
| 47 | + }); | ||
| 48 | + | ||
| 49 | + programItemFactory.programRepository = dummyProgramRepository; | ||
| 50 | + }); | ||
| 51 | + | ||
| 52 | + it('should return a video page item', (done) => { | ||
| 53 | + programItemFactory.prepareProgramItem(PROGRAM_ITEM_TYPE_VIDEO, { | ||
| 54 | + video : 'sample-video-id' | ||
| 55 | + }).then((programItem) => { | ||
| 56 | + expect(programItem.type).toEqual(PROGRAM_ITEM_TYPE_VIDEO); | ||
| 57 | + expect(programItem.data.video).toEqual('/basepath/somefilename.mp4'); | ||
| 58 | + done(); | ||
| 59 | + }); | ||
| 60 | + }); | ||
| 61 | + | ||
| 62 | + it('should return a slideshow page item', (done) => { | ||
| 63 | + programItemFactory.prepareProgramItem(PROGRAM_ITEM_TYPE_SLIDESHOW, { | ||
| 64 | + images : ['image-id-1','image-id-2','image-id-3'] , | ||
| 65 | + settings : { | ||
| 66 | + speed : 1000 , | ||
| 67 | + effect : 'something' | ||
| 68 | + } | ||
| 69 | + }).then((programItem) => { | ||
| 70 | + expect(programItem.type).toEqual(PROGRAM_ITEM_TYPE_SLIDESHOW); | ||
| 71 | + expect(programItem.data.speed).toEqual(1000); | ||
| 72 | + expect(programItem.data.effect).toEqual('something'); | ||
| 73 | + expect(programItem.data.images).toEqual(['/basepath/somefilename1.jpg','/basepath/somefilename2.jpg','/basepath/somefilename3.jpg']); | ||
| 74 | + done(); | ||
| 75 | + }); | ||
| 76 | + }); | ||
| 77 | + | ||
| 78 | +}); | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
spec/program-manager.spec.ts
0 → 100644
| 1 | +import DummyProgramRepository from './dummy-program-repository'; | ||
| 2 | +import ProgramManager from '../src/program-manager'; | ||
| 3 | +import Util from '../src/util'; | ||
| 4 | + | ||
| 5 | +describe('Program Manager', () => { | ||
| 6 | + | ||
| 7 | + let programManager = new ProgramManager(); | ||
| 8 | + | ||
| 9 | + it('should find respective program item, according to given time', () => { | ||
| 10 | + let nowInMinutes1 = Util.convertToMinutes("00:00"); | ||
| 11 | + let nowInMinutes2 = Util.convertToMinutes("03:23"); | ||
| 12 | + let nowInMinutes3 = Util.convertToMinutes("23:29"); | ||
| 13 | + | ||
| 14 | + let schedule = { | ||
| 15 | + "23:30" : "item-4" , | ||
| 16 | + "07:20" : "item-2" , | ||
| 17 | + "16:47" : "item-3" , | ||
| 18 | + "03:22" : "item-1" , | ||
| 19 | + "00:00" : "item-0" | ||
| 20 | + }; | ||
| 21 | + | ||
| 22 | + expect(programManager.findCurrentProgramItem(schedule, nowInMinutes1)).toEqual("item-0"); | ||
| 23 | + expect(programManager.findCurrentProgramItem(schedule, nowInMinutes2)).toEqual("item-1"); | ||
| 24 | + expect(programManager.findCurrentProgramItem(schedule, nowInMinutes3)).toEqual("item-3"); | ||
| 25 | + }); | ||
| 26 | + | ||
| 27 | +}); | ||
| 28 | + | ||
| 29 | +describe('Program Segment', () => { | ||
| 30 | + | ||
| 31 | + let programManager = new ProgramManager(); | ||
| 32 | + let dummyProgramRepository = new DummyProgramRepository(); | ||
| 33 | + | ||
| 34 | + beforeEach(() => { | ||
| 35 | + | ||
| 36 | + let schedule = {}; | ||
| 37 | + schedule['2016-12-24'] = 'xmas-program-segment'; | ||
| 38 | + schedule[Util.getISODate()] = 'program-segment-of-today'; | ||
| 39 | + | ||
| 40 | + spyOn(dummyProgramRepository, "findByType").and.callFake(() => { | ||
| 41 | + return new Promise<any> ((resolve, reject) => { | ||
| 42 | + resolve([{ | ||
| 43 | + _id : 'test-program-1' , | ||
| 44 | + type : "program" , | ||
| 45 | + default : "xmas-program-segment" , | ||
| 46 | + schedule : schedule | ||
| 47 | + }]); | ||
| 48 | + }); | ||
| 49 | + }); | ||
| 50 | + | ||
| 51 | + spyOn(dummyProgramRepository, "findById").and.callFake(() => { | ||
| 52 | + return new Promise<any> ((resolve, reject) => { | ||
| 53 | + resolve({ | ||
| 54 | + _id : 'program-segment-of-today' , | ||
| 55 | + type : 'program_segment' | ||
| 56 | + }); | ||
| 57 | + }); | ||
| 58 | + }); | ||
| 59 | + | ||
| 60 | + programManager.programRepository = dummyProgramRepository; | ||
| 61 | + }); | ||
| 62 | + | ||
| 63 | + it('retrieve program segment for today', (done) => { | ||
| 64 | + | ||
| 65 | + programManager.findCurrentProgramSegment() | ||
| 66 | + .then(programSegment => { | ||
| 67 | + | ||
| 68 | + expect(dummyProgramRepository.findById).toHaveBeenCalled(); | ||
| 69 | + expect(dummyProgramRepository.findByType).toHaveBeenCalled(); | ||
| 70 | + | ||
| 71 | + expect(programSegment._id).toEqual('program-segment-of-today'); | ||
| 72 | + expect(programSegment.type).toEqual('program_segment'); | ||
| 73 | + | ||
| 74 | + done(); | ||
| 75 | + }); | ||
| 76 | + }, 5000); | ||
| 77 | + | ||
| 78 | +}); | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
spec/util.spec.ts
0 → 100644
| 1 | +import Util from './../src/util'; | ||
| 2 | + | ||
| 3 | +describe("Util", () => { | ||
| 4 | + | ||
| 5 | + it('should convert to minutes', () => { | ||
| 6 | + expect(Util.convertToMinutes('2:26')).toEqual(146); | ||
| 7 | + expect(Util.convertToMinutes('22:3')).toEqual(1323); | ||
| 8 | + expect(Util.convertToMinutes("00:00")).toEqual(0); | ||
| 9 | + expect(Util.convertToMinutes("23:59")).toEqual(1439); | ||
| 10 | + expect(Util.convertToMinutes("12:00")).toEqual(720); | ||
| 11 | + expect(Util.convertToMinutes('435.abc')).toEqual(0); | ||
| 12 | + expect(Util.convertToMinutes("55555:3434")).toEqual(0); | ||
| 13 | + }); | ||
| 14 | + | ||
| 15 | + it('should get the remaining seconds', () => { | ||
| 16 | + expect(Util.calculateNextMinute()).toEqual((60 - (new Date()).getSeconds()) * 1000); | ||
| 17 | + }); | ||
| 18 | + | ||
| 19 | +}); | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/index.ts
0 → 100644
src/player.ts
0 → 100644
| 1 | +import { EventEmitter } from 'events'; | ||
| 2 | +import {ProgramRepository} from './program-repository'; | ||
| 3 | +import ProgramManager from './program-manager'; | ||
| 4 | +import Util from './util'; | ||
| 5 | + | ||
| 6 | +const STATE_START = "start"; | ||
| 7 | +const STATE_STOP = "stop"; | ||
| 8 | + | ||
| 9 | +export default class Player extends EventEmitter { | ||
| 10 | + | ||
| 11 | + protected _programRepository:ProgramRepository; | ||
| 12 | + protected _programManager:ProgramManager; | ||
| 13 | + protected _minutesReplication:number = 5; | ||
| 14 | + protected _replicationRetry:number = 10000; | ||
| 15 | + | ||
| 16 | + protected _currentProgramItemId:string = ''; | ||
| 17 | + protected _currentReplicationCounter:number = 0; | ||
| 18 | + protected _state = STATE_STOP; | ||
| 19 | + | ||
| 20 | + set state(st:string) { | ||
| 21 | + this._state = st; | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + set programManager(pm:ProgramManager) { | ||
| 25 | + this._programManager = pm; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + get programManager() : ProgramManager { | ||
| 29 | + return this._programManager; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + set programRepository(pr:ProgramRepository) { | ||
| 33 | + this._programRepository = pr; | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + get programRepository() : ProgramRepository { | ||
| 37 | + return this._programRepository; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + set minutesReplication(mr:number) { | ||
| 41 | + this._minutesReplication = mr; | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + get minutesReplication() : number { | ||
| 45 | + return this._minutesReplication; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + set replicationRetry(rr:number) { | ||
| 49 | + this._replicationRetry = rr; | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + get replicationRetry() : number { | ||
| 53 | + return this._replicationRetry; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + triggerReplication() : Promise<void> { | ||
| 57 | + return this.programRepository.replicate() | ||
| 58 | + .then(() => { | ||
| 59 | + this._currentReplicationCounter = 0; | ||
| 60 | + this.trigger(this.triggerProgramItemId, Util.calculateNextMinute()); | ||
| 61 | + }) | ||
| 62 | + .catch(() => { | ||
| 63 | + this.trigger(this.triggerReplication, this.replicationRetry); | ||
| 64 | + }); | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + triggerProgramItemId() { | ||
| 68 | + this.programManager.getCurrentProgramItemId() | ||
| 69 | + .then(programItemId => { | ||
| 70 | + this._currentReplicationCounter++; | ||
| 71 | + | ||
| 72 | + // if there is a new program item id trigger play | ||
| 73 | + // else (1) calculate next potential program change point | ||
| 74 | + // or (2) trigger replication | ||
| 75 | + | ||
| 76 | + if (programItemId != this._currentProgramItemId) { | ||
| 77 | + this._currentProgramItemId = programItemId; | ||
| 78 | + this.emit('play', programItemId); | ||
| 79 | + } else if (this._currentReplicationCounter >= this._minutesReplication) { | ||
| 80 | + this.triggerReplication(); | ||
| 81 | + } else { | ||
| 82 | + this.trigger(this.triggerProgramItemId, Util.calculateNextMinute()); | ||
| 83 | + } | ||
| 84 | + }); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + trigger(func:Function, milliseconds:number) { | ||
| 88 | + if (this._state === STATE_START) { | ||
| 89 | + setTimeout(() => { func(); }, milliseconds); | ||
| 90 | + } | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + start() { | ||
| 94 | + if (this._state === STATE_STOP) { | ||
| 95 | + this.triggerReplication(); | ||
| 96 | + this._state = STATE_START; | ||
| 97 | + } | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + stop() { | ||
| 101 | + this._state = STATE_STOP; | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/program-item/program-item-factory.ts
0 → 100644
| 1 | +import ProgramItem, { PROGRAM_ITEM_TYPE_SLIDESHOW, PROGRAM_ITEM_TYPE_VIDEO } from './program-item'; | ||
| 2 | +import { ProgramRepository } from '../program-repository'; | ||
| 3 | + | ||
| 4 | +export default class ProgramItemFactory { | ||
| 5 | + | ||
| 6 | + protected _programRepository:ProgramRepository; | ||
| 7 | + protected _basePath:string; | ||
| 8 | + | ||
| 9 | + set basePath(bp:string) { | ||
| 10 | + this._basePath = bp; | ||
| 11 | + } | ||
| 12 | + | ||
| 13 | + get basePath() : string { | ||
| 14 | + return this._basePath; | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + set programRepository(pr:ProgramRepository) { | ||
| 18 | + this._programRepository = pr; | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + get programRepository() : ProgramRepository { | ||
| 22 | + return this._programRepository; | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + getProgramItem(programItemId:string) : Promise<ProgramItem> { | ||
| 26 | + return this.programRepository | ||
| 27 | + .findById(programItemId) | ||
| 28 | + .then((programItem) => { | ||
| 29 | + return this.prepareProgramItem(programItem.program_item_type, programItem); | ||
| 30 | + }); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + prepareProgramItem(type:string, data:any) : Promise<ProgramItem> { | ||
| 34 | + let programItem = new ProgramItem(); | ||
| 35 | + programItem.type = type; | ||
| 36 | + | ||
| 37 | + if (type === PROGRAM_ITEM_TYPE_VIDEO) { | ||
| 38 | + return this.prepareVideoItem(programItem, data); | ||
| 39 | + } else if (type === PROGRAM_ITEM_TYPE_SLIDESHOW) { | ||
| 40 | + return this.prepareSlideshowItem(programItem, data); | ||
| 41 | + } else { | ||
| 42 | + return null; | ||
| 43 | + } | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + prepareSlideshowItem(programItem:ProgramItem, data:any) : Promise<ProgramItem> { | ||
| 47 | + return this._programRepository.findByIds(data.images) | ||
| 48 | + .then(images => { | ||
| 49 | + programItem.data = { | ||
| 50 | + speed : data.settings.speed , | ||
| 51 | + effect : data.settings.effect , | ||
| 52 | + images : [] | ||
| 53 | + }; | ||
| 54 | + | ||
| 55 | + for (let image of images) { | ||
| 56 | + programItem.data.images.push(this.basePath + image.filename); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + return programItem; | ||
| 60 | + }); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + prepareVideoItem(programItem:ProgramItem, data:any) : Promise<ProgramItem> { | ||
| 64 | + return this._programRepository.findById(data.video) | ||
| 65 | + .then((data) => { | ||
| 66 | + programItem.data = { | ||
| 67 | + video : this.basePath + data['filename'] | ||
| 68 | + }; | ||
| 69 | + return programItem; | ||
| 70 | + }); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/program-item/program-item.ts
0 → 100644
| 1 | +export const PROGRAM_ITEM_TYPE_SLIDESHOW = "slideshow"; | ||
| 2 | +export const PROGRAM_ITEM_TYPE_VIDEO = "video"; | ||
| 3 | + | ||
| 4 | +export default class ProgramItem { | ||
| 5 | + | ||
| 6 | + protected _type:string; | ||
| 7 | + protected _data:any; | ||
| 8 | + | ||
| 9 | + set type(t:string) { | ||
| 10 | + this._type = t; | ||
| 11 | + } | ||
| 12 | + | ||
| 13 | + get type():string { | ||
| 14 | + return this._type; | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + set data(d:any) { | ||
| 18 | + this._data = d; | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + get data():any { | ||
| 22 | + return this._data; | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/program-manager.ts
0 → 100644
| 1 | +import {ProgramRepository} from './program-repository'; | ||
| 2 | +import Util from './util'; | ||
| 3 | +import ProgramItem, { PROGRAM_ITEM_TYPE_SLIDESHOW, PROGRAM_ITEM_TYPE_VIDEO } from './program-item/program-item' | ||
| 4 | + | ||
| 5 | +export default class ProgramManager { | ||
| 6 | + | ||
| 7 | + protected _programRepository:ProgramRepository; | ||
| 8 | + | ||
| 9 | + set programRepository(pr:ProgramRepository) { | ||
| 10 | + this._programRepository = pr; | ||
| 11 | + } | ||
| 12 | + | ||
| 13 | + get programRepository() : ProgramRepository { | ||
| 14 | + return this._programRepository; | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + getCurrentProgramItemId() : Promise<string> { | ||
| 18 | + return new Promise<string> ((resolve, reject) => { | ||
| 19 | + this.findCurrentProgramSegment().then(programSegment => { | ||
| 20 | + let currentProgramItemId = programSegment.default; | ||
| 21 | + if (programSegment.schedule) { | ||
| 22 | + currentProgramItemId = this.findCurrentProgramItem(programSegment.schedule, Util.getDateInMinutes()); | ||
| 23 | + } | ||
| 24 | + resolve(currentProgramItemId); | ||
| 25 | + }); | ||
| 26 | + }); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + /** | ||
| 30 | + * find program item in schedule, which fits | ||
| 31 | + * according to current hh:mm | ||
| 32 | + */ | ||
| 33 | + findCurrentProgramItem(schedule:any, dateInMinutes:number) : string { | ||
| 34 | + let timeList:any = []; | ||
| 35 | + let tmpSchedule:any = {}; | ||
| 36 | + | ||
| 37 | + for (let startTime in schedule) { | ||
| 38 | + if (schedule.hasOwnProperty(startTime)) { | ||
| 39 | + let minutes = Util.convertToMinutes(startTime); | ||
| 40 | + timeList.push(minutes); | ||
| 41 | + tmpSchedule[minutes] = schedule[startTime]; | ||
| 42 | + } | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + // sort ascending (-) | ||
| 46 | + timeList.sort((a,b) => { return a-b; }); | ||
| 47 | + | ||
| 48 | + let last = 0; | ||
| 49 | + for (let i = 0; i < timeList.length; i++) { | ||
| 50 | + if (timeList[i] <= dateInMinutes) { | ||
| 51 | + last = timeList[i]; | ||
| 52 | + } else { | ||
| 53 | + break; | ||
| 54 | + } | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + return tmpSchedule[last]; | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * Find the program segment | ||
| 62 | + * This is dependent on the date set on the device | ||
| 63 | + */ | ||
| 64 | + findCurrentProgramSegment() : Promise<any> { | ||
| 65 | + return new Promise<any>((resolve, reject) => { | ||
| 66 | + let today = Util.getISODate(); | ||
| 67 | + | ||
| 68 | + this.programRepository.findByType('program') | ||
| 69 | + .then(programs => { | ||
| 70 | + | ||
| 71 | + if (programs.length > 0) { | ||
| 72 | + let program:any = programs[0]; | ||
| 73 | + let programSegmentId; | ||
| 74 | + | ||
| 75 | + // if there is a program_segment for today else default | ||
| 76 | + if (program.schedule && program.schedule[today]) { | ||
| 77 | + programSegmentId = program.schedule[today]; | ||
| 78 | + } else { | ||
| 79 | + programSegmentId = program.default; | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + this.programRepository | ||
| 83 | + .findById(programSegmentId) | ||
| 84 | + .then(programSegment => { | ||
| 85 | + resolve(programSegment); | ||
| 86 | + }).catch(error => { | ||
| 87 | + reject("program segment not found"); | ||
| 88 | + }); | ||
| 89 | + } else { | ||
| 90 | + reject('No Program found'); | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + }).catch(error => { | ||
| 94 | + reject(error); | ||
| 95 | + }); | ||
| 96 | + }); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/program-repository.ts
0 → 100644
| 1 | +export interface ProgramRepository { | ||
| 2 | + | ||
| 3 | + findById(id:string) : Promise<any>; | ||
| 4 | + | ||
| 5 | + findByIds(ids:Array<string>) : Promise<Array<any>>; | ||
| 6 | + | ||
| 7 | + findByType(type:string) : Promise<Array<any>>; | ||
| 8 | + | ||
| 9 | + replicate() : Promise<void>; | ||
| 10 | + | ||
| 11 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/util.ts
0 → 100644
| 1 | +export default class Util { | ||
| 2 | + | ||
| 3 | + static getISODate() : string { | ||
| 4 | + return (new Date()).toISOString().slice(0,10); | ||
| 5 | + } | ||
| 6 | + | ||
| 7 | + static getDateInMinutes() : number { | ||
| 8 | + let now = new Date(); | ||
| 9 | + return (now.getHours() * 60) + now.getMinutes(); | ||
| 10 | + } | ||
| 11 | + | ||
| 12 | + /** | ||
| 13 | + * convert a time input to minutes | ||
| 14 | + * e.g. 23:59 = 1439 | ||
| 15 | + */ | ||
| 16 | + static convertToMinutes(time:string) : number { | ||
| 17 | + let times = time.split(":"); | ||
| 18 | + let convered = (parseInt(times[0]) * 60) + parseInt(times[1]); | ||
| 19 | + return (convered >= 0 && convered <= 1439) ? convered : 0; | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + static calculateNextMinute() : number { | ||
| 23 | + return (60 - (Math.round((new Date()).getTime() / 1000) % 60)) * 1000; | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
tsconfig.json
0 → 100644
-
Please register or login to post a comment