mirror of
https://github.com/chuot/rdio-scanner.git
synced 2026-08-13 16:33:01 -06:00
Version 4.1.1
This commit is contained in:
parent
a37861ee99
commit
349d5b2b64
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -34,5 +34,5 @@
|
|||
"build": "ng build --prod $*",
|
||||
"start": "ng serve"
|
||||
},
|
||||
"version": "4.1.0"
|
||||
"version": "4.1.1"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()">
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<link rel="manifest" href="manifest.webmanifest">
|
||||
<link rel="stylesheet" href="///fonts.googleapis.com/css?family=Roboto:400,500|Material+Icons&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>
|
||||
|
|
|
|||
|
|
@ -8,5 +8,5 @@
|
|||
"start": "node run.js",
|
||||
"update": "node update.js"
|
||||
},
|
||||
"version": "4.1.0"
|
||||
"version": "4.1.1"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue