Version 4.1.1

This commit is contained in:
Chrystian Huot 2020-05-29 12:24:34 -04:00
parent a37861ee99
commit 349d5b2b64
10 changed files with 108 additions and 65 deletions

View file

@ -16,7 +16,7 @@ Please click the **star button** at the top of the page on *github* to express y
Playing calls from the `SEARCH CALL` panel while `LIVE FEED` being `OFF` will play the call sequence according to the search filters. Note that the `LIVE FEED` indicator lights `yellow` when continuous offline play mode is active.
To stop continuous offline play mode, press the `STOP BUTTON` on the `SEARCH CALL` panel or press the `LIVE FEED` button twice.
To stop continuous offline play mode, press the `STOP BUTTON` on the `SEARCH CALL` panel or press the `LIVE FEED` button.
Note that this mode does not take into account the system/talkgroup from the `SELECT TG` panel. `HOLD SYS`, `HOLD TG` and `AVOID` buttons will also be disabled.

View file

@ -34,5 +34,5 @@
"build": "ng build --prod $*",
"start": "ng serve"
},
"version": "4.1.0"
"version": "4.1.1"
}

View file

@ -17,17 +17,20 @@
<mat-cell *matCellDef="let row">
<ng-container *ngIf="row">
<ng-container *ngIf="config.allowDownload && downloadMode.checked">
<button (click)="download(row.id)" mat-icon-button>
<button mat-icon-button (click)="download(row.id)">
<mat-icon>save_alt</mat-icon>
</button>
</ng-container>
<ng-container
*ngIf="!liveFeedPaused && (!config.allowDownload || !downloadMode.checked)">
<button (click)="row?.id === call?.id ? stop() : play(row.id)" mat-icon-button>
<ng-container *ngIf="row?.id === call?.id">
<button mat-icon-button (click)="row?.id === call?.id ? stop() : play(row.id)">
<ng-container *ngIf="call && row?.id === call?.id">
<mat-icon>stop</mat-icon>
</ng-container>
<ng-container *ngIf="row?.id !== call?.id">
<ng-container *ngIf="callPending === row?.id">
<mat-icon class="spinning">cached</mat-icon>
</ng-container>
<ng-container *ngIf="callPending !== row?.id && row?.id !== call?.id">
<mat-icon>play_arrow</mat-icon>
</ng-container>
</button>
@ -82,8 +85,8 @@
</mat-table>
<mat-progress-bar color="primary" [mode]="searchResultsPending ? 'query' : 'determinate'">
</mat-progress-bar>
<mat-paginator [disabled]="liveFeedOffline || searchResultsPending" [length]="list?.count" [hidePageSize]="true"
[pageSize]="searchResults.value.length" [showFirstLastButtons]="true">
<mat-paginator [disabled]="liveFeedOffline || searchResultsPending" [length]="list?.count"
[hidePageSize]="true" [pageSize]="searchResults.value.length" [showFirstLastButtons]="true">
</mat-paginator>
</div>
<form class="rdio-search-form" [formGroup]="searchForm" autocomplete="off">
@ -216,7 +219,8 @@
<div *ngIf="config?.useLed" class="rdio-status" fxLayout="row" fxLayoutAlign="end">
<span class="rdio-led" [ngClass]="ledClass"></span>
</div>
<div class="rdio-display" [ngClass]="{ idle: !config || (config?.useDimmer && !dimmerDelay) }" (dblclick)="toggleFullscreen()">
<div class="rdio-display" [ngClass]="{ idle: !config || (config?.useDimmer && !dimmerDelay) }"
(dblclick)="toggleFullscreen()">
<div class="rdio-display-row margin" fxLayout="row" fxLayoutAlign="space-between">
<div>
<span>
@ -348,7 +352,7 @@
<button class="rdio-button" (click)="replay()">
REPLAY<br>LAST
</button>
<button class="rdio-button" (click)="skip(true)">
<button class="rdio-button" (click)="skip({ delay: false })">
SKIP<br>NEXT
</button>
<button class="rdio-button" (click)="avoid()">

View file

@ -404,6 +404,23 @@
}
}
.spinning {
animation-duration: 1000ms;
animation-iteration-count: infinite;
animation-name: spin;
animation-timing-function: linear;
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
}
@media (min-width: 720px) {
.rdio-select {
.rdio-button {

View file

@ -70,6 +70,7 @@ export class AppRdioScannerComponent implements OnDestroy, OnInit {
callError = '0';
callFrequency: string = this.formatFrequency(0);
callHistory: RdioScannerCall[] = new Array<RdioScannerCall>(5);
callPending: string = null;
callPrevious: RdioScannerCall;
callProgress = new Date(0, 0, 0, 0, 0, 0);
callQueue = 0;
@ -206,9 +207,12 @@ export class AppRdioScannerComponent implements OnDestroy, OnInit {
if (this.auth) {
this.authPassword.focus();
} else {
} else if (this.liveFeedOffline) {
this.liveFeedOffline = false;
this.appRdioScannerService.stop();
} else {
this.appRdioScannerService.liveFeed();
this.updateDimmer();
@ -248,7 +252,7 @@ export class AppRdioScannerComponent implements OnDestroy, OnInit {
this.clockRefresh = setTimeout(() => setClock(), (60 - this.clock.getSeconds()) * 1000);
this.ngChangeDetectorRef.markForCheck();
this.ngDetectChanges();
};
setClock();
@ -299,15 +303,21 @@ export class AppRdioScannerComponent implements OnDestroy, OnInit {
if (event.call) {
this.call = this.transformCall(event.call);
if (this.callPending === this.call.id) {
this.callPending = null;
}
this.updateDimmer();
} else {
this.callPrevious = this.call;
if (this.call) {
this.callPrevious = this.call;
this.call = null;
this.call = null;
if (this.liveFeedOffline) {
this.playNextCall();
if (this.liveFeedOffline) {
this.playNextCall();
}
}
}
}
@ -455,18 +465,22 @@ export class AppRdioScannerComponent implements OnDestroy, OnInit {
}
play(id: string): void {
if (!this.liveFeedActive && !this.liveFeedOffline) {
this.liveFeedOffline = true;
if (!this.callPending) {
if (!this.liveFeedActive && !this.liveFeedOffline) {
this.liveFeedOffline = true;
if (this.holdSys) {
this.appRdioScannerService.holdSystem();
if (this.holdSys) {
this.appRdioScannerService.holdSystem();
} else if (this.holdTg) {
this.appRdioScannerService.holdTalkgroup();
} else if (this.holdTg) {
this.appRdioScannerService.holdTalkgroup();
}
}
}
this.appRdioScannerService.loadAndPlay(id);
this.callPending = id;
this.appRdioScannerService.loadAndPlay(id);
}
}
replay(): void {
@ -568,12 +582,12 @@ export class AppRdioScannerComponent implements OnDestroy, OnInit {
}
}
skip(nodelay?: boolean): void {
skip(options: any): void {
if (this.auth) {
this.authPassword.focus();
} else {
this.appRdioScannerService.skip(nodelay);
this.appRdioScannerService.skip(options);
this.updateDimmer();
}
@ -656,6 +670,8 @@ export class AppRdioScannerComponent implements OnDestroy, OnInit {
}
}
this.callPending = nextId;
this.appRdioScannerService.loadAndPlay(nextId);
} else {
@ -684,6 +700,8 @@ export class AppRdioScannerComponent implements OnDestroy, OnInit {
}
}
this.callPending = nextId;
this.appRdioScannerService.loadAndPlay(nextId);
} else {
@ -750,9 +768,9 @@ export class AppRdioScannerComponent implements OnDestroy, OnInit {
this.ngDetectChanges();
}, 4000);
}
this.ngDetectChanges();
this.ngDetectChanges();
}
}
private updateDisplay(time = this.callTime): void {

View file

@ -74,6 +74,8 @@ export class AppRdioScannerService implements OnDestroy {
private webSocket: WebSocket;
private wsMessagePending = false;
constructor(@Inject(DOCUMENT) private document: Document) {
this.bootstrapAudio();
@ -302,17 +304,11 @@ export class AppRdioScannerService implements OnDestroy {
play(call?: RdioScannerCall): void {
if (this.audioContext && !this.liveFeedPaused) {
if (!call && !this.call && this.callQueue.length) {
if (!call && !this.skipDelay && !this.call && this.callQueue.length) {
call = this.callQueue.shift();
this.event.emit({ queue: this.callQueue.length });
}
if (call && call.audio) {
this.stop(false);
this.call = call;
if (call?.audio?.data?.length) {
const arrayBuffer = new ArrayBuffer(call.audio.data.length);
const arrayBufferView = new Uint8Array(arrayBuffer);
@ -321,9 +317,13 @@ export class AppRdioScannerService implements OnDestroy {
}
this.audioContext.decodeAudioData(arrayBuffer, (buffer) => {
this.event.emit({ call });
this.audioContext.resume().then(() => {
this.stop({ emit: false });
this.call = call;
this.event.emit({ call, queue: this.callQueue.length });
this.audioSource = this.audioContext.createBufferSource();
this.audioSource.buffer = buffer;
@ -340,8 +340,15 @@ export class AppRdioScannerService implements OnDestroy {
this.event.emit({ time: this.audioContext.currentTime - this.audioStartTime });
}
}, 500);
});
}, () => this.skip());
}).catch(() => this.skip());
}, () => {
this.event.emit({ call: null, queue: this.callQueue.length });
this.skip();
});
} else if (call) {
this.event.emit({ call: null, queue: this.callQueue.length });
}
}
}
@ -369,29 +376,22 @@ export class AppRdioScannerService implements OnDestroy {
this.wsSend(WsCommand.ListCall, options);
}
skip(nodelay = false): void {
this.stop(!this.callQueue.length);
skip(options?: { delay?: boolean }): void {
this.stop();
if (nodelay) {
if (this.skipDelay) {
this.skipDelay = clearTimeout(this.skipDelay);
}
this.play();
} else {
if (!this.skipDelay) {
this.skipDelay = setTimeout(() => {
this.skipDelay = undefined;
this.play();
}, 1000);
}, typeof options?.delay !== 'boolean' || options.delay ? 1000 : 0);
}
}
stop(emitEvent = true): void {
stop(options?: { emit?: boolean }): void {
if (this.audioSource) {
if (this.audioTimer !== null) {
clearInterval(this.audioTimer);
if (this.audioTimer) {
this.audioTimer = clearInterval(this.audioTimer);
}
this.audioSource.onended = null;
@ -400,18 +400,16 @@ export class AppRdioScannerService implements OnDestroy {
this.audioSource = null;
this.audioStartTime = NaN;
this.audioTimer = null;
}
if (this.call) {
this.callPrevious = this.call;
this.call = null;
}
if (emitEvent) {
this.event.emit({ call: null });
}
if (typeof options?.emit !== 'boolean' || options.emit) {
this.event.emit({ call: null });
}
}
@ -604,6 +602,8 @@ export class AppRdioScannerService implements OnDestroy {
}
if (Array.isArray(message)) {
this.wsMessagePending = false;
switch (message[0]) {
case WsCommand.Call:
switch (message[2]) {
@ -616,7 +616,9 @@ export class AppRdioScannerService implements OnDestroy {
break;
default:
this.queue(message[1]);
if (this.liveFeedActive) {
this.queue(message[1]);
}
}
break;
@ -713,7 +715,7 @@ export class AppRdioScannerService implements OnDestroy {
}
private wsSend(command: string, payload?: any, flags?: any): void {
if (this.webSocket instanceof WebSocket && this.webSocket.readyState === 1) {
if (this.webSocket instanceof WebSocket && this.webSocket.readyState === 1 && !this.wsMessagePending) {
const message = [command, payload];
if (flags !== null && flags !== undefined) {
@ -721,6 +723,8 @@ export class AppRdioScannerService implements OnDestroy {
}
this.webSocket.send(JSON.stringify(message));
this.wsMessagePending = true;
}
}
}

View file

@ -6,7 +6,7 @@
<link rel="manifest" href="manifest.webmanifest">
<link rel="stylesheet" href="///fonts.googleapis.com/css?family=Roboto:400,500|Material+Icons&amp;display=swap">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Rdio Scanner</title>
<meta name="theme-color" content="#1e1e1e">
</head>

View file

@ -8,5 +8,5 @@
"start": "node run.js",
"update": "node update.js"
},
"version": "4.1.0"
"version": "4.1.1"
}

View file

@ -124,6 +124,7 @@ class Controller {
async getCall(id, scope) {
const where = scope !== null && typeof scope === 'object' ? {
[Op.and]: [
{ id },
{
[Op.or]: Object.keys(scope).map((sys) => ({
system: sys,
@ -132,7 +133,6 @@ class Controller {
},
}), []),
},
{ id },
],
} : { id };

View file

@ -34,5 +34,5 @@
"migrate": "npx sequelize db:migrate --config lib/sequelize/config.js --migrations-path lib/rdio-scanner/migrations",
"start": "nodemon index"
},
"version": "4.1.0"
"version": "4.1.1"
}