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).
- 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
- 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.
rdio-scanner-darwin-amd64-v6.3.2.zip
rdio-scanner-darwin-arm64-v6.3.2.zip
rdio-scanner-freebsd-amd64-v6.3.2.zip
rdio-scanner-linux-386-v6.3.2.zip
rdio-scanner-linux-amd64-v6.3.2.zip
rdio-scanner-linux-arm64-v6.3.2.zip
rdio-scanner-linux-arm-v6.3.2.zip
rdio-scanner-windows-amd64-v6.3.2.zip
rdio-scanner-darwin-amd64-v6.3.3.zip
rdio-scanner-darwin-arm64-v6.3.3.zip
rdio-scanner-freebsd-amd64-v6.3.3.zip
rdio-scanner-linux-386-v6.3.3.zip
rdio-scanner-linux-amd64-v6.3.3.zip
rdio-scanner-linux-arm64-v6.3.3.zip
rdio-scanner-linux-arm-v6.3.3.zip
rdio-scanner-windows-amd64-v6.3.3.zip
**Happy Rdio scanning !**

View file

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

View file

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

View file

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

View file

@ -286,7 +286,7 @@ export class RdioScannerAdminService implements OnDestroy {
}
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> {

View file

@ -44,21 +44,21 @@
}
&.off::after {
background: #e63232;
background: rgb(255, 23, 68);
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 {
background: rgb(100, 230, 50);
background: rgb(0, 230, 118);;
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 {
background: rgb(230, 165, 50);
background: rgb(255, 234, 0);
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;
&.on {
background: rgb(100, 230, 50);
box-shadow: 0 0 6px 3px rgb(100, 230, 50);
background: rgb(0, 230, 118);
box-shadow: 0 0 6px 3px rgb(0, 230, 118);
&.blue {
background: rgb(50, 50, 230);
box-shadow: 0 0 6px 3px rgb(50, 50, 230);
background: rgb(41, 121, 255);
box-shadow: 0 0 6px 3px rgb(41, 121, 255);
}
&.cyan {
background: rgb(50, 230, 230);
box-shadow: 0 0 6px 3px rgb(50, 230, 230);
background: rgb(0, 229, 255);
box-shadow: 0 0 6px 3px rgb(0, 229, 255);
}
&.green {
background: rgb(100, 230, 50);
box-shadow: 0 0 6px 3px rgb(100, 230, 50);
background: rgb(0, 230, 118);
box-shadow: 0 0 6px 3px rgb(0, 230, 118);
}
&.magenta {
background: rgb(230, 50, 230);
box-shadow: 0 0 6px 3px rgb(230, 50, 230);
background: rgb(213, 0, 249);
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 {
background: rgb(230, 50, 50);
box-shadow: 0 0 6px 3px rgb(230, 50, 50);
background: rgb(255, 23, 68);
box-shadow: 0 0 6px 3px rgb(255, 23, 68);
}
&.white {
background: rgb(230, 230, 230);
box-shadow: 0 0 6px 3px rgb(230, 230, 230);
background: rgb(255, 255, 255);
box-shadow: 0 0 6px 3px rgb(255, 255, 255);
}
&.yellow {
background: rgb(230, 230, 50);
box-shadow: 0 0 6px 3px rgb(230, 230, 50);
background: rgb(255, 234, 0);
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;
callError = '0';
callFrequency: string = this.formatFrequency(0);
callHistory: RdioScannerCall[] | undefined;
callHistory: RdioScannerCall[] = new Array<RdioScannerCall>(5);
callPrevious: RdioScannerCall | undefined;
callProgress = new Date(0, 0, 0, 0, 0, 0);
callQueue = 0;
@ -101,6 +101,9 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
playbackMode = false;
replayOffset = 0;
replayTimer: Subscription | undefined;
get showListenersCount(): boolean {
return this.config?.showListenersCount || false;
}
@ -125,9 +128,7 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
private rdioScannerService: RdioScannerService,
private ngChangeDetectorRef: ChangeDetectorRef,
private ngFormBuilder: FormBuilder,
) {
this.callHistory = this.rdioScannerService.getHistory();
}
) { }
authenticate(password = this.authForm.value.password): void {
this.authForm.disable();
@ -253,7 +254,27 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
if (!this.livefeedPaused && (this.call || this.callPrevious)) {
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 {
this.rdioScannerService.beep(RdioScannerBeepStyle.Denied);
@ -376,10 +397,6 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
this.authForm.get('password')?.setErrors({ expired: true });
}
if ('history' in event) {
this.callHistory = event.history || [];
}
if ('holdSys' in event) {
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}` : '';
}
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;
@ -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';

View file

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

View file

@ -105,7 +105,6 @@ export interface RdioScannerEvent {
call?: RdioScannerCall;
config?: RdioScannerConfig;
expired?: boolean;
history?: RdioScannerCall[];
holdSys?: boolean;
holdTg?: boolean;
linked?: boolean;
@ -160,7 +159,7 @@ export interface RdioScannerSearchOptions {
export interface RdioScannerSystem {
id: number;
label: string;
led?: 'blue' | 'cyan' | 'green' | 'magenta' | 'red' | 'white' | 'yellow';
led?: 'blue' | 'cyan' | 'green' | 'magenta' | 'orange' | 'red' | 'white' | 'yellow';
order?: number;
talkgroups: RdioScannerTalkgroup[];
units: RdioScannerUnit[];
@ -171,7 +170,7 @@ export interface RdioScannerTalkgroup {
group: string;
id: number;
label: string;
led?: 'blue' | 'cyan' | 'green' | 'magenta' | 'red' | 'white' | 'yellow';
led?: 'blue' | 'cyan' | 'green' | 'magenta' | 'orange' | 'red' | 'white' | 'yellow';
name: 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 ~ % cd rdio-scanner
rdio@macos rdio-scanner % unzip \
> ~/Downloads/rdio-scanner-darwin-arm64-v6.3.2.zip
Archive: /Users/rdio/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.3.zip
inflating: rdio-scanner
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 Scanner v6.3.2
Rdio Scanner v6.3.3
----------------------------------
2022/05/21 08:38:06 server started
2022/05/21 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 server started
2022/05/24 08:38:06 main interface at http://macos.local:3000
2022/05/24 08:38:06 admin interface at http://macos.local:3000/admin
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 Scanner v6.3.2
Rdio Scanner v6.3.3
----------------------------------
2022/05/21 08:48:03 server started
2022/05/21 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 server started
2022/05/24 08:48:03 main interface at http://macos.local
2022/05/24 08:48:03 admin interface at http://macos.local/admin
## 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_listen :443
Rdio Scanner v6.3.2
Rdio Scanner v6.3.3
----------------------------------
2022/05/21 08:50:58 server started
2022/05/21 08:50:58 main interface at http://macos.local
2022/05/21 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 server started
2022/05/24 08:50:58 main interface at http://macos.local
2022/05/24 08:50:58 main interface at https://macos.local
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.
@ -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_listen :443 \
> -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.
@ -132,12 +132,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an
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/21 08:54:08 main interface at http://macos.local
2022/05/21 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 server started
2022/05/24 08:54:08 main interface at http://macos.local
2022/05/24 08:54:08 main interface at https://macos.local
2022/05/24 08:54:08 admin interface at https://macos.local/admin
## 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:~ $ cd rdio-scanner
rdio@pc-freebsd:~/rdio-scanner $ unzip \
> ~/rdio-scanner-freebsd-amd64-v6.3.2.zip
Archive: ../rdio-scanner-freebsd-amd64-v6.3.2.zip
> ~/rdio-scanner-freebsd-amd64-v6.3.3.zip
Archive: ../rdio-scanner-freebsd-amd64-v6.3.3.zip
extracting: rdio-scanner
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 Scanner v6.3.2
Rdio Scanner v6.3.3
----------------------------------
2022/05/21 08:16:36 server started
2022/05/21 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 server started
2022/05/24 08:16:36 main interface at http://pc-freebsd:3000
2022/05/24 08:16:36 admin interface at http://pc-freebsd:3000/admin
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:
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/21 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 server started
2022/05/24 08:19:38 main interface at http://pc-freebsd
2022/05/24 08:19:38 admin interface at http://pc-freebsd/admin
## 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_listen :443
Rdio Scanner v6.3.2
Rdio Scanner v6.3.3
----------------------------------
2022/05/21 10:34:29 server started
2022/05/21 10:34:29 main interface at http://pc-freebsd
2022/05/21 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 server started
2022/05/24 10:34:29 main interface at http://pc-freebsd
2022/05/24 10:34:29 main interface at https://pc-freebsd
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.
@ -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_listen :443 \
> -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.
@ -142,12 +142,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an
Password:
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/21 10:38:29 main interface at http://pc-freebsd
2022/05/21 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:28 server started
2022/05/24 10:38:29 main interface at http://pc-freebsd
2022/05/24 10:38:29 main interface at https://pc-freebsd
2022/05/24 10:38:29 admin interface at https://pc-freebsd/admin
## 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 ~]$ cd rdio-scanner
[rdio@pc-linux rdio-scanner]$ unzip \
> ~/Downloads/rdio-scanner-linux-amd64-v6.3.2.zip
Archive: /home/rdio/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.3.zip
inflating: rdio-scanner
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 Scanner v6.3.2
Rdio Scanner v6.3.3
----------------------------------
2022/05/21 09:11:48 server started
2022/05/21 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 server started
2022/05/24 09:11:48 main interface at http://pc-linux:3000
2022/05/24 09:11:48 admin interface at http://pc-linux:3000/admin
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
[sudo] password for rdio:
Rdio Scanner v6.3.2
Rdio Scanner v6.3.3
----------------------------------
2022/05/21 09:14:00 server started
2022/05/21 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 server started
2022/05/24 09:14:00 main interface at http://pc-linux
2022/05/24 09:14:00 admin interface at http://pc-linux/admin
## 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
[sudo] password for rdio:
Rdio Scanner v6.3.2
Rdio Scanner v6.3.3
----------------------------------
2022/05/21 09:16:47 server started
2022/05/21 09:16:47 main interface at http://pc-linux
2022/05/21 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 server started
2022/05/24 09:16:47 main interface at http://pc-linux
2022/05/24 09:16:47 main interface at https://pc-linux
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.
@ -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_listen :443 \
> -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.
@ -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
[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/21 09:20:40 main interface at http://pc-linux
2022/05/21 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 server started
2022/05/24 09:20:40 main interface at http://pc-linux
2022/05/24 09:20:40 main interface at https://pc-linux
2022/05/24 09:20:40 admin interface at https://pc-linux/admin
## 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\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.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
Rdio Scanner v6.3.2
Rdio Scanner v6.3.3
----------------------------------
2022/05/21 13:48:48 server started
2022/05/21 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 server started
2022/05/24 13:48:48 main interface at http://pc-windows:3000
2022/05/24 13:48:48 admin interface at http://pc-windows:3000/admin
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
Rdio Scanner v6.3.2
Rdio Scanner v6.3.3
----------------------------------
2022/05/21 10:53:31 server started
2022/05/21 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 server started
2022/05/24 10:53:31 main interface at http://pc-windows
2022/05/24 10:53:31 admin interface at http://pc-windows/admin
## 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_listen :443
Rdio Scanner v6.3.2
Rdio Scanner v6.3.3
----------------------------------
2022/05/21 11:05:43 server started
2022/05/21 11:05:43 main interface at http://pc-windows
2022/05/21 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 server started
2022/05/24 11:05:43 main interface at http://pc-windows
2022/05/24 11:05:43 main interface at https://pc-windows
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.
@ -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_listen :443 ^
-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.
@ -133,12 +133,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an
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/21 11:11:28 main interface at http://pc-windows
2022/05/21 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 server started
2022/05/24 11:11:28 main interface at http://pc-windows
2022/05/24 11:11:28 main interface at https://pc-windows
2022/05/24 11:11:28 admin interface at https://pc-windows/admin
\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. |
| ![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. |
| ![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. |
| ![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. |

View file

@ -371,20 +371,22 @@ func (admin *Admin) LoginHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
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)
return
}
attempt := admin.Attempts[r.RemoteAddr]
remoteAddr := GetRemoteAddr(r)
attempt := admin.Attempts[remoteAddr]
if attempt == nil {
admin.Attempts[r.RemoteAddr] = &AdminLoginAttempt{
admin.Attempts[remoteAddr] = &AdminLoginAttempt{
Count: 1,
Date: time.Now(),
}
attempt = admin.Attempts[r.RemoteAddr]
attempt = admin.Attempts[remoteAddr]
} else {
attempt.Count++
attempt.Date = time.Now()
@ -395,7 +397,7 @@ func (admin *Admin) LoginHandler(w http.ResponseWriter, r *http.Request) {
admin.Controller.Logs.LogEvent(
admin.Controller.Database,
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.Database,
LogLevelWarn,
fmt.Sprintf("invalid login attempt for ip=%v", r.RemoteAddr),
fmt.Sprintf("invalid login attempt for ip=%v", remoteAddr),
)
w.WriteHeader(http.StatusUnauthorized)
return

View file

@ -152,10 +152,10 @@ func (calls *Calls) GetCall(id uint, db *Database) (*Call, error) {
calls.mutex.Lock()
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)
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)
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.Audio, &call.AudioName, &call.AudioType, &dateTime, &frequencies, &call.Frequency, &patches, &call.Source, &sources, &call.System, &call.Talkgroup)
if err != nil && err != sql.ErrNoRows {
return nil, fmt.Errorf("getcall: %v, %v", err, query)
}

View file

@ -19,6 +19,7 @@ import (
"errors"
"fmt"
"log"
"net/http"
"sync"
"time"
@ -36,9 +37,10 @@ type Client struct {
TagsMap TagsMap
Livefeed *Livefeed
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 (
pongWait = 60 * time.Second
pingPeriod = pongWait / 10 * 9
@ -57,8 +59,9 @@ func (client *Client) Init(controller *Controller, conn *websocket.Conn) error {
client.Access = &Access{}
client.Controller = controller
client.Conn = conn
client.Send = make(chan *Message)
client.Livefeed = NewLivefeed()
client.Send = make(chan *Message)
client.request = request
controller.Register <- client
@ -148,7 +151,15 @@ func (client *Client) Init(controller *Controller, conn *websocket.Conn) error {
return nil
}
func (client *Client) GetRemoteAddr() string {
return GetRemoteAddr(client.request)
}
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.GroupsMap = groups.GetGroupsMap(&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) {
defer func() {
recover()
}()
client.Send <- &Message{
Command: MessagecommandListenersCount,
Payload: count,
@ -218,6 +233,10 @@ func (clients *Clients) Count() int {
}
func (clients *Clients) EmitCall(call *Call, restricted bool) {
defer func() {
recover()
}()
clients.Map.Range(func(k interface{}, _ interface{}) bool {
switch c := k.(type) {
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) {
defer func() {
recover()
}()
count := clients.Count()
clients.Map.Range(func(k interface{}, _ interface{}) bool {
@ -265,6 +288,10 @@ func (clients *Clients) EmitListenersCount() {
}
func (clients *Clients) Remove(client *Client) {
defer func() {
recover()
}()
clients.Map.Delete(client)
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.Stdin = bytes.NewReader(call.Audio)
@ -497,7 +497,7 @@ func (controller *Controller) ProcessMessageCommandPin(client *Client, message *
controller.Logs.LogEvent(
controller.Database,
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}
return nil

View file

@ -59,7 +59,6 @@ type Dirwatch struct {
UsePolling bool `json:"usePolling"`
controller *Controller
dirs map[string]bool
pending map[string]*time.Timer
watcher *fsnotify.Watcher
}
@ -525,8 +524,9 @@ func (dirwatch *Dirwatch) parseMask(call *Call) {
func (dirwatch *Dirwatch) Start(controller *Controller) error {
var (
delay time.Duration
err error
delay time.Duration
err error
timers = map[string]*time.Timer{}
)
if dirwatch.Disabled {
@ -558,7 +558,7 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
newTimer := func(eventName string) *time.Timer {
return time.AfterFunc(delay, func() {
delete(dirwatch.pending, eventName)
delete(timers, eventName)
if _, err := os.Stat(eventName); err == nil {
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 {
if dirwatch.watcher == nil {
break
@ -582,10 +589,10 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
}
} else {
if dirwatch.pending[event.Name] != nil {
dirwatch.pending[event.Name].Stop()
if timers[event.Name] != nil {
timers[event.Name].Stop()
}
dirwatch.pending[event.Name] = newTimer(event.Name)
timers[event.Name] = newTimer(event.Name)
}
case fsnotify.Remove:
@ -598,11 +605,11 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
}
case fsnotify.Write:
if dirwatch.pending[event.Name] != nil {
if dirwatch.pending[event.Name] != nil {
dirwatch.pending[event.Name].Stop()
if timers[event.Name] != nil {
if timers[event.Name] != nil {
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:
if ok {
logError(err)
return
}
}
}
@ -647,11 +656,6 @@ func (dirwatch *Dirwatch) Stop() {
dirwatch.watcher.Close()
dirwatch.watcher = nil
}
for k := range dirwatch.pending {
dirwatch.pending[k].Stop()
delete(dirwatch.pending, k)
}
}
type Dirwatches struct {
@ -715,7 +719,7 @@ func (dirwatches *Dirwatches) Read(db *Database) error {
}
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 {
break

View file

@ -136,7 +136,7 @@ func main() {
}
client := &Client{}
if err = client.Init(controller, conn); err != nil {
if err = client.Init(controller, r, conn); err != nil {
log.Println(err)
}
@ -245,3 +245,17 @@ func main() {
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
const Version = "6.3.2"
const Version = "6.3.3"