Version 6.3.3

This commit is contained in:
Chrystian Huot 2022-05-24 11:02:19 -04:00
parent 6beae3869b
commit 4d0dbd0e8e
23 changed files with 261 additions and 193 deletions

View file

@ -19,6 +19,15 @@ _v6.3.2_
- Fix `semacquire` lockup in Clients (issue #177, #181, #182). - Fix `semacquire` lockup in Clients (issue #177, #181, #182).
- Replay button now replays from history if pressed multiple times quickly (issue #186). - Replay button now replays from history if pressed multiple times quickly (issue #186).
_v6.3.3_
- Fix concurrent map writes fatal error in dirwatch (issue #187).
- Brighter LED colors and new orange color.
- Fix call id when retrieved from a MySQL database.
- Add loudnorm audio filter to the ffmpeg audio conversion.
- Show the real IP address in the logs taking into account if behind a proxy.
- Fix panic when emitting a call to clients.
## Version 6.2 ## Version 6.2
- New max clients options which is 200 by default. - New max clients options which is 200 by default.

View file

@ -27,13 +27,13 @@ Clone the official repository on your computer and start the build process.
When finished, you will find the precompiled versions for various platforms in the `dist` folder. When finished, you will find the precompiled versions for various platforms in the `dist` folder.
rdio-scanner-darwin-amd64-v6.3.2.zip rdio-scanner-darwin-amd64-v6.3.3.zip
rdio-scanner-darwin-arm64-v6.3.2.zip rdio-scanner-darwin-arm64-v6.3.3.zip
rdio-scanner-freebsd-amd64-v6.3.2.zip rdio-scanner-freebsd-amd64-v6.3.3.zip
rdio-scanner-linux-386-v6.3.2.zip rdio-scanner-linux-386-v6.3.3.zip
rdio-scanner-linux-amd64-v6.3.2.zip rdio-scanner-linux-amd64-v6.3.3.zip
rdio-scanner-linux-arm64-v6.3.2.zip rdio-scanner-linux-arm64-v6.3.3.zip
rdio-scanner-linux-arm-v6.3.2.zip rdio-scanner-linux-arm-v6.3.3.zip
rdio-scanner-windows-amd64-v6.3.2.zip rdio-scanner-windows-amd64-v6.3.3.zip
**Happy Rdio scanning !** **Happy Rdio scanning !**

View file

@ -16,8 +16,8 @@
################################################################################ ################################################################################
app := rdio-scanner app := rdio-scanner
date := 2022/05/21 date := 2022/05/24
ver := 6.3.2 ver := 6.3.3
client := $(wildcard client/*.json client/*.ts) client := $(wildcard client/*.json client/*.ts)
server := $(wildcard server/*.go) server := $(wildcard server/*.go)

View file

@ -1,12 +1,12 @@
{ {
"name": "rdio-scanner", "name": "rdio-scanner",
"version": "6.3.2", "version": "6.3.3",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "rdio-scanner", "name": "rdio-scanner",
"version": "6.3.2", "version": "6.3.3",
"license": "LICENSE", "license": "LICENSE",
"dependencies": { "dependencies": {
"@angular/animations": "^13.3.9", "@angular/animations": "^13.3.9",

View file

@ -38,5 +38,5 @@
"build": "ng build --base-href ./ --configuration production", "build": "ng build --base-href ./ --configuration production",
"start": "ng serve" "start": "ng serve"
}, },
"version": "6.3.2" "version": "6.3.3"
} }

View file

@ -286,7 +286,7 @@ export class RdioScannerAdminService implements OnDestroy {
} }
getLeds(): string[] { getLeds(): string[] {
return ['blue', 'cyan', 'green', 'magenta', 'red', 'white', 'yellow']; return ['blue', 'cyan', 'green', 'magenta', 'orage', 'red', 'white', 'yellow'];
} }
async getLogs(options: LogsQueryOptions): Promise<LogsQuery | undefined> { async getLogs(options: LogsQueryOptions): Promise<LogsQuery | undefined> {

View file

@ -44,21 +44,21 @@
} }
&.off::after { &.off::after {
background: #e63232; background: rgb(255, 23, 68);
box-shadow: 1px 1px 1px rgba(255, 255, 255, 0.7) inset, box-shadow: 1px 1px 1px rgba(255, 255, 255, 0.7) inset,
0 0 3px 1px rgb(230, 50, 50); 0 0 3px 1px rgb(255, 23, 68);
} }
&.on::after { &.on::after {
background: rgb(100, 230, 50); background: rgb(0, 230, 118);;
box-shadow: 1px 1px 1px rgba(255, 255, 255, 0.7) inset, box-shadow: 1px 1px 1px rgba(255, 255, 255, 0.7) inset,
0 0 3px 1px rgb(100, 230, 50); 0 0 3px 1px rgb(0, 230, 118);;
} }
&.partial::after { &.partial::after {
background: rgb(230, 165, 50); background: rgb(255, 234, 0);
box-shadow: 1px 1px 1px rgba(255, 255, 255, 0.7) inset, box-shadow: 1px 1px 1px rgba(255, 255, 255, 0.7) inset,
0 0 3px 1px rgb(230, 165, 50); 0 0 3px 1px rgb(255, 234, 0);
} }
} }

View file

@ -175,42 +175,47 @@
width: 24px; width: 24px;
&.on { &.on {
background: rgb(100, 230, 50); background: rgb(0, 230, 118);
box-shadow: 0 0 6px 3px rgb(100, 230, 50); box-shadow: 0 0 6px 3px rgb(0, 230, 118);
&.blue { &.blue {
background: rgb(50, 50, 230); background: rgb(41, 121, 255);
box-shadow: 0 0 6px 3px rgb(50, 50, 230); box-shadow: 0 0 6px 3px rgb(41, 121, 255);
} }
&.cyan { &.cyan {
background: rgb(50, 230, 230); background: rgb(0, 229, 255);
box-shadow: 0 0 6px 3px rgb(50, 230, 230); box-shadow: 0 0 6px 3px rgb(0, 229, 255);
} }
&.green { &.green {
background: rgb(100, 230, 50); background: rgb(0, 230, 118);
box-shadow: 0 0 6px 3px rgb(100, 230, 50); box-shadow: 0 0 6px 3px rgb(0, 230, 118);
} }
&.magenta { &.magenta {
background: rgb(230, 50, 230); background: rgb(213, 0, 249);
box-shadow: 0 0 6px 3px rgb(230, 50, 230); box-shadow: 0 0 6px 3px rgb(213, 0, 249);
}
&.orange {
background: rgb(255, 145, 0);
box-shadow: 0 0 6px 3px rgb(255, 145, 0);
} }
&.red { &.red {
background: rgb(230, 50, 50); background: rgb(255, 23, 68);
box-shadow: 0 0 6px 3px rgb(230, 50, 50); box-shadow: 0 0 6px 3px rgb(255, 23, 68);
} }
&.white { &.white {
background: rgb(230, 230, 230); background: rgb(255, 255, 255);
box-shadow: 0 0 6px 3px rgb(230, 230, 230); box-shadow: 0 0 6px 3px rgb(255, 255, 255);
} }
&.yellow { &.yellow {
background: rgb(230, 230, 50); background: rgb(255, 234, 0);
box-shadow: 0 0 6px 3px rgb(230, 230, 50); box-shadow: 0 0 6px 3px rgb(255, 234, 0);
} }
} }

View file

@ -52,7 +52,7 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
call: RdioScannerCall | undefined; call: RdioScannerCall | undefined;
callError = '0'; callError = '0';
callFrequency: string = this.formatFrequency(0); callFrequency: string = this.formatFrequency(0);
callHistory: RdioScannerCall[] | undefined; callHistory: RdioScannerCall[] = new Array<RdioScannerCall>(5);
callPrevious: RdioScannerCall | undefined; callPrevious: RdioScannerCall | undefined;
callProgress = new Date(0, 0, 0, 0, 0, 0); callProgress = new Date(0, 0, 0, 0, 0, 0);
callQueue = 0; callQueue = 0;
@ -101,6 +101,9 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
playbackMode = false; playbackMode = false;
replayOffset = 0;
replayTimer: Subscription | undefined;
get showListenersCount(): boolean { get showListenersCount(): boolean {
return this.config?.showListenersCount || false; return this.config?.showListenersCount || false;
} }
@ -125,9 +128,7 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
private rdioScannerService: RdioScannerService, private rdioScannerService: RdioScannerService,
private ngChangeDetectorRef: ChangeDetectorRef, private ngChangeDetectorRef: ChangeDetectorRef,
private ngFormBuilder: FormBuilder, private ngFormBuilder: FormBuilder,
) { ) { }
this.callHistory = this.rdioScannerService.getHistory();
}
authenticate(password = this.authForm.value.password): void { authenticate(password = this.authForm.value.password): void {
this.authForm.disable(); this.authForm.disable();
@ -253,7 +254,27 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
if (!this.livefeedPaused && (this.call || this.callPrevious)) { if (!this.livefeedPaused && (this.call || this.callPrevious)) {
this.rdioScannerService.beep(RdioScannerBeepStyle.Activate); this.rdioScannerService.beep(RdioScannerBeepStyle.Activate);
this.rdioScannerService.replay(); if (this.replayTimer instanceof Subscription) {
this.replayTimer.unsubscribe();
this.replayOffset = Math.min(this.callHistory.length, this.replayOffset + 1);
}
this.replayTimer = timer(750).subscribe(() => {
this.replayTimer = undefined;
this.replayOffset = 0;
});
if (this.call && !this.replayOffset) {
this.rdioScannerService.replay()
} else if (this.callPrevious !== this.callHistory[0]) {
if (this.replayOffset) {
this.rdioScannerService.play(this.callHistory[this.replayOffset - 1]);
} else {
this.rdioScannerService.replay()
}
} else {
this.rdioScannerService.play(this.callHistory[this.replayOffset]);
}
} else { } else {
this.rdioScannerService.beep(RdioScannerBeepStyle.Denied); this.rdioScannerService.beep(RdioScannerBeepStyle.Denied);
@ -376,10 +397,6 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
this.authForm.get('password')?.setErrors({ expired: true }); this.authForm.get('password')?.setErrors({ expired: true });
} }
if ('history' in event) {
this.callHistory = event.history || [];
}
if ('holdSys' in event) { if ('holdSys' in event) {
this.holdSys = event.holdSys || false; this.holdSys = event.holdSys || false;
} }
@ -530,6 +547,16 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
this.callUnit = typeof this.call.source === 'number' ? `${this.call.source}` : ''; this.callUnit = typeof this.call.source === 'number' ? `${this.call.source}` : '';
} }
if (
this.callPrevious &&
this.callPrevious.id !== this.call.id &&
!this.callHistory.find((call: RdioScannerCall) => call?.id === this.callPrevious?.id)
) {
this.callHistory.pop();
this.callHistory.unshift(this.callPrevious);
}
} }
const call = this.call || this.callPrevious; const call = this.call || this.callPrevious;
@ -544,7 +571,7 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
} }
} }
const colors = ['blue', 'cyan', 'green', 'magenta', 'red', 'white', 'yellow']; const colors = ['blue', 'cyan', 'green', 'magenta', 'orange', 'red', 'white', 'yellow'];
this.ledStyle = this.call && this.livefeedPaused ? 'on paused' : this.call ? 'on' : 'off'; this.ledStyle = this.call && this.livefeedPaused ? 'on paused' : this.call ? 'on' : 'off';

View file

@ -73,7 +73,7 @@ export class RdioScannerService implements OnDestroy {
private beepContext: AudioContext | undefined; private beepContext: AudioContext | undefined;
private call: RdioScannerCall | undefined; private call: RdioScannerCall | undefined;
private callHistory: RdioScannerCall[] = new Array<RdioScannerCall>(5); private callPrevious: RdioScannerCall | undefined;
private callQueue: RdioScannerCall[] = []; private callQueue: RdioScannerCall[] = [];
private categories: RdioScannerCategory[] = []; private categories: RdioScannerCategory[] = [];
@ -99,7 +99,6 @@ export class RdioScannerService implements OnDestroy {
private playbackRefreshing = false; private playbackRefreshing = false;
private replayDelay: Subscription | undefined; private replayDelay: Subscription | undefined;
private replayOffset = 0;
private skipDelay: Subscription | undefined; private skipDelay: Subscription | undefined;
@ -156,7 +155,7 @@ export class RdioScannerService implements OnDestroy {
}); });
} else { } else {
const call = this.call || this.callHistory[0]; const call = this.call || this.callPrevious;
if (call) { if (call) {
const sys = call.system; const sys = call.system;
@ -225,12 +224,8 @@ export class RdioScannerService implements OnDestroy {
}); });
} }
getHistory(): RdioScannerCall[] {
return this.callHistory;
}
holdSystem(options?: { resubscribe?: boolean }): void { holdSystem(options?: { resubscribe?: boolean }): void {
const call = this.call || this.callHistory[0]; const call = this.call || this.callPrevious;
if (call && this.livefeedMap) { if (call && this.livefeedMap) {
if (this.livefeedMapPriorToHoldSystem) { if (this.livefeedMapPriorToHoldSystem) {
@ -284,7 +279,7 @@ export class RdioScannerService implements OnDestroy {
} }
holdTalkgroup(options?: { resubscribe?: boolean }): void { holdTalkgroup(options?: { resubscribe?: boolean }): void {
const call = this.call || this.callHistory[0]; const call = this.call || this.callPrevious;
if (call && this.livefeedMap) { if (call && this.livefeedMap) {
if (this.livefeedMapPriorToHoldTalkgroup) { if (this.livefeedMapPriorToHoldTalkgroup) {
@ -508,17 +503,7 @@ export class RdioScannerService implements OnDestroy {
} }
replay(): void { replay(): void {
if (this.replayDelay instanceof Subscription) { this.play(this.call || this.callPrevious);
this.replayDelay.unsubscribe();
this.replayOffset = Math.min(this.callHistory.length - 1, this.replayOffset + 1);
}
this.replayDelay = timer(750).subscribe(() => {
this.replayDelay = undefined;
this.replayOffset = 0;
});
this.play(this.replayOffset > 0 ? this.callHistory[this.replayOffset] : this.call || this.callHistory[0]);
} }
searchCalls(options: RdioScannerSearchOptions): void { searchCalls(options: RdioScannerSearchOptions): void {
@ -573,11 +558,7 @@ export class RdioScannerService implements OnDestroy {
} }
if (this.call) { if (this.call) {
if (!this.callHistory.find((call: RdioScannerCall) => call?.id === this.call?.id)) { this.callPrevious = this.call;
this.callHistory.pop();
this.callHistory.unshift(this.call);
this.event.emit({ history: this.callHistory });
}
this.call = undefined; this.call = undefined;
} }
@ -771,7 +752,7 @@ export class RdioScannerService implements OnDestroy {
this.sendtoWebsocket(WebsocketCommand.Call, `${id}`, flags); this.sendtoWebsocket(WebsocketCommand.Call, `${id}`, flags);
} }
private getPlaybackQueueCount(id = this.call?.id || this.callHistory[0]?.id): number { private getPlaybackQueueCount(id = this.call?.id || this.callPrevious?.id): number {
let queueCount = 0; let queueCount = 0;
if (id && this.playbackList) { if (id && this.playbackList) {
@ -922,7 +903,7 @@ export class RdioScannerService implements OnDestroy {
return; return;
} }
const index = this.playbackList.results.findIndex((call) => call.id === this.callHistory[0]?.id); const index = this.playbackList.results.findIndex((call) => call.id === this.callPrevious?.id);
if (this.playbackList.options.sort === -1) { if (this.playbackList.options.sort === -1) {
if (index === -1) { if (index === -1) {

View file

@ -105,7 +105,6 @@ export interface RdioScannerEvent {
call?: RdioScannerCall; call?: RdioScannerCall;
config?: RdioScannerConfig; config?: RdioScannerConfig;
expired?: boolean; expired?: boolean;
history?: RdioScannerCall[];
holdSys?: boolean; holdSys?: boolean;
holdTg?: boolean; holdTg?: boolean;
linked?: boolean; linked?: boolean;
@ -160,7 +159,7 @@ export interface RdioScannerSearchOptions {
export interface RdioScannerSystem { export interface RdioScannerSystem {
id: number; id: number;
label: string; label: string;
led?: 'blue' | 'cyan' | 'green' | 'magenta' | 'red' | 'white' | 'yellow'; led?: 'blue' | 'cyan' | 'green' | 'magenta' | 'orange' | 'red' | 'white' | 'yellow';
order?: number; order?: number;
talkgroups: RdioScannerTalkgroup[]; talkgroups: RdioScannerTalkgroup[];
units: RdioScannerUnit[]; units: RdioScannerUnit[];
@ -171,7 +170,7 @@ export interface RdioScannerTalkgroup {
group: string; group: string;
id: number; id: number;
label: string; label: string;
led?: 'blue' | 'cyan' | 'green' | 'magenta' | 'red' | 'white' | 'yellow'; led?: 'blue' | 'cyan' | 'green' | 'magenta' | 'orange' | 'red' | 'white' | 'yellow';
name: string; name: string;
tag: string; tag: string;
} }

View file

@ -42,8 +42,8 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
rdio@macos ~ % mkdir rdio-scanner rdio@macos ~ % mkdir rdio-scanner
rdio@macos ~ % cd rdio-scanner rdio@macos ~ % cd rdio-scanner
rdio@macos rdio-scanner % unzip \ rdio@macos rdio-scanner % unzip \
> ~/Downloads/rdio-scanner-darwin-arm64-v6.3.2.zip > ~/Downloads/rdio-scanner-darwin-arm64-v6.3.3.zip
Archive: /Users/rdio/Downloads/rdio-scanner-darwin-arm64-v6.3.2.zip Archive: /Users/rdio/Downloads/rdio-scanner-darwin-arm64-v6.3.3.zip
inflating: rdio-scanner inflating: rdio-scanner
inflating: rdio-scanner.pdf inflating: rdio-scanner.pdf
@ -51,11 +51,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
rdio@macos rdio-scanner % ./rdio-scanner rdio@macos rdio-scanner % ./rdio-scanner
Rdio Scanner v6.3.2 Rdio Scanner v6.3.3
---------------------------------- ----------------------------------
2022/05/21 08:38:06 server started 2022/05/24 08:38:06 server started
2022/05/21 08:38:06 main interface at http://macos.local:3000 2022/05/24 08:38:06 main interface at http://macos.local:3000
2022/05/21 08:38:06 admin interface at http://macos.local:3000/admin 2022/05/24 08:38:06 admin interface at http://macos.local:3000/admin
4. Access the administrative dashboard to finalize the configuration. 4. Access the administrative dashboard to finalize the configuration.
@ -77,11 +77,11 @@ Here we want our [Rdio Scanner](https://guthub.com/chuot/rdio-scanner) instance
rdio@macos rdio-scanner % ./rdio-scanner --listen :80 rdio@macos rdio-scanner % ./rdio-scanner --listen :80
Rdio Scanner v6.3.2 Rdio Scanner v6.3.3
---------------------------------- ----------------------------------
2022/05/21 08:48:03 server started 2022/05/24 08:48:03 server started
2022/05/21 08:48:03 main interface at http://macos.local 2022/05/24 08:48:03 main interface at http://macos.local
2022/05/21 08:48:03 admin interface at http://macos.local/admin 2022/05/24 08:48:03 admin interface at http://macos.local/admin
## Listening on a SSL port ## Listening on a SSL port
@ -95,12 +95,12 @@ You can use your own SSL certificates that match your domain name with `-ssl_cer
> -ssl_key_file mykey.key \ > -ssl_key_file mykey.key \
> -ssl_listen :443 > -ssl_listen :443
Rdio Scanner v6.3.2 Rdio Scanner v6.3.3
---------------------------------- ----------------------------------
2022/05/21 08:50:58 server started 2022/05/24 08:50:58 server started
2022/05/21 08:50:58 main interface at http://macos.local 2022/05/24 08:50:58 main interface at http://macos.local
2022/05/21 08:50:58 main interface at https://macos.local 2022/05/24 08:50:58 main interface at https://macos.local
2022/05/21 08:50:58 admin interface at https://macos.local/admin 2022/05/24 08:50:58 admin interface at https://macos.local/admin
If you don't want to worry about SSL certificates, you can use the built-in Let's Encrypt auto-cert feature. This requires that you have both port 80 (HTTP) and port 443 (HTTPS) open to the world. Also, your domain name should point to your IP address where [Rdio Scanner](https://github.com/chuot/rdio-scanner/) is running. The advantage of this approach is that everything is done automatically, no certificate request, no certificate renewal. If you don't want to worry about SSL certificates, you can use the built-in Let's Encrypt auto-cert feature. This requires that you have both port 80 (HTTP) and port 443 (HTTPS) open to the world. Also, your domain name should point to your IP address where [Rdio Scanner](https://github.com/chuot/rdio-scanner/) is running. The advantage of this approach is that everything is done automatically, no certificate request, no certificate renewal.
@ -118,7 +118,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y
> -ssl_auto_cert mydomain.com \ > -ssl_auto_cert mydomain.com \
> -ssl_listen :443 \ > -ssl_listen :443 \
> -config_save > -config_save
2022/05/21 08:52:24 rdio-scanner.ini file created 2022/05/24 08:52:24 rdio-scanner.ini file created
All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list. All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list.
@ -132,12 +132,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an
rdio@macos rdio-scanner % ./rdio-scanner rdio@macos rdio-scanner % ./rdio-scanner
Rdio Scanner v6.3.2 Rdio Scanner v6.3.3
---------------------------------- ----------------------------------
2022/05/21 08:54:08 server started 2022/05/24 08:54:08 server started
2022/05/21 08:54:08 main interface at http://macos.local 2022/05/24 08:54:08 main interface at http://macos.local
2022/05/21 08:54:08 main interface at https://macos.local 2022/05/24 08:54:08 main interface at https://macos.local
2022/05/21 08:54:08 admin interface at https://macos.local/admin 2022/05/24 08:54:08 admin interface at https://macos.local/admin
## Install Rdio Scanner as a service ## Install Rdio Scanner as a service

View file

@ -42,8 +42,8 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
rdio@pc-freebsd:~ $ mkdir rdio-scanner rdio@pc-freebsd:~ $ mkdir rdio-scanner
rdio@pc-freebsd:~ $ cd rdio-scanner rdio@pc-freebsd:~ $ cd rdio-scanner
rdio@pc-freebsd:~/rdio-scanner $ unzip \ rdio@pc-freebsd:~/rdio-scanner $ unzip \
> ~/rdio-scanner-freebsd-amd64-v6.3.2.zip > ~/rdio-scanner-freebsd-amd64-v6.3.3.zip
Archive: ../rdio-scanner-freebsd-amd64-v6.3.2.zip Archive: ../rdio-scanner-freebsd-amd64-v6.3.3.zip
extracting: rdio-scanner extracting: rdio-scanner
extracting: rdio-scanner.pdf extracting: rdio-scanner.pdf
@ -51,11 +51,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
rdio@pc-freebsd:~/rdio-scanner $ ./rdio-scanner rdio@pc-freebsd:~/rdio-scanner $ ./rdio-scanner
Rdio Scanner v6.3.2 Rdio Scanner v6.3.3
---------------------------------- ----------------------------------
2022/05/21 08:16:36 server started 2022/05/24 08:16:36 server started
2022/05/21 08:16:36 main interface at http://pc-freebsd:3000 2022/05/24 08:16:36 main interface at http://pc-freebsd:3000
2022/05/21 08:16:36 admin interface at http://pc-freebsd:3000/admin 2022/05/24 08:16:36 admin interface at http://pc-freebsd:3000/admin
4. Access the administrative dashboard to finalize the configuration. 4. Access the administrative dashboard to finalize the configuration.
@ -81,11 +81,11 @@ Here we want our [Rdio Scanner](https://guthub.com/chuot/rdio-scanner) instance
Password: Password:
root@pc-freebsd:/home/rdio/rdio-scanner # ./rdio-scanner -listen :80 root@pc-freebsd:/home/rdio/rdio-scanner # ./rdio-scanner -listen :80
Rdio Scanner v6.3.2 Rdio Scanner v6.3.3
---------------------------------- ----------------------------------
2022/05/21 08:19:38 server started 2022/05/24 08:19:38 server started
2022/05/21 08:19:38 main interface at http://pc-freebsd 2022/05/24 08:19:38 main interface at http://pc-freebsd
2022/05/21 08:19:38 admin interface at http://pc-freebsd/admin 2022/05/24 08:19:38 admin interface at http://pc-freebsd/admin
## Listening on a SSL port ## Listening on a SSL port
@ -101,12 +101,12 @@ You can use your own SSL certificates that match your domain name with `-ssl_cer
> -ssl_key_file mykey.key \ > -ssl_key_file mykey.key \
> -ssl_listen :443 > -ssl_listen :443
Rdio Scanner v6.3.2 Rdio Scanner v6.3.3
---------------------------------- ----------------------------------
2022/05/21 10:34:29 server started 2022/05/24 10:34:29 server started
2022/05/21 10:34:29 main interface at http://pc-freebsd 2022/05/24 10:34:29 main interface at http://pc-freebsd
2022/05/21 10:34:29 main interface at https://pc-freebsd 2022/05/24 10:34:29 main interface at https://pc-freebsd
2022/05/21 10:34:29 admin interface at https://pc-freebsd/admin 2022/05/24 10:34:29 admin interface at https://pc-freebsd/admin
If you don't want to worry about SSL certificates, you can use the built-in Let's Encrypt auto-cert feature. This requires that you have both port 80 (HTTP) and port 443 (HTTPS) open to the world. Also, your domain name should point to your IP address where [Rdio Scanner](https://github.com/chuot/rdio-scanner/) is running. The advantage of this approach is that everything is done automatically, no certificate request, no certificate renewal. If you don't want to worry about SSL certificates, you can use the built-in Let's Encrypt auto-cert feature. This requires that you have both port 80 (HTTP) and port 443 (HTTPS) open to the world. Also, your domain name should point to your IP address where [Rdio Scanner](https://github.com/chuot/rdio-scanner/) is running. The advantage of this approach is that everything is done automatically, no certificate request, no certificate renewal.
@ -126,7 +126,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y
> -ssl_auto_cert mydomain.com \ > -ssl_auto_cert mydomain.com \
> -ssl_listen :443 \ > -ssl_listen :443 \
> -config_save > -config_save
2022/05/21 10:37:00 rdio-scanner.ini file created 2022/05/24 10:37:00 rdio-scanner.ini file created
All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list. All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list.
@ -142,12 +142,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an
Password: Password:
root@pc-freebsd:/home/rdio/rdio-scanner # ./rdio-scanner root@pc-freebsd:/home/rdio/rdio-scanner # ./rdio-scanner
Rdio Scanner v6.3.2 Rdio Scanner v6.3.3
---------------------------------- ----------------------------------
2022/05/21 10:38:28 server started 2022/05/24 10:38:28 server started
2022/05/21 10:38:29 main interface at http://pc-freebsd 2022/05/24 10:38:29 main interface at http://pc-freebsd
2022/05/21 10:38:29 main interface at https://pc-freebsd 2022/05/24 10:38:29 main interface at https://pc-freebsd
2022/05/21 10:38:29 admin interface at https://pc-freebsd/admin 2022/05/24 10:38:29 admin interface at https://pc-freebsd/admin
## Install Rdio Scanner as a service ## Install Rdio Scanner as a service

View file

@ -42,8 +42,8 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
[rdio@pc-linux ~]$ mkdir rdio-scanner [rdio@pc-linux ~]$ mkdir rdio-scanner
[rdio@pc-linux ~]$ cd rdio-scanner [rdio@pc-linux ~]$ cd rdio-scanner
[rdio@pc-linux rdio-scanner]$ unzip \ [rdio@pc-linux rdio-scanner]$ unzip \
> ~/Downloads/rdio-scanner-linux-amd64-v6.3.2.zip > ~/Downloads/rdio-scanner-linux-amd64-v6.3.3.zip
Archive: /home/rdio/Downloads/rdio-scanner-linux-amd64-v6.3.2.zip Archive: /home/rdio/Downloads/rdio-scanner-linux-amd64-v6.3.3.zip
inflating: rdio-scanner inflating: rdio-scanner
inflating: rdio-scanner.pdf inflating: rdio-scanner.pdf
@ -51,11 +51,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
[rdio@pc-linux rdio-scanner]$ ./rdio-scanner [rdio@pc-linux rdio-scanner]$ ./rdio-scanner
Rdio Scanner v6.3.2 Rdio Scanner v6.3.3
---------------------------------- ----------------------------------
2022/05/21 09:11:48 server started 2022/05/24 09:11:48 server started
2022/05/21 09:11:48 main interface at http://pc-linux:3000 2022/05/24 09:11:48 main interface at http://pc-linux:3000
2022/05/21 09:11:48 admin interface at http://pc-linux:3000/admin 2022/05/24 09:11:48 admin interface at http://pc-linux:3000/admin
4. Access the administrative dashboard to finalize the configuration. 4. Access the administrative dashboard to finalize the configuration.
@ -78,11 +78,11 @@ Here we want our [Rdio Scanner](https://guthub.com/chuot/rdio-scanner) instance
[rdio@pc-linux rdio-scanner]$ sudo ./rdio-scanner -listen :80 [rdio@pc-linux rdio-scanner]$ sudo ./rdio-scanner -listen :80
[sudo] password for rdio: [sudo] password for rdio:
Rdio Scanner v6.3.2 Rdio Scanner v6.3.3
---------------------------------- ----------------------------------
2022/05/21 09:14:00 server started 2022/05/24 09:14:00 server started
2022/05/21 09:14:00 main interface at http://pc-linux 2022/05/24 09:14:00 main interface at http://pc-linux
2022/05/21 09:14:00 admin interface at http://pc-linux/admin 2022/05/24 09:14:00 admin interface at http://pc-linux/admin
## Listening on a SSL port ## Listening on a SSL port
@ -97,12 +97,12 @@ You can use your own SSL certificates that match your domain name with `-ssl_cer
> -ssl_listen :443 > -ssl_listen :443
[sudo] password for rdio: [sudo] password for rdio:
Rdio Scanner v6.3.2 Rdio Scanner v6.3.3
---------------------------------- ----------------------------------
2022/05/21 09:16:47 server started 2022/05/24 09:16:47 server started
2022/05/21 09:16:47 main interface at http://pc-linux 2022/05/24 09:16:47 main interface at http://pc-linux
2022/05/21 09:16:47 main interface at https://pc-linux 2022/05/24 09:16:47 main interface at https://pc-linux
2022/05/21 09:16:47 admin interface at https://pc-linux/admin 2022/05/24 09:16:47 admin interface at https://pc-linux/admin
If you don't want to worry about SSL certificates, you can use the built-in Let's Encrypt auto-cert feature. This requires that you have both port 80 (HTTP) and port 443 (HTTPS) open to the world. Also, your domain name should point to your IP address where [Rdio Scanner](https://github.com/chuot/rdio-scanner/) is running. The advantage of this approach is that everything is done automatically, no certificate request, no certificate renewal. If you don't want to worry about SSL certificates, you can use the built-in Let's Encrypt auto-cert feature. This requires that you have both port 80 (HTTP) and port 443 (HTTPS) open to the world. Also, your domain name should point to your IP address where [Rdio Scanner](https://github.com/chuot/rdio-scanner/) is running. The advantage of this approach is that everything is done automatically, no certificate request, no certificate renewal.
@ -120,7 +120,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y
> -ssl_auto_cert mydomain.com \ > -ssl_auto_cert mydomain.com \
> -ssl_listen :443 \ > -ssl_listen :443 \
> -config_save > -config_save
2022/05/21 09:19:29 rdio-scanner.ini file created 2022/05/24 09:19:29 rdio-scanner.ini file created
All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list. All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list.
@ -135,12 +135,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an
[rdio@pc-linux rdio-scanner]$ sudo ./rdio-scanner [rdio@pc-linux rdio-scanner]$ sudo ./rdio-scanner
[sudo] Mot de passe de rdio : [sudo] Mot de passe de rdio :
Rdio Scanner v6.3.2 Rdio Scanner v6.3.3
---------------------------------- ----------------------------------
2022/05/21 09:20:40 server started 2022/05/24 09:20:40 server started
2022/05/21 09:20:40 main interface at http://pc-linux 2022/05/24 09:20:40 main interface at http://pc-linux
2022/05/21 09:20:40 main interface at https://pc-linux 2022/05/24 09:20:40 main interface at https://pc-linux
2022/05/21 09:20:40 admin interface at https://pc-linux/admin 2022/05/24 09:20:40 admin interface at https://pc-linux/admin
## Install Rdio Scanner as a service ## Install Rdio Scanner as a service

View file

@ -44,7 +44,7 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
C:\Users\rdio> cd rdio-scanner C:\Users\rdio> cd rdio-scanner
C:\Users\rdio\rdio-scanner> tar -xvf ^ C:\Users\rdio\rdio-scanner> tar -xvf ^
..\downloads\rdio-scanner-windows-amd64-v6.3.2.zip ..\downloads\rdio-scanner-windows-amd64-v6.3.3.zip
x rdio-scanner.exe x rdio-scanner.exe
x rdio-scanner.pdf x rdio-scanner.pdf
@ -52,11 +52,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
C:\Users\rdio\rdio-scanner>rdio-scanner C:\Users\rdio\rdio-scanner>rdio-scanner
Rdio Scanner v6.3.2 Rdio Scanner v6.3.3
---------------------------------- ----------------------------------
2022/05/21 13:48:48 server started 2022/05/24 13:48:48 server started
2022/05/21 13:48:48 main interface at http://pc-windows:3000 2022/05/24 13:48:48 main interface at http://pc-windows:3000
2022/05/21 13:48:48 admin interface at http://pc-windows:3000/admin 2022/05/24 13:48:48 admin interface at http://pc-windows:3000/admin
4. Access the administrative dashboard to finalize the configuration. 4. Access the administrative dashboard to finalize the configuration.
@ -78,11 +78,11 @@ Here we want our [Rdio Scanner](https://guthub.com/chuot/rdio-scanner) instance
C:\Users\rdio\rdio-scanner>rdio-scanner -listen :80 C:\Users\rdio\rdio-scanner>rdio-scanner -listen :80
Rdio Scanner v6.3.2 Rdio Scanner v6.3.3
---------------------------------- ----------------------------------
2022/05/21 10:53:31 server started 2022/05/24 10:53:31 server started
2022/05/21 10:53:31 main interface at http://pc-windows 2022/05/24 10:53:31 main interface at http://pc-windows
2022/05/21 10:53:31 admin interface at http://pc-windows/admin 2022/05/24 10:53:31 admin interface at http://pc-windows/admin
## Listening on a SSL port ## Listening on a SSL port
@ -96,12 +96,12 @@ You can use your own SSL certificates that match your domain name with `-ssl_cer
-ssl_key_file meykey.key ^ -ssl_key_file meykey.key ^
-ssl_listen :443 -ssl_listen :443
Rdio Scanner v6.3.2 Rdio Scanner v6.3.3
---------------------------------- ----------------------------------
2022/05/21 11:05:43 server started 2022/05/24 11:05:43 server started
2022/05/21 11:05:43 main interface at http://pc-windows 2022/05/24 11:05:43 main interface at http://pc-windows
2022/05/21 11:05:43 main interface at https://pc-windows 2022/05/24 11:05:43 main interface at https://pc-windows
2022/05/21 11:05:43 admin interface at https://pc-windows/admin 2022/05/24 11:05:43 admin interface at https://pc-windows/admin
If you don't want to worry about SSL certificates, you can use the built-in Let's Encrypt auto-cert feature. This requires that you have both port 80 (HTTP) and port 443 (HTTPS) open to the world. Also, your domain name should point to your IP address where [Rdio Scanner](https://github.com/chuot/rdio-scanner/) is running. The advantage of this approach is that everything is done automatically, no certificate request, no certificate renewal. If you don't want to worry about SSL certificates, you can use the built-in Let's Encrypt auto-cert feature. This requires that you have both port 80 (HTTP) and port 443 (HTTPS) open to the world. Also, your domain name should point to your IP address where [Rdio Scanner](https://github.com/chuot/rdio-scanner/) is running. The advantage of this approach is that everything is done automatically, no certificate request, no certificate renewal.
@ -119,7 +119,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y
-ssl_auto_cert mydomain.com ^ -ssl_auto_cert mydomain.com ^
-ssl_listen :443 ^ -ssl_listen :443 ^
-config_save -config_save
2022/05/21 11:08:28 rdio-scanner.ini file created 2022/05/24 11:08:28 rdio-scanner.ini file created
All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list. All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list.
@ -133,12 +133,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an
C:\Users\rdio\rdio-scanner>rdio-scanner C:\Users\rdio\rdio-scanner>rdio-scanner
Rdio Scanner v6.3.2 Rdio Scanner v6.3.3
---------------------------------- ----------------------------------
2022/05/21 11:11:28 server started 2022/05/24 11:11:28 server started
2022/05/21 11:11:28 main interface at http://pc-windows 2022/05/24 11:11:28 main interface at http://pc-windows
2022/05/21 11:11:28 main interface at https://pc-windows 2022/05/24 11:11:28 main interface at https://pc-windows
2022/05/21 11:11:28 admin interface at https://pc-windows/admin 2022/05/24 11:11:28 admin interface at https://pc-windows/admin
\pagebreak{} \pagebreak{}

View file

@ -49,7 +49,7 @@ The color is _green_ by default, but can be customized by the system or by the t
| ![LIVE FEED](./images/webapp-control-livefeed-partial.png?raw=true) | When active, incoming audio will be played according to the active systems/talkgroups on the **SELECT TG** panel. The LED can also be _yellow_ if playing audio from the archive while **LIVE FEED** is inactive, this is called **playback mode**. Disabling **LIVE FEED** will also stop any playing audio and will clear the listening queue. | | ![LIVE FEED](./images/webapp-control-livefeed-partial.png?raw=true) | When active, incoming audio will be played according to the active systems/talkgroups on the **SELECT TG** panel. The LED can also be _yellow_ if playing audio from the archive while **LIVE FEED** is inactive, this is called **playback mode**. Disabling **LIVE FEED** will also stop any playing audio and will clear the listening queue. |
| ![HOLD SYS](./images/webapp-control-holdsys.png?raw=true) | Temporarily maintain the current system in live feed mode. | | ![HOLD SYS](./images/webapp-control-holdsys.png?raw=true) | Temporarily maintain the current system in live feed mode. |
| ![HOLD TG](./images/webapp-control-holdtg.png?raw=true) | Temporarily maintain the current talkgroup in live feed mode. | | ![HOLD TG](./images/webapp-control-holdtg.png?raw=true) | Temporarily maintain the current talkgroup in live feed mode. |
| ![REPLAY LAST](./images/webapp-control-replay.png?raw=true) | Replay the current audio from the beginning or the previous one if there is none active. | | ![REPLAY LAST](./images/webapp-control-replay.png?raw=true) | Replay the current audio from the beginning or the previous one if there is none active. Press repeatedly to replay items displayed in the history section. For example, press three times to replay the third history item. |
| ![SKIP NEXT](./images/webapp-control-skip.png?raw=true) | Stop the audio currently playing and play the next one in the listening queue. This is useful when playing boring or encrypted sound. | | ![SKIP NEXT](./images/webapp-control-skip.png?raw=true) | Stop the audio currently playing and play the next one in the listening queue. This is useful when playing boring or encrypted sound. |
| ![AVOID](./images/webapp-control-avoid.png?raw=true) | Activate and deactivate the talkgroup from the current or previous audio. | | ![AVOID](./images/webapp-control-avoid.png?raw=true) | Activate and deactivate the talkgroup from the current or previous audio. |
| ![SEARCH CALL](./images/webapp-control-search.png?raw=true) | Display the archived audio panel. | | ![SEARCH CALL](./images/webapp-control-search.png?raw=true) | Display the archived audio panel. |

View file

@ -371,20 +371,22 @@ func (admin *Admin) LoginHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method { switch r.Method {
case http.MethodPost: case http.MethodPost:
m := map[string]interface{}{} m := map[string]interface{}{}
err := json.NewDecoder(r.Body).Decode(&m)
if err != nil { if err := json.NewDecoder(r.Body).Decode(&m); err != nil {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
} }
attempt := admin.Attempts[r.RemoteAddr] remoteAddr := GetRemoteAddr(r)
attempt := admin.Attempts[remoteAddr]
if attempt == nil { if attempt == nil {
admin.Attempts[r.RemoteAddr] = &AdminLoginAttempt{ admin.Attempts[remoteAddr] = &AdminLoginAttempt{
Count: 1, Count: 1,
Date: time.Now(), Date: time.Now(),
} }
attempt = admin.Attempts[r.RemoteAddr] attempt = admin.Attempts[remoteAddr]
} else { } else {
attempt.Count++ attempt.Count++
attempt.Date = time.Now() attempt.Date = time.Now()
@ -395,7 +397,7 @@ func (admin *Admin) LoginHandler(w http.ResponseWriter, r *http.Request) {
admin.Controller.Logs.LogEvent( admin.Controller.Logs.LogEvent(
admin.Controller.Database, admin.Controller.Database,
LogLevelWarn, LogLevelWarn,
fmt.Sprintf("too many login attempts for ip=\"%v\"", r.RemoteAddr), fmt.Sprintf("too many login attempts for ip=\"%v\"", remoteAddr),
) )
} }
@ -418,7 +420,7 @@ func (admin *Admin) LoginHandler(w http.ResponseWriter, r *http.Request) {
admin.Controller.Logs.LogEvent( admin.Controller.Logs.LogEvent(
admin.Controller.Database, admin.Controller.Database,
LogLevelWarn, LogLevelWarn,
fmt.Sprintf("invalid login attempt for ip=%v", r.RemoteAddr), fmt.Sprintf("invalid login attempt for ip=%v", remoteAddr),
) )
w.WriteHeader(http.StatusUnauthorized) w.WriteHeader(http.StatusUnauthorized)
return return

View file

@ -152,10 +152,10 @@ func (calls *Calls) GetCall(id uint, db *Database) (*Call, error) {
calls.mutex.Lock() calls.mutex.Lock()
defer calls.mutex.Unlock() defer calls.mutex.Unlock()
call := Call{} call := Call{Id: id}
query := fmt.Sprintf("select `id`, `audio`, `audioName`, `audioType`, `DateTime`, `frequencies`, `frequency`, `patches`, `source`, `sources`, `system`, `talkgroup` from `rdioScannerCalls` where `id` = %v", id) query := fmt.Sprintf("select `audio`, `audioName`, `audioType`, `DateTime`, `frequencies`, `frequency`, `patches`, `source`, `sources`, `system`, `talkgroup` from `rdioScannerCalls` where `id` = %v", id)
err := db.Sql.QueryRow(query).Scan(&call.Id, &call.Audio, &call.AudioName, &call.AudioType, &dateTime, &frequencies, &call.Frequency, &patches, &call.Source, &sources, &call.System, &call.Talkgroup) err := db.Sql.QueryRow(query).Scan(&call.Audio, &call.AudioName, &call.AudioType, &dateTime, &frequencies, &call.Frequency, &patches, &call.Source, &sources, &call.System, &call.Talkgroup)
if err != nil && err != sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
return nil, fmt.Errorf("getcall: %v, %v", err, query) return nil, fmt.Errorf("getcall: %v, %v", err, query)
} }

View file

@ -19,6 +19,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"log" "log"
"net/http"
"sync" "sync"
"time" "time"
@ -36,9 +37,10 @@ type Client struct {
TagsMap TagsMap TagsMap TagsMap
Livefeed *Livefeed Livefeed *Livefeed
SystemsMap SystemsMap SystemsMap SystemsMap
request *http.Request
} }
func (client *Client) Init(controller *Controller, conn *websocket.Conn) error { func (client *Client) Init(controller *Controller, request *http.Request, conn *websocket.Conn) error {
const ( const (
pongWait = 60 * time.Second pongWait = 60 * time.Second
pingPeriod = pongWait / 10 * 9 pingPeriod = pongWait / 10 * 9
@ -57,8 +59,9 @@ func (client *Client) Init(controller *Controller, conn *websocket.Conn) error {
client.Access = &Access{} client.Access = &Access{}
client.Controller = controller client.Controller = controller
client.Conn = conn client.Conn = conn
client.Send = make(chan *Message)
client.Livefeed = NewLivefeed() client.Livefeed = NewLivefeed()
client.Send = make(chan *Message)
client.request = request
controller.Register <- client controller.Register <- client
@ -148,7 +151,15 @@ func (client *Client) Init(controller *Controller, conn *websocket.Conn) error {
return nil return nil
} }
func (client *Client) GetRemoteAddr() string {
return GetRemoteAddr(client.request)
}
func (client *Client) SendConfig(groups *Groups, options *Options, systems *Systems, tags *Tags) { func (client *Client) SendConfig(groups *Groups, options *Options, systems *Systems, tags *Tags) {
defer func() {
recover()
}()
client.SystemsMap = systems.GetScopedSystems(client, groups, tags, options.SortTalkgroups) client.SystemsMap = systems.GetScopedSystems(client, groups, tags, options.SortTalkgroups)
client.GroupsMap = groups.GetGroupsMap(&client.SystemsMap) client.GroupsMap = groups.GetGroupsMap(&client.SystemsMap)
client.TagsMap = tags.GetTagsMap(&client.SystemsMap) client.TagsMap = tags.GetTagsMap(&client.SystemsMap)
@ -171,6 +182,10 @@ func (client *Client) SendConfig(groups *Groups, options *Options, systems *Syst
} }
func (client *Client) SendListenersCount(count int) { func (client *Client) SendListenersCount(count int) {
defer func() {
recover()
}()
client.Send <- &Message{ client.Send <- &Message{
Command: MessagecommandListenersCount, Command: MessagecommandListenersCount,
Payload: count, Payload: count,
@ -218,6 +233,10 @@ func (clients *Clients) Count() int {
} }
func (clients *Clients) EmitCall(call *Call, restricted bool) { func (clients *Clients) EmitCall(call *Call, restricted bool) {
defer func() {
recover()
}()
clients.Map.Range(func(k interface{}, _ interface{}) bool { clients.Map.Range(func(k interface{}, _ interface{}) bool {
switch c := k.(type) { switch c := k.(type) {
case *Client: case *Client:
@ -231,6 +250,10 @@ func (clients *Clients) EmitCall(call *Call, restricted bool) {
} }
func (clients *Clients) EmitConfig(groups *Groups, options *Options, systems *Systems, tags *Tags, restricted bool) { func (clients *Clients) EmitConfig(groups *Groups, options *Options, systems *Systems, tags *Tags, restricted bool) {
defer func() {
recover()
}()
count := clients.Count() count := clients.Count()
clients.Map.Range(func(k interface{}, _ interface{}) bool { clients.Map.Range(func(k interface{}, _ interface{}) bool {
@ -265,6 +288,10 @@ func (clients *Clients) EmitListenersCount() {
} }
func (clients *Clients) Remove(client *Client) { func (clients *Clients) Remove(client *Client) {
defer func() {
recover()
}()
clients.Map.Delete(client) clients.Map.Delete(client)
close(client.Send) close(client.Send)

View file

@ -114,7 +114,7 @@ func (controller *Controller) ConvertAudio(call *Call) {
} }
} }
args = append(args, "-c:a", "aac", "-b:a", "32k", "-movflags", "frag_keyframe+empty_moov", "-f", "ipod", "-") args = append(args, "-af", "apad=whole_dur=3s,loudnorm=I=-16:TP=-1.5:LRA=11", "-c:a", "aac", "-b:a", "32k", "-movflags", "frag_keyframe+empty_moov", "-f", "ipod", "-")
cmd := exec.Command("ffmpeg", args...) cmd := exec.Command("ffmpeg", args...)
cmd.Stdin = bytes.NewReader(call.Audio) cmd.Stdin = bytes.NewReader(call.Audio)
@ -497,7 +497,7 @@ func (controller *Controller) ProcessMessageCommandPin(client *Client, message *
controller.Logs.LogEvent( controller.Logs.LogEvent(
controller.Database, controller.Database,
LogLevelWarn, LogLevelWarn,
fmt.Sprintf("invalid access code=\"%s\" address=\"%s\"", code, client.Conn.RemoteAddr().String()), fmt.Sprintf("invalid access code=\"%s\" address=\"%s\"", code, client.GetRemoteAddr()),
) )
client.Send <- &Message{Command: MessageCommandPin} client.Send <- &Message{Command: MessageCommandPin}
return nil return nil

View file

@ -59,7 +59,6 @@ type Dirwatch struct {
UsePolling bool `json:"usePolling"` UsePolling bool `json:"usePolling"`
controller *Controller controller *Controller
dirs map[string]bool dirs map[string]bool
pending map[string]*time.Timer
watcher *fsnotify.Watcher watcher *fsnotify.Watcher
} }
@ -525,8 +524,9 @@ func (dirwatch *Dirwatch) parseMask(call *Call) {
func (dirwatch *Dirwatch) Start(controller *Controller) error { func (dirwatch *Dirwatch) Start(controller *Controller) error {
var ( var (
delay time.Duration delay time.Duration
err error err error
timers = map[string]*time.Timer{}
) )
if dirwatch.Disabled { if dirwatch.Disabled {
@ -558,7 +558,7 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
newTimer := func(eventName string) *time.Timer { newTimer := func(eventName string) *time.Timer {
return time.AfterFunc(delay, func() { return time.AfterFunc(delay, func() {
delete(dirwatch.pending, eventName) delete(timers, eventName)
if _, err := os.Stat(eventName); err == nil { if _, err := os.Stat(eventName); err == nil {
dirwatch.Ingest(eventName) dirwatch.Ingest(eventName)
@ -566,6 +566,13 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
}) })
} }
defer func() {
for k := range timers {
timers[k].Stop()
delete(timers, k)
}
}()
for { for {
if dirwatch.watcher == nil { if dirwatch.watcher == nil {
break break
@ -582,10 +589,10 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
} }
} else { } else {
if dirwatch.pending[event.Name] != nil { if timers[event.Name] != nil {
dirwatch.pending[event.Name].Stop() timers[event.Name].Stop()
} }
dirwatch.pending[event.Name] = newTimer(event.Name) timers[event.Name] = newTimer(event.Name)
} }
case fsnotify.Remove: case fsnotify.Remove:
@ -598,11 +605,11 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
} }
case fsnotify.Write: case fsnotify.Write:
if dirwatch.pending[event.Name] != nil { if timers[event.Name] != nil {
if dirwatch.pending[event.Name] != nil { if timers[event.Name] != nil {
dirwatch.pending[event.Name].Stop() timers[event.Name].Stop()
} }
dirwatch.pending[event.Name] = newTimer(event.Name) timers[event.Name] = newTimer(event.Name)
} }
} }
} }
@ -610,6 +617,8 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
case err, ok := <-dirwatch.watcher.Errors: case err, ok := <-dirwatch.watcher.Errors:
if ok { if ok {
logError(err) logError(err)
return
} }
} }
} }
@ -647,11 +656,6 @@ func (dirwatch *Dirwatch) Stop() {
dirwatch.watcher.Close() dirwatch.watcher.Close()
dirwatch.watcher = nil dirwatch.watcher = nil
} }
for k := range dirwatch.pending {
dirwatch.pending[k].Stop()
delete(dirwatch.pending, k)
}
} }
type Dirwatches struct { type Dirwatches struct {
@ -715,7 +719,7 @@ func (dirwatches *Dirwatches) Read(db *Database) error {
} }
for rows.Next() { for rows.Next() {
dirwatch := &Dirwatch{pending: map[string]*time.Timer{}} dirwatch := &Dirwatch{}
if err = rows.Scan(&id, &delay, &dirwatch.DeleteAfter, &dirwatch.Directory, &dirwatch.Disabled, &extension, &frequency, &mask, &order, &systemId, &talkgroupId, &kind, &dirwatch.UsePolling); err != nil { if err = rows.Scan(&id, &delay, &dirwatch.DeleteAfter, &dirwatch.Directory, &dirwatch.Disabled, &extension, &frequency, &mask, &order, &systemId, &talkgroupId, &kind, &dirwatch.UsePolling); err != nil {
break break

View file

@ -136,7 +136,7 @@ func main() {
} }
client := &Client{} client := &Client{}
if err = client.Init(controller, conn); err != nil { if err = client.Init(controller, r, conn); err != nil {
log.Println(err) log.Println(err)
} }
@ -245,3 +245,17 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
} }
func GetRemoteAddr(r *http.Request) string {
for _, addr := range strings.Split(r.Header.Get("X-Forwarded-For"), ",") {
if ip := strings.Split(addr, ":"); len(ip[0]) > 0 {
return ip[0]
}
}
if ip := strings.Split(r.RemoteAddr, ":"); len(ip[0]) > 0 {
return ip[0]
}
return "unknown"
}

View file

@ -15,4 +15,4 @@
package main package main
const Version = "6.3.2" const Version = "6.3.3"