From d03f0b1fef17bb0acdbb18a86af6ad3da95061c7 Mon Sep 17 00:00:00 2001 From: Chrystian Huot Date: Fri, 9 Sep 2022 11:52:20 -0400 Subject: [PATCH] Version 6.5.4 --- CHANGELOG.md | 8 + COMPILING.md | 16 +- Makefile | 4 +- README.md | 3 +- client/package-lock.json | 4 +- client/package.json | 2 +- .../rdio-scanner/admin/admin.service.ts | 8 +- .../config/dir-watch/dir-watch.component.html | 56 +++--- docs/docker/README.md | 10 +- docs/platforms/darwin.md | 45 ++--- docs/platforms/freebsd.md | 45 ++--- docs/platforms/linux.md | 45 ++--- docs/platforms/windows.md | 43 ++--- server/call.go | 6 +- server/client.go | 97 +++++----- server/command.go | 8 +- server/controller.go | 9 +- server/dirwatch.go | 178 ++++++++++++------ server/ffmpeg.go | 2 +- server/main.go | 4 +- server/parsers.go | 59 +++++- server/version.go | 2 +- 22 files changed, 398 insertions(+), 256 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cf3f887a..00f04d13f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,14 @@ _v6.5.3_ - Add filename to dirwatch error messages (issue #248). - Dirwatch.type=default now defaults to the current date and time if none are provided by the metatags (discussion #250). +_v6.5.4_ + +- Fixed some warnings when linting server code. +- New dirwatch type `DSDPlus Fast Lane` (discussion #244). +- Added new error catches on dirwatch (issue 254). +- Fixed search by inaccurate time (issue #258). +- Reverted sync.Map to regular map with sync.Mutex. + ## Version 6.4 - New `-cmd` command line options to allow advanced administrative tasks. diff --git a/COMPILING.md b/COMPILING.md index 9dcb9b4ce..7a83ba894 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -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.5.3.zip - rdio-scanner-darwin-arm64-v6.5.3.zip - rdio-scanner-freebsd-amd64-v6.5.3.zip - rdio-scanner-linux-386-v6.5.3.zip - rdio-scanner-linux-amd64-v6.5.3.zip - rdio-scanner-linux-arm64-v6.5.3.zip - rdio-scanner-linux-arm-v6.5.3.zip - rdio-scanner-windows-amd64-v6.5.3.zip + rdio-scanner-darwin-amd64-v6.5.4.zip + rdio-scanner-darwin-arm64-v6.5.4.zip + rdio-scanner-freebsd-amd64-v6.5.4.zip + rdio-scanner-linux-386-v6.5.4.zip + rdio-scanner-linux-amd64-v6.5.4.zip + rdio-scanner-linux-arm64-v6.5.4.zip + rdio-scanner-linux-arm-v6.5.4.zip + rdio-scanner-windows-amd64-v6.5.4.zip **Happy Rdio scanning !** \ No newline at end of file diff --git a/Makefile b/Makefile index 33ee1687b..55456b8df 100644 --- a/Makefile +++ b/Makefile @@ -16,8 +16,8 @@ ################################################################################ app := rdio-scanner -date := 2022/08/10 -ver := 6.5.3 +date := 2022/09/09 +ver := 6.5.4 client := $(wildcard client/*.json client/*.ts) server := $(wildcard server/*.go) diff --git a/README.md b/README.md index e18bd5cd2..d324a0c86 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,10 @@ Here is a list of recorders known to work with [Rdio Scanner](https://github.com | -------------------------------------------------------------- | --- | -------- | | [Trunk Recorder](https://github.com/robotastic/trunk-recorder) | X | X | | [RTLSDR-Airband](https://github.com/szpajder/RTLSDR-Airband) | | X | -| [sdrtrunk](https://github.com/DSheirer/sdrtrunk) | | X | +| [SDRTrunk](https://github.com/DSheirer/sdrtrunk) | | X | | [voxcall](https://github.com/aaknitt/voxcall) | X | | | [ProScan](https://www.proscan.org/) | | X | +| [DSDPlus Fast Lane](https://https://www.dsdplus.com/) | | X | # Quick start diff --git a/client/package-lock.json b/client/package-lock.json index 2c5be4796..f1e3c7c3f 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,12 +1,12 @@ { "name": "rdio-scanner", - "version": "6.5.3", + "version": "6.5.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "rdio-scanner", - "version": "6.5.2", + "version": "6.5.4", "license": "LICENSE", "dependencies": { "@angular/animations": "^13.3.10", diff --git a/client/package.json b/client/package.json index 07c2fae1f..39c7f79f0 100644 --- a/client/package.json +++ b/client/package.json @@ -38,5 +38,5 @@ "build": "ng build --base-href ./ --configuration production", "start": "ng serve" }, - "version": "6.5.3" + "version": "6.5.4" } diff --git a/client/src/app/components/rdio-scanner/admin/admin.service.ts b/client/src/app/components/rdio-scanner/admin/admin.service.ts index d7a1e7b1b..a6311953c 100644 --- a/client/src/app/components/rdio-scanner/admin/admin.service.ts +++ b/client/src/app/components/rdio-scanner/admin/admin.service.ts @@ -637,6 +637,10 @@ export class RdioScannerAdminService implements OnDestroy { return null; } + if (control.value.startsWith('\\')) { + return { network: true } + } + const dirWatch: DirWatch[] = control.parent?.parent?.getRawValue() || []; const count = dirWatch.reduce((c, a) => c += a.directory === control.value ? 1 : 0, 0); @@ -653,7 +657,7 @@ export class RdioScannerAdminService implements OnDestroy { const type = dirwatch.type; - return ['sdr-trunk'].includes(type) || control.value !== null || /#SYS/.test(mask) ? null : { required: true }; + return ['dsdplus', 'sdr-trunk'].includes(type) || control.value !== null || /#SYS/.test(mask) ? null : { required: true }; }; } @@ -665,7 +669,7 @@ export class RdioScannerAdminService implements OnDestroy { const type = dirwatch.type; - return ['trunk-recorder', 'sdr-trunk'].includes(type) || control.value !== null || /#TG/.test(mask) ? null : { required: true }; + return ['dsdplus', 'trunk-recorder', 'sdr-trunk'].includes(type) || control.value !== null || /#TG/.test(mask) ? null : { required: true }; }; } diff --git a/client/src/app/components/rdio-scanner/admin/config/dir-watch/dir-watch.component.html b/client/src/app/components/rdio-scanner/admin/config/dir-watch/dir-watch.component.html index 6f6459a01..3e5c3d12e 100644 --- a/client/src/app/components/rdio-scanner/admin/config/dir-watch/dir-watch.component.html +++ b/client/src/app/components/rdio-scanner/admin/config/dir-watch/dir-watch.component.html @@ -33,6 +33,28 @@ +
+

+ Type
+ + Dirwatch type defines how the metadata are obtained. +

+ +

+ + + Default + DSDPlus Fast Lane + Trunk Recorder + SDR Trunk + + +

Directory
@@ -49,9 +71,12 @@ Directory is already defined + + Incompatible network folder +

-
+

Extension
The audio call extension to monitor without the period. Ex.: "mp3", @@ -64,29 +89,14 @@

-
-

- Type
- When SDR Trunk, metadata are obtained from the MP3 tags. Note that - the label of the SDR Trunk system must match the label of the Rdio Scanner system label. When - trunk-recorder, metadata are obtained from the JSON file. -

- - - Default - Trunk Recorder - SDR Trunk (aliases tab only) - - -
-
+

System
System to where the audio files should go.

- Metatag #SYS + {{ system.value.label }} @@ -96,14 +106,14 @@
-
+

Talkgroup
Talkgroup to where the audio files should go.

- Metatag #TG + {{ talkgroup.value.label }} @@ -114,7 +124,7 @@
-
+

Mask
Some metadata can be extracted from the file name of the audio file using @@ -156,7 +166,7 @@

-
+

Frequency
Fake frequency in hertz displayed on the main screen. @@ -168,7 +178,7 @@

-
+

Delay
Depending on the recorder, audio files can be ingested too soon after the diff --git a/docs/docker/README.md b/docs/docker/README.md index e856e20d0..91243b4fc 100644 --- a/docs/docker/README.md +++ b/docs/docker/README.md @@ -53,12 +53,12 @@ $ docker run --detach --env TZ=America/Toronto --name rdio-scanner --publish 300 520cdbf51fca11d8bacea12d81245f1cb4d984f80d2be2e3039727b59533a6a9 $ docker logs rdio-scanner -Rdio Scanner v6.5.3 +Rdio Scanner v6.5.4 ---------------------------------- -2022/08/10 08:57:23 server started -2022/08/10 08:57:23 base folder is /app/data -2022/08/10 08:57:23 main interface at http://6d87e8dc37a0:3000 -2022/08/10 08:57:23 admin interface at http://6d87e8dc37a0:3000/admin +2022/09/09 08:57:23 server started +2022/09/09 08:57:23 base folder is /app/data +2022/09/09 08:57:23 main interface at http://6d87e8dc37a0:3000 +2022/09/09 08:57:23 admin interface at http://6d87e8dc37a0:3000/admin ``` You can also use docker-compose to start the server with this simple `docker-compose.yml`: diff --git a/docs/platforms/darwin.md b/docs/platforms/darwin.md index d8ce24b6c..5aab629a5 100644 --- a/docs/platforms/darwin.md +++ b/docs/platforms/darwin.md @@ -18,9 +18,10 @@ Here is a list of recorders known to work with [Rdio Scanner](https://github.com | -------------------------------------------------------------- | --- | -------- | | [Trunk Recorder](https://github.com/robotastic/trunk-recorder) | X | X | | [RTLSDR-Airband](https://github.com/szpajder/RTLSDR-Airband) | | X | -| [sdrtrunk](https://github.com/DSheirer/sdrtrunk) | | X | +| [SDRTrunk](https://github.com/DSheirer/sdrtrunk) | | X | | [voxcall](https://github.com/aaknitt/voxcall) | X | | | [ProScan](https://www.proscan.org/) | | X | +| [DSDPlus Fast Lane](https://https://www.dsdplus.com/) | | X | # Improve your experience on the go @@ -42,8 +43,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.5.3.zip - Archive: /Users/rdio/Downloads/rdio-scanner-darwin-arm64-v6.5.3.zip + > ~/Downloads/rdio-scanner-darwin-arm64-v6.5.4.zip + Archive: /Users/rdio/Downloads/rdio-scanner-darwin-arm64-v6.5.4.zip inflating: rdio-scanner inflating: rdio-scanner.pdf @@ -51,11 +52,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd rdio@macos rdio-scanner % ./rdio-scanner - Rdio Scanner v6.5.3 + Rdio Scanner v6.5.4 ---------------------------------- - 2022/08/10 08:38:06 server started - 2022/08/10 08:38:06 main interface at http://macos.local:3000 - 2022/08/10 08:38:06 admin interface at http://macos.local:3000/admin + 2022/09/09 08:38:06 server started + 2022/09/09 08:38:06 main interface at http://macos.local:3000 + 2022/09/09 08:38:06 admin interface at http://macos.local:3000/admin 4. Access the administrative dashboard to finalize the configuration. @@ -77,11 +78,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.5.3 + Rdio Scanner v6.5.4 ---------------------------------- - 2022/08/10 08:48:03 server started - 2022/08/10 08:48:03 main interface at http://macos.local - 2022/08/10 08:48:03 admin interface at http://macos.local/admin + 2022/09/09 08:48:03 server started + 2022/09/09 08:48:03 main interface at http://macos.local + 2022/09/09 08:48:03 admin interface at http://macos.local/admin ## Listening on a SSL port @@ -95,12 +96,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.5.3 + Rdio Scanner v6.5.4 ---------------------------------- - 2022/08/10 08:50:58 server started - 2022/08/10 08:50:58 main interface at http://macos.local - 2022/08/10 08:50:58 main interface at https://macos.local - 2022/08/10 08:50:58 admin interface at https://macos.local/admin + 2022/09/09 08:50:58 server started + 2022/09/09 08:50:58 main interface at http://macos.local + 2022/09/09 08:50:58 main interface at https://macos.local + 2022/09/09 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 +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/08/10 08:52:24 rdio-scanner.ini file created + 2022/09/09 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 +133,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an rdio@macos rdio-scanner % ./rdio-scanner - Rdio Scanner v6.5.3 + Rdio Scanner v6.5.4 ---------------------------------- - 2022/08/10 08:54:08 server started - 2022/08/10 08:54:08 main interface at http://macos.local - 2022/08/10 08:54:08 main interface at https://macos.local - 2022/08/10 08:54:08 admin interface at https://macos.local/admin + 2022/09/09 08:54:08 server started + 2022/09/09 08:54:08 main interface at http://macos.local + 2022/09/09 08:54:08 main interface at https://macos.local + 2022/09/09 08:54:08 admin interface at https://macos.local/admin ## Install Rdio Scanner as a service diff --git a/docs/platforms/freebsd.md b/docs/platforms/freebsd.md index 73189cd93..b743a9fb3 100644 --- a/docs/platforms/freebsd.md +++ b/docs/platforms/freebsd.md @@ -18,9 +18,10 @@ Here is a list of recorders known to work with [Rdio Scanner](https://github.com | -------------------------------------------------------------- | --- | -------- | | [Trunk Recorder](https://github.com/robotastic/trunk-recorder) | X | X | | [RTLSDR-Airband](https://github.com/szpajder/RTLSDR-Airband) | | X | -| [sdrtrunk](https://github.com/DSheirer/sdrtrunk) | | X | +| [SDRTrunk](https://github.com/DSheirer/sdrtrunk) | | X | | [voxcall](https://github.com/aaknitt/voxcall) | X | | | [ProScan](https://www.proscan.org/) | | X | +| [DSDPlus Fast Lane](https://https://www.dsdplus.com/) | | X | # Improve your experience on the go @@ -42,8 +43,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.5.3.zip - Archive: ../rdio-scanner-freebsd-amd64-v6.5.3.zip + > ~/rdio-scanner-freebsd-amd64-v6.5.4.zip + Archive: ../rdio-scanner-freebsd-amd64-v6.5.4.zip extracting: rdio-scanner extracting: rdio-scanner.pdf @@ -51,11 +52,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd rdio@pc-freebsd:~/rdio-scanner $ ./rdio-scanner - Rdio Scanner v6.5.3 + Rdio Scanner v6.5.4 ---------------------------------- - 2022/08/10 08:16:36 server started - 2022/08/10 08:16:36 main interface at http://pc-freebsd:3000 - 2022/08/10 08:16:36 admin interface at http://pc-freebsd:3000/admin + 2022/09/09 08:16:36 server started + 2022/09/09 08:16:36 main interface at http://pc-freebsd:3000 + 2022/09/09 08:16:36 admin interface at http://pc-freebsd:3000/admin 4. Access the administrative dashboard to finalize the configuration. @@ -81,11 +82,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.5.3 + Rdio Scanner v6.5.4 ---------------------------------- - 2022/08/10 08:19:38 server started - 2022/08/10 08:19:38 main interface at http://pc-freebsd - 2022/08/10 08:19:38 admin interface at http://pc-freebsd/admin + 2022/09/09 08:19:38 server started + 2022/09/09 08:19:38 main interface at http://pc-freebsd + 2022/09/09 08:19:38 admin interface at http://pc-freebsd/admin ## Listening on a SSL port @@ -101,12 +102,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.5.3 + Rdio Scanner v6.5.4 ---------------------------------- - 2022/08/10 10:34:29 server started - 2022/08/10 10:34:29 main interface at http://pc-freebsd - 2022/08/10 10:34:29 main interface at https://pc-freebsd - 2022/08/10 10:34:29 admin interface at https://pc-freebsd/admin + 2022/09/09 10:34:29 server started + 2022/09/09 10:34:29 main interface at http://pc-freebsd + 2022/09/09 10:34:29 main interface at https://pc-freebsd + 2022/09/09 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 +127,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/08/10 10:37:00 rdio-scanner.ini file created + 2022/09/09 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 +143,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.5.3 + Rdio Scanner v6.5.4 ---------------------------------- - 2022/08/10 10:38:28 server started - 2022/08/10 10:38:29 main interface at http://pc-freebsd - 2022/08/10 10:38:29 main interface at https://pc-freebsd - 2022/08/10 10:38:29 admin interface at https://pc-freebsd/admin + 2022/09/09 10:38:28 server started + 2022/09/09 10:38:29 main interface at http://pc-freebsd + 2022/09/09 10:38:29 main interface at https://pc-freebsd + 2022/09/09 10:38:29 admin interface at https://pc-freebsd/admin ## Install Rdio Scanner as a service diff --git a/docs/platforms/linux.md b/docs/platforms/linux.md index e57998ffb..4f907900a 100644 --- a/docs/platforms/linux.md +++ b/docs/platforms/linux.md @@ -18,9 +18,10 @@ Here is a list of recorders known to work with [Rdio Scanner](https://github.com | -------------------------------------------------------------- | --- | -------- | | [Trunk Recorder](https://github.com/robotastic/trunk-recorder) | X | X | | [RTLSDR-Airband](https://github.com/szpajder/RTLSDR-Airband) | | X | -| [sdrtrunk](https://github.com/DSheirer/sdrtrunk) | | X | +| [SDRTrunk](https://github.com/DSheirer/sdrtrunk) | | X | | [voxcall](https://github.com/aaknitt/voxcall) | X | | | [ProScan](https://www.proscan.org/) | | X | +| [DSDPlus Fast Lane](https://https://www.dsdplus.com/) | | X | # Improve your experience on the go @@ -42,8 +43,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.5.3.zip - Archive: /home/rdio/Downloads/rdio-scanner-linux-amd64-v6.5.3.zip + > ~/Downloads/rdio-scanner-linux-amd64-v6.5.4.zip + Archive: /home/rdio/Downloads/rdio-scanner-linux-amd64-v6.5.4.zip inflating: rdio-scanner inflating: rdio-scanner.pdf @@ -51,11 +52,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd [rdio@pc-linux rdio-scanner]$ ./rdio-scanner - Rdio Scanner v6.5.3 + Rdio Scanner v6.5.4 ---------------------------------- - 2022/08/10 09:11:48 server started - 2022/08/10 09:11:48 main interface at http://pc-linux:3000 - 2022/08/10 09:11:48 admin interface at http://pc-linux:3000/admin + 2022/09/09 09:11:48 server started + 2022/09/09 09:11:48 main interface at http://pc-linux:3000 + 2022/09/09 09:11:48 admin interface at http://pc-linux:3000/admin 4. Access the administrative dashboard to finalize the configuration. @@ -78,11 +79,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.5.3 + Rdio Scanner v6.5.4 ---------------------------------- - 2022/08/10 09:14:00 server started - 2022/08/10 09:14:00 main interface at http://pc-linux - 2022/08/10 09:14:00 admin interface at http://pc-linux/admin + 2022/09/09 09:14:00 server started + 2022/09/09 09:14:00 main interface at http://pc-linux + 2022/09/09 09:14:00 admin interface at http://pc-linux/admin ## Listening on a SSL port @@ -97,12 +98,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.5.3 + Rdio Scanner v6.5.4 ---------------------------------- - 2022/08/10 09:16:47 server started - 2022/08/10 09:16:47 main interface at http://pc-linux - 2022/08/10 09:16:47 main interface at https://pc-linux - 2022/08/10 09:16:47 admin interface at https://pc-linux/admin + 2022/09/09 09:16:47 server started + 2022/09/09 09:16:47 main interface at http://pc-linux + 2022/09/09 09:16:47 main interface at https://pc-linux + 2022/09/09 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 +121,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/08/10 09:19:29 rdio-scanner.ini file created + 2022/09/09 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 +136,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.5.3 + Rdio Scanner v6.5.4 ---------------------------------- - 2022/08/10 09:20:40 server started - 2022/08/10 09:20:40 main interface at http://pc-linux - 2022/08/10 09:20:40 main interface at https://pc-linux - 2022/08/10 09:20:40 admin interface at https://pc-linux/admin + 2022/09/09 09:20:40 server started + 2022/09/09 09:20:40 main interface at http://pc-linux + 2022/09/09 09:20:40 main interface at https://pc-linux + 2022/09/09 09:20:40 admin interface at https://pc-linux/admin ## Install Rdio Scanner as a service diff --git a/docs/platforms/windows.md b/docs/platforms/windows.md index 2c8a5387a..5d7d38ac5 100644 --- a/docs/platforms/windows.md +++ b/docs/platforms/windows.md @@ -18,9 +18,10 @@ Here is a list of recorders known to work with [Rdio Scanner](https://github.com | -------------------------------------------------------------- | --- | -------- | | [Trunk Recorder](https://github.com/robotastic/trunk-recorder) | X | X | | [RTLSDR-Airband](https://github.com/szpajder/RTLSDR-Airband) | | X | -| [sdrtrunk](https://github.com/DSheirer/sdrtrunk) | | X | +| [SDRTrunk](https://github.com/DSheirer/sdrtrunk) | | X | | [voxcall](https://github.com/aaknitt/voxcall) | X | | | [ProScan](https://www.proscan.org/) | | X | +| [DSDPlus Fast Lane](https://https://www.dsdplus.com/) | | X | # Improve your experience on the go @@ -44,7 +45,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.5.3.zip + ..\downloads\rdio-scanner-windows-amd64-v6.5.4.zip x rdio-scanner.exe x rdio-scanner.pdf @@ -52,11 +53,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd C:\Users\rdio\rdio-scanner>rdio-scanner - Rdio Scanner v6.5.3 + Rdio Scanner v6.5.4 ---------------------------------- - 2022/08/10 13:48:48 server started - 2022/08/10 13:48:48 main interface at http://pc-windows:3000 - 2022/08/10 13:48:48 admin interface at http://pc-windows:3000/admin + 2022/09/09 13:48:48 server started + 2022/09/09 13:48:48 main interface at http://pc-windows:3000 + 2022/09/09 13:48:48 admin interface at http://pc-windows:3000/admin 4. Access the administrative dashboard to finalize the configuration. @@ -78,11 +79,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.5.3 + Rdio Scanner v6.5.4 ---------------------------------- - 2022/08/10 10:53:31 server started - 2022/08/10 10:53:31 main interface at http://pc-windows - 2022/08/10 10:53:31 admin interface at http://pc-windows/admin + 2022/09/09 10:53:31 server started + 2022/09/09 10:53:31 main interface at http://pc-windows + 2022/09/09 10:53:31 admin interface at http://pc-windows/admin ## Listening on a SSL port @@ -96,12 +97,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.5.3 + Rdio Scanner v6.5.4 ---------------------------------- - 2022/08/10 11:05:43 server started - 2022/08/10 11:05:43 main interface at http://pc-windows - 2022/08/10 11:05:43 main interface at https://pc-windows - 2022/08/10 11:05:43 admin interface at https://pc-windows/admin + 2022/09/09 11:05:43 server started + 2022/09/09 11:05:43 main interface at http://pc-windows + 2022/09/09 11:05:43 main interface at https://pc-windows + 2022/09/09 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 +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/08/10 11:08:28 rdio-scanner.ini file created + 2022/09/09 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 +134,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an C:\Users\rdio\rdio-scanner>rdio-scanner - Rdio Scanner v6.5.3 + Rdio Scanner v6.5.4 ---------------------------------- - 2022/08/10 11:11:28 server started - 2022/08/10 11:11:28 main interface at http://pc-windows - 2022/08/10 11:11:28 main interface at https://pc-windows - 2022/08/10 11:11:28 admin interface at https://pc-windows/admin + 2022/09/09 11:11:28 server started + 2022/09/09 11:11:28 main interface at http://pc-windows + 2022/09/09 11:11:28 main interface at https://pc-windows + 2022/09/09 11:11:28 admin interface at https://pc-windows/admin \pagebreak{} diff --git a/server/call.go b/server/call.go index dc8b28463..8e7f3e8a4 100644 --- a/server/call.go +++ b/server/call.go @@ -338,6 +338,8 @@ func (calls *Calls) Search(searchOptions *CallsSearchOptions, client *Client) (* if t, err = db.ParseDateTime(dateTime); err == nil { searchResults.DateStop = t + } else { + searchResults.DateStop = time.Now() } switch v := searchOptions.Sort.(type) { @@ -364,8 +366,8 @@ func (calls *Calls) Search(searchOptions *CallsSearchOptions, client *Client) (* stop = start.Add(time.Hour*24 - time.Millisecond) } else { - start = time.Date(v.Year(), v.Month(), v.Day(), v.Hour(), v.Minute(), 0, 0, time.UTC).Add(time.Hour*-24 - time.Duration(v.Hour())).Add(time.Minute * time.Duration(-v.Minute())) - stop = start.Add(time.Hour*24 - time.Millisecond - time.Duration(v.Hour())).Add(time.Minute * time.Duration(-v.Minute())) + start = time.Date(v.Year(), v.Month(), v.Day(), v.Hour(), v.Minute(), 0, 0, time.UTC).Add(time.Hour*-24 + time.Millisecond) + stop = time.Date(v.Year(), v.Month(), v.Day(), v.Hour(), v.Minute(), 0, 0, time.UTC) } where += fmt.Sprintf(" and (`dateTime` between '%v' and '%v')", start.Format(df), stop.Format(df)) diff --git a/server/client.go b/server/client.go index 7036bec2e..66eb73ac6 100644 --- a/server/client.go +++ b/server/client.go @@ -206,91 +206,90 @@ func (client *Client) SendListenersCount(count int) { } type Clients struct { - Map sync.Map - count int + Map map[*Client]bool + mutex sync.Mutex } func NewClients() *Clients { return &Clients{ - Map: sync.Map{}, - count: 0, + Map: map[*Client]bool{}, + mutex: sync.Mutex{}, } } func (clients *Clients) AccessCount(client *Client) int { + clients.mutex.Lock() + defer clients.mutex.Unlock() + count := 0 - clients.Map.Range(func(k any, _ any) bool { - switch c := k.(type) { - case *Client: - if c.Access == client.Access { - count++ - } + for c := range clients.Map { + if c.Access == client.Access { + count++ } - return true - }) + } return count } func (clients *Clients) Add(client *Client) { - clients.count = clients.count + 1 - clients.Map.Store(client, true) + clients.mutex.Lock() + defer clients.mutex.Unlock() + + clients.Map[client] = true } func (clients *Clients) Count() int { - return clients.count + clients.mutex.Lock() + defer clients.mutex.Unlock() + + return len(clients.Map) } func (clients *Clients) EmitCall(call *Call, restricted bool) { - clients.Map.Range(func(k any, _ any) bool { - switch c := k.(type) { - case *Client: - if (!restricted || c.Access.HasAccess(call)) && c.Livefeed.IsEnabled(call) { - c.Send <- &Message{Command: MessageCommandCall, Payload: call} - } - } + clients.mutex.Lock() + defer clients.mutex.Unlock() - return true - }) + for c := range clients.Map { + if (!restricted || c.Access.HasAccess(call)) && c.Livefeed.IsEnabled(call) { + c.Send <- &Message{Command: MessageCommandCall, Payload: call} + } + } } func (clients *Clients) EmitConfig(groups *Groups, options *Options, systems *Systems, tags *Tags, restricted bool) { - count := clients.Count() + clients.mutex.Lock() + defer clients.mutex.Unlock() - clients.Map.Range(func(k any, _ any) bool { - switch c := k.(type) { - case *Client: - if restricted { - c.Send <- &Message{Command: MessageCommandPin} - } else { - c.SendConfig(groups, options, systems, tags) - } + count := len(clients.Map) - if options.ShowListenersCount { - c.SendListenersCount(count) - } + for c := range clients.Map { + if restricted { + c.Send <- &Message{Command: MessageCommandPin} + } else { + c.SendConfig(groups, options, systems, tags) } - return true - }) + if options.ShowListenersCount { + c.SendListenersCount(count) + } + } } func (clients *Clients) EmitListenersCount() { - count := clients.Count() + clients.mutex.Lock() + defer clients.mutex.Unlock() - clients.Map.Range(func(k any, _ any) bool { - switch c := k.(type) { - case *Client: - c.SendListenersCount(count) - } + count := len(clients.Map) - return true - }) + for c := range clients.Map { + c.SendListenersCount(count) + } } func (clients *Clients) Remove(client *Client) { - if _, loaded := clients.Map.LoadAndDelete(client); loaded { - clients.count = clients.count - 1 - } + clients.mutex.Lock() + defer clients.mutex.Unlock() + + delete(clients.Map, client) } diff --git a/server/command.go b/server/command.go index 6762ffe32..47392895f 100644 --- a/server/command.go +++ b/server/command.go @@ -138,7 +138,7 @@ func (command *Command) Do(action string) { command.url = readVal() if err != nil || !regexp.MustCompile(`^https?://`).Match([]byte(command.url)) { - command.exitWithError(errors.New("Invalid URL")) + command.exitWithError(errors.New("invalid URL")) } } @@ -259,7 +259,7 @@ func (command *Command) configGet() { } } default: - command.exitWithError(errors.New("Invalid response")) + command.exitWithError(errors.New("invalid response")) } } else { command.exitWithError(err) @@ -314,10 +314,10 @@ func (command *Command) login() { } fmt.Println("Logged in.") default: - command.exitWithError(errors.New("No token in response")) + command.exitWithError(errors.New("no token in response")) } default: - command.exitWithError(errors.New("Invalid response")) + command.exitWithError(errors.New("invalid response")) } } else { command.exitWithError(err) diff --git a/server/controller.go b/server/controller.go index 6d30c0a2e..b883b7d66 100644 --- a/server/controller.go +++ b/server/controller.go @@ -23,7 +23,6 @@ import ( "os" "os/signal" "strconv" - "sync" "time" ) @@ -48,7 +47,6 @@ type Controller struct { Register chan *Client Unregister chan *Client Ingest chan *Call - ingestMutex sync.Mutex running bool } @@ -271,7 +269,7 @@ func (controller *Controller) IngestCall(call *Call) { } if system == nil || talkgroup == nil { - logCall(call, LogLevelInfo, "no matching system/talkgroup") + logCall(call, LogLevelWarn, "no matching system/talkgroup") return } @@ -516,7 +514,7 @@ func (controller *Controller) Start() error { } go func() { - c := make(chan os.Signal) + c := make(chan os.Signal, 8) signal.Notify(c, os.Interrupt) <-c controller.Terminate() @@ -536,7 +534,10 @@ func (controller *Controller) Start() error { if timer != nil { timer.Stop() } + timer = time.AfterFunc(time.Second, func() { + timer = nil + controller.LogClientsCount() if controller.Options.ShowListenersCount { diff --git a/server/dirwatch.go b/server/dirwatch.go index 8ff6be095..95c3f1cce 100644 --- a/server/dirwatch.go +++ b/server/dirwatch.go @@ -21,7 +21,6 @@ import ( "errors" "fmt" "io/fs" - "io/ioutil" "math" "mime" "os" @@ -38,9 +37,10 @@ import ( ) const ( - DirwatchKindDefault = "default" - DirwatchKindSdrTrunk = "sdr-trunk" - DirwatchKindTrunkRecorder = "trunk-recorder" + DirwatchTypeDefault = "default" + DirwatchTypeDSDPlus = "dsdplus" + DirwatchTypeSdrTrunk = "sdr-trunk" + DirwatchTypeTrunkRecorder = "trunk-recorder" ) type Dirwatch struct { @@ -59,9 +59,19 @@ type Dirwatch struct { UsePolling bool `json:"usePolling"` controller *Controller dirs map[string]bool + mutex sync.Mutex + timers map[string]*time.Timer watcher *fsnotify.Watcher } +func NewDirwatch() *Dirwatch { + return &Dirwatch{ + dirs: map[string]bool{}, + mutex: sync.Mutex{}, + timers: map[string]*time.Timer{}, + } +} + func (dirwatch *Dirwatch) FromMap(m map[string]any) *Dirwatch { switch v := m["_id"].(type) { case float64: @@ -135,16 +145,18 @@ func (dirwatch *Dirwatch) Ingest(p string) { var err error switch dirwatch.Kind { - case DirwatchKindTrunkRecorder: + case DirwatchTypeDSDPlus: + err = dirwatch.ingestDSDPlus(p) + case DirwatchTypeTrunkRecorder: err = dirwatch.ingestTrunkRecorder(p) - case DirwatchKindSdrTrunk: + case DirwatchTypeSdrTrunk: err = dirwatch.ingestSdrTrunk(p) default: err = dirwatch.ingestDefault(p) } if err != nil { - dirwatch.controller.Logs.LogEvent(LogLevelError, fmt.Sprintf("dirwatch.ingest: %s, %s", err.Error(), p)) + dirwatch.controller.Logs.LogEvent(LogLevelWarn, fmt.Sprintf("dirwatch.ingest: %s, %s", err.Error(), p)) } } @@ -156,7 +168,11 @@ func (dirwatch *Dirwatch) ingestDefault(p string) error { switch v := dirwatch.Extension.(type) { case string: - ext = fmt.Sprintf(".%s", v) + if len(v) > 0 { + ext = fmt.Sprintf(".%s", v) + } else { + ext = ".wav" + } default: ext = ".wav" } @@ -202,7 +218,7 @@ func (dirwatch *Dirwatch) ingestDefault(p string) error { return err } -func (dirwatch *Dirwatch) ingestSdrTrunk(p string) error { +func (dirwatch *Dirwatch) ingestDSDPlus(p string) error { var ( err error ext string @@ -229,6 +245,48 @@ func (dirwatch *Dirwatch) ingestSdrTrunk(p string) error { call.AudioType = mime.TypeByExtension(path.Ext(p)) call.Frequency = dirwatch.Frequency + switch v := dirwatch.SystemId.(type) { + case uint: + call.System = v + } + + if call.Audio, err = os.ReadFile(p); err != nil { + return err + } + + if err = ParseDSDPlusMeta(call, p); err != nil { + return err + } + + if ok, err := call.IsValid(); ok { + dirwatch.controller.Ingest <- call + + if dirwatch.DeleteAfter { + if err = os.Remove(p); err != nil { + return err + } + } + + } else { + return err + } + + return nil +} + +func (dirwatch *Dirwatch) ingestSdrTrunk(p string) error { + var err error + + if !strings.EqualFold(path.Ext(p), ".mp3") { + return nil + } + + call := NewCall() + + call.AudioName = filepath.Base(p) + call.AudioType = mime.TypeByExtension(path.Ext(p)) + call.Frequency = dirwatch.Frequency + if call.Audio, err = os.ReadFile(p); err != nil { return err } @@ -290,11 +348,11 @@ func (dirwatch *Dirwatch) ingestTrunkRecorder(p string) error { call.System = v } - if call.Audio, err = ioutil.ReadFile(audioName); err != nil { + if call.Audio, err = os.ReadFile(audioName); err != nil { return nil } - if b, err = ioutil.ReadFile(p); err != nil { + if b, err = os.ReadFile(p); err != nil { return err } @@ -550,15 +608,16 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error { } go func() { - var timers = sync.Map{} - logError := func(err error) { controller.Logs.LogEvent(LogLevelError, fmt.Sprintf("dirwatch.watcher: %v", err.Error())) } newTimer := func(eventName string) *time.Timer { return time.AfterFunc(delay, func() { - timers.Delete(eventName) + dirwatch.mutex.Lock() + defer dirwatch.mutex.Unlock() + + delete(dirwatch.timers, eventName) if _, err := os.Stat(eventName); err == nil { dirwatch.Ingest(eventName) @@ -567,74 +626,71 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error { } defer func() { - timers.Range(func(k any, t any) bool { - switch v := t.(type) { - case *time.Timer: - v.Stop() - } - - timers.Delete(k) - - return true - }) - switch v := recover().(type) { case error: controller.Logs.LogEvent(LogLevelError, v.Error()) } + + dirwatch.mutex.Lock() + defer dirwatch.mutex.Unlock() + + for e, t := range dirwatch.timers { + t.Stop() + delete(dirwatch.timers, e) + } + + if dirwatch.watcher != nil { + dirwatch.Start(controller) + } }() for { if dirwatch.watcher == nil { - break + return } select { case event, ok := <-dirwatch.watcher.Events: - if ok { - switch event.Op { - case fsnotify.Create: - if dirwatch.isDir(event.Name) { - if err := dirwatch.walkDir(event.Name); err != nil { - logError(err) - } + if !ok { + return + } - } else { - if f, ok := timers.LoadAndDelete(event.Name); ok { - switch v := f.(type) { - case *time.Timer: - v.Stop() - } - } - timers.Store(event.Name, newTimer(event.Name)) + switch event.Op { + case fsnotify.Create: + if dirwatch.isDir(event.Name) { + if err := dirwatch.walkDir(event.Name); err != nil { + logError(err) } - case fsnotify.Remove: - if dirwatch.dirs[event.Name] { - if err := dirwatch.watcher.Remove(event.Name); err == nil { - delete(dirwatch.dirs, event.Name) - } else { - logError(err) - } + } else { + if dirwatch.timers[event.Name] != nil { + dirwatch.timers[event.Name].Stop() } + dirwatch.timers[event.Name] = newTimer(event.Name) + } - case fsnotify.Write: - if f, ok := timers.LoadAndDelete(event.Name); ok { - switch v := f.(type) { - case *time.Timer: - v.Stop() - } + case fsnotify.Remove: + if dirwatch.dirs[event.Name] { + if err := dirwatch.watcher.Remove(event.Name); err == nil { + delete(dirwatch.dirs, event.Name) + } else { + logError(err) } - timers.Store(event.Name, newTimer(event.Name)) } + + case fsnotify.Write: + if dirwatch.timers[event.Name] != nil { + dirwatch.timers[event.Name].Stop() + } + dirwatch.timers[event.Name] = newTimer(event.Name) } case err, ok := <-dirwatch.watcher.Errors: if ok { logError(err) - - return } + + return } } }() @@ -671,8 +727,9 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error { func (dirwatch *Dirwatch) Stop() { if dirwatch.watcher != nil { - dirwatch.watcher.Close() + w := dirwatch.watcher dirwatch.watcher = nil + w.Close() } } @@ -699,8 +756,7 @@ func (dirwatches *Dirwatches) FromMap(f []any) *Dirwatches { for _, f := range f { switch v := f.(type) { case map[string]any: - dirwatch := &Dirwatch{} - dirwatch.FromMap(v) + dirwatch := NewDirwatch().FromMap(v) dirwatches.List = append(dirwatches.List, dirwatch) } } @@ -739,7 +795,7 @@ func (dirwatches *Dirwatches) Read(db *Database) error { } for rows.Next() { - dirwatch := &Dirwatch{} + dirwatch := NewDirwatch() if err = rows.Scan(&id, &delay, &dirwatch.DeleteAfter, &dirwatch.Directory, &dirwatch.Disabled, &extension, &frequency, &mask, &order, &systemId, &talkgroupId, &kind, &dirwatch.UsePolling); err != nil { break diff --git a/server/ffmpeg.go b/server/ffmpeg.go index 17342fd97..ce502dc90 100644 --- a/server/ffmpeg.go +++ b/server/ffmpeg.go @@ -75,7 +75,7 @@ func (ffmpeg *FFMpeg) Convert(call *Call, systems *Systems, tags *Tags, mode uin if !ffmpeg.warned { ffmpeg.warned = true - return errors.New("ffmpeg is not available, no audio conversion will be performed.") + return errors.New("ffmpeg is not available, no audio conversion will be performed") } return nil } diff --git a/server/main.go b/server/main.go index b565a85d3..6b725b9c9 100644 --- a/server/main.go +++ b/server/main.go @@ -18,7 +18,7 @@ package main import ( "crypto/tls" "fmt" - "io/ioutil" + "io" "log" "mime" "net/http" @@ -198,7 +198,7 @@ func main() { TLSConfig: tlsConfig, ReadTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second, - ErrorLog: log.New(ioutil.Discard, "", 0), + ErrorLog: log.New(io.Discard, "", 0), } s.SetKeepAlivesEnabled(true) diff --git a/server/parsers.go b/server/parsers.go index 129894d65..0c169d54a 100644 --- a/server/parsers.go +++ b/server/parsers.go @@ -21,13 +21,70 @@ import ( "mime" "mime/multipart" "path" + "path/filepath" "regexp" "strconv" + "strings" "time" "github.com/dhowden/tag" ) +func ParseDSDPlusMeta(call *Call, fp string) error { + dir := filepath.Dir(fp) + base := strings.TrimSuffix(filepath.Base(fp), filepath.Ext(fp)) + + if d := regexp.MustCompile(`([0-9]+)$`).FindStringSubmatch(dir); len(d) == 2 && len(d[1]) == 8 { + if t := regexp.MustCompile(`^([0-9]+)`).FindStringSubmatch(base); len(t) == 2 && len(t[1]) == 6 { + if dy, err := strconv.Atoi(d[1][0:4]); err == nil { + if dm, err := strconv.Atoi(d[1][4:6]); err == nil { + if dd, err := strconv.Atoi(d[1][6:8]); err == nil { + if th, err := strconv.Atoi(t[1][0:2]); err == nil { + if tm, err := strconv.Atoi(t[1][2:4]); err == nil { + if ts, err := strconv.Atoi(t[1][4:6]); err == nil { + call.DateTime = time.Date(dy, time.Month(dm), dd, th, tm, ts, 0, time.Now().Location()).UTC() + } + } + } + } + } + } + } + } + + if s := regexp.MustCompile(`^[0-9]+_[0-9]+_[^_]+_([0-9]+)-([0-9]+)_`).FindStringSubmatch(base); len(s) == 3 { + if sys, err := strconv.Atoi(s[1]); err == nil && sys > 0 { + call.System = uint(sys) + } + } + + tu := regexp.MustCompile(`([0-9]+)_([0-9]+)$|([0-9]+)_([0-9]+)\[([^\]]*)\]$`).FindStringSubmatch(base) + + if len(tu[3]) > 0 { + tu[1] = tu[3] + } + + if len(tu[4]) > 0 { + tu[2] = tu[4] + } + + if tg, err := strconv.Atoi(tu[1]); err == nil && tg > 0 { + call.Talkgroup = uint(tg) + if src, err := strconv.Atoi(tu[2]); err == nil && src > 0 { + call.Source = uint(src) + if len(tu[5]) > 0 && strings.TrimSpace(tu[5]) != strings.TrimSpace(tu[2]) { + if b := regexp.MustCompile(`^([\.\-\ ,_]+)$`).MatchString(tu[5]); !b { + units := NewUnits() + units.Add(uint(src), tu[5]) + call.units = units + } + } + } + } + + return nil +} + func ParseSdrTrunkMeta(call *Call, controller *Controller) error { var ( s []string @@ -43,7 +100,7 @@ func ParseSdrTrunkMeta(call *Call, controller *Controller) error { s = regexp.MustCompile(`^([0-9]+) ?(.*)$`).FindStringSubmatch(m.Artist()) if len(s) >= 2 { - if i, err = strconv.Atoi(s[1]); err != nil { + if i, err = strconv.Atoi(s[1][0:1]); err != nil { return err } if i > 0 { diff --git a/server/version.go b/server/version.go index 41df5ed21..505ad406c 100644 --- a/server/version.go +++ b/server/version.go @@ -15,4 +15,4 @@ package main -const Version = "6.5.3" +const Version = "6.5.4"