mirror of
https://github.com/chuot/rdio-scanner.git
synced 2026-08-16 18:03:01 -06:00
Version 6.5.4
This commit is contained in:
parent
bf75f92f47
commit
d03f0b1fef
|
|
@ -31,6 +31,14 @@ _v6.5.3_
|
||||||
- Add filename to dirwatch error messages (issue #248).
|
- 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).
|
- 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
|
## Version 6.4
|
||||||
|
|
||||||
- New `-cmd` command line options to allow advanced administrative tasks.
|
- New `-cmd` command line options to allow advanced administrative tasks.
|
||||||
|
|
|
||||||
16
COMPILING.md
16
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.
|
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-amd64-v6.5.4.zip
|
||||||
rdio-scanner-darwin-arm64-v6.5.3.zip
|
rdio-scanner-darwin-arm64-v6.5.4.zip
|
||||||
rdio-scanner-freebsd-amd64-v6.5.3.zip
|
rdio-scanner-freebsd-amd64-v6.5.4.zip
|
||||||
rdio-scanner-linux-386-v6.5.3.zip
|
rdio-scanner-linux-386-v6.5.4.zip
|
||||||
rdio-scanner-linux-amd64-v6.5.3.zip
|
rdio-scanner-linux-amd64-v6.5.4.zip
|
||||||
rdio-scanner-linux-arm64-v6.5.3.zip
|
rdio-scanner-linux-arm64-v6.5.4.zip
|
||||||
rdio-scanner-linux-arm-v6.5.3.zip
|
rdio-scanner-linux-arm-v6.5.4.zip
|
||||||
rdio-scanner-windows-amd64-v6.5.3.zip
|
rdio-scanner-windows-amd64-v6.5.4.zip
|
||||||
|
|
||||||
**Happy Rdio scanning !**
|
**Happy Rdio scanning !**
|
||||||
4
Makefile
4
Makefile
|
|
@ -16,8 +16,8 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
app := rdio-scanner
|
app := rdio-scanner
|
||||||
date := 2022/08/10
|
date := 2022/09/09
|
||||||
ver := 6.5.3
|
ver := 6.5.4
|
||||||
|
|
||||||
client := $(wildcard client/*.json client/*.ts)
|
client := $(wildcard client/*.json client/*.ts)
|
||||||
server := $(wildcard server/*.go)
|
server := $(wildcard server/*.go)
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
| [Trunk Recorder](https://github.com/robotastic/trunk-recorder) | X | X |
|
||||||
| [RTLSDR-Airband](https://github.com/szpajder/RTLSDR-Airband) | | 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 | |
|
| [voxcall](https://github.com/aaknitt/voxcall) | X | |
|
||||||
| [ProScan](https://www.proscan.org/) | | X |
|
| [ProScan](https://www.proscan.org/) | | X |
|
||||||
|
| [DSDPlus Fast Lane](https://https://www.dsdplus.com/) | | X |
|
||||||
|
|
||||||
# Quick start
|
# Quick start
|
||||||
|
|
||||||
|
|
|
||||||
4
client/package-lock.json
generated
4
client/package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "rdio-scanner",
|
"name": "rdio-scanner",
|
||||||
"version": "6.5.3",
|
"version": "6.5.4",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "rdio-scanner",
|
"name": "rdio-scanner",
|
||||||
"version": "6.5.2",
|
"version": "6.5.4",
|
||||||
"license": "LICENSE",
|
"license": "LICENSE",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^13.3.10",
|
"@angular/animations": "^13.3.10",
|
||||||
|
|
|
||||||
|
|
@ -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.5.3"
|
"version": "6.5.4"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -637,6 +637,10 @@ export class RdioScannerAdminService implements OnDestroy {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (control.value.startsWith('\\')) {
|
||||||
|
return { network: true }
|
||||||
|
}
|
||||||
|
|
||||||
const dirWatch: DirWatch[] = control.parent?.parent?.getRawValue() || [];
|
const dirWatch: DirWatch[] = control.parent?.parent?.getRawValue() || [];
|
||||||
|
|
||||||
const count = dirWatch.reduce((c, a) => c += a.directory === control.value ? 1 : 0, 0);
|
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;
|
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;
|
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 };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,28 @@
|
||||||
<mat-slide-toggle color="primary" formControlName="deleteAfter"></mat-slide-toggle>
|
<mat-slide-toggle color="primary" formControlName="deleteAfter"></mat-slide-toggle>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<p>
|
||||||
|
<span class="mat-body">Type</span><br>
|
||||||
|
<span class="mat-caption">
|
||||||
|
Dirwatch type defines how the metadata are obtained.
|
||||||
|
<ul>
|
||||||
|
<li><b>Default</b> - Extract the metadata from a custom mask.</li>
|
||||||
|
<li><b>DSDPlus Fast Lane</b> - Extract the metadata from the file path.</li>
|
||||||
|
<li><b>Trunk Recorder</b> - Extract the metadata from the json file.</li>
|
||||||
|
<li><b>SDR Trunk</b> - Extract the metadata from the MP3 tags defined on the SDR Trunk's aliases tab.</li>
|
||||||
|
</ul>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<mat-form-field floatLabel="never">
|
||||||
|
<mat-select formControlName="type" placeholder="Type">
|
||||||
|
<mat-option value="default">Default</mat-option>
|
||||||
|
<mat-option value="dsdplus">DSDPlus Fast Lane</mat-option>
|
||||||
|
<mat-option value="trunk-recorder">Trunk Recorder</mat-option>
|
||||||
|
<mat-option value="sdr-trunk">SDR Trunk</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<p>
|
<p>
|
||||||
<span class="mat-body">Directory</span><br>
|
<span class="mat-body">Directory</span><br>
|
||||||
|
|
@ -49,9 +71,12 @@
|
||||||
<mat-error *ngIf="dirWatch.get('directory')?.hasError('duplicate')">
|
<mat-error *ngIf="dirWatch.get('directory')?.hasError('duplicate')">
|
||||||
Directory is already defined
|
Directory is already defined
|
||||||
</mat-error>
|
</mat-error>
|
||||||
|
<mat-error *ngIf="dirWatch.get('directory')?.hasError('network')">
|
||||||
|
Incompatible network folder
|
||||||
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row" *ngIf="['default','dsdplus','trunk-recorder'].includes(dirWatch.get('type')?.value)">
|
||||||
<p>
|
<p>
|
||||||
<span class="mat-body">Extension</span><br>
|
<span class="mat-body">Extension</span><br>
|
||||||
<span class="mat-caption">The audio call extension to monitor without the period. Ex.: "mp3",
|
<span class="mat-caption">The audio call extension to monitor without the period. Ex.: "mp3",
|
||||||
|
|
@ -64,29 +89,14 @@
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row" *ngIf="['default','dsdplus'].includes(dirWatch.get('type')?.value)">
|
||||||
<p>
|
|
||||||
<span class="mat-body">Type</span><br>
|
|
||||||
<span class="mat-caption">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.</span>
|
|
||||||
</p>
|
|
||||||
<mat-form-field floatLabel="never">
|
|
||||||
<mat-select formControlName="type" placeholder="Type">
|
|
||||||
<mat-option [value]="null">Default</mat-option>
|
|
||||||
<mat-option value="trunk-recorder">Trunk Recorder</mat-option>
|
|
||||||
<mat-option value="sdr-trunk">SDR Trunk (aliases tab only)</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<p>
|
<p>
|
||||||
<span class="mat-body">System</span><br>
|
<span class="mat-body">System</span><br>
|
||||||
<span class="mat-caption">System to where the audio files should go.</span>
|
<span class="mat-caption">System to where the audio files should go.</span>
|
||||||
</p>
|
</p>
|
||||||
<mat-form-field floatLabel="never">
|
<mat-form-field floatLabel="never">
|
||||||
<mat-select #sysId formControlName="systemId" placeholder="System">
|
<mat-select #sysId formControlName="systemId" placeholder="System">
|
||||||
<mat-option [value]="null">Metatag #SYS</mat-option>
|
<mat-option [value]="null"></mat-option>
|
||||||
<mat-option *ngFor="let system of systems" [value]="system.value.id">
|
<mat-option *ngFor="let system of systems" [value]="system.value.id">
|
||||||
{{ system.value.label }}
|
{{ system.value.label }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
|
|
@ -96,14 +106,14 @@
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row" *ngIf="['default'].includes(dirWatch.get('type')?.value)">
|
||||||
<p>
|
<p>
|
||||||
<span class="mat-body">Talkgroup</span><br>
|
<span class="mat-body">Talkgroup</span><br>
|
||||||
<span class="mat-caption">Talkgroup to where the audio files should go.</span>
|
<span class="mat-caption">Talkgroup to where the audio files should go.</span>
|
||||||
</p>
|
</p>
|
||||||
<mat-form-field floatLabel="never">
|
<mat-form-field floatLabel="never">
|
||||||
<mat-select formControlName="talkgroupId" placeholder="Talkgroup">
|
<mat-select formControlName="talkgroupId" placeholder="Talkgroup">
|
||||||
<mat-option [value]="null">Metatag #TG</mat-option>
|
<mat-option [value]="null"></mat-option>
|
||||||
<mat-option *ngFor="let talkgroup of talkgroups[dirWatch.value.systemId] || []"
|
<mat-option *ngFor="let talkgroup of talkgroups[dirWatch.value.systemId] || []"
|
||||||
[value]="talkgroup.value.id">
|
[value]="talkgroup.value.id">
|
||||||
{{ talkgroup.value.label }}
|
{{ talkgroup.value.label }}
|
||||||
|
|
@ -114,7 +124,7 @@
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row" *ngIf="['default'].includes(dirWatch.get('type')?.value)">
|
||||||
<p>
|
<p>
|
||||||
<span class="mat-body">Mask</span><br>
|
<span class="mat-body">Mask</span><br>
|
||||||
<span class="mat-caption">Some metadata can be extracted from the file name of the audio file using
|
<span class="mat-caption">Some metadata can be extracted from the file name of the audio file using
|
||||||
|
|
@ -156,7 +166,7 @@
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row" *ngIf="['default'].includes(dirWatch.get('type')?.value)">
|
||||||
<p>
|
<p>
|
||||||
<span class="mat-body">Frequency</span><br>
|
<span class="mat-body">Frequency</span><br>
|
||||||
<span class="mat-caption">Fake frequency in hertz displayed on the main screen.</span>
|
<span class="mat-caption">Fake frequency in hertz displayed on the main screen.</span>
|
||||||
|
|
@ -168,7 +178,7 @@
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row" *ngIf="['default'].includes(dirWatch.get('type')?.value)">
|
||||||
<p>
|
<p>
|
||||||
<span class="mat-body">Delay</span><br>
|
<span class="mat-body">Delay</span><br>
|
||||||
<span class="mat-caption">Depending on the recorder, audio files can be ingested too soon after the
|
<span class="mat-caption">Depending on the recorder, audio files can be ingested too soon after the
|
||||||
|
|
|
||||||
|
|
@ -53,12 +53,12 @@ $ docker run --detach --env TZ=America/Toronto --name rdio-scanner --publish 300
|
||||||
520cdbf51fca11d8bacea12d81245f1cb4d984f80d2be2e3039727b59533a6a9
|
520cdbf51fca11d8bacea12d81245f1cb4d984f80d2be2e3039727b59533a6a9
|
||||||
|
|
||||||
$ docker logs rdio-scanner
|
$ docker logs rdio-scanner
|
||||||
Rdio Scanner v6.5.3
|
Rdio Scanner v6.5.4
|
||||||
----------------------------------
|
----------------------------------
|
||||||
2022/08/10 08:57:23 server started
|
2022/09/09 08:57:23 server started
|
||||||
2022/08/10 08:57:23 base folder is /app/data
|
2022/09/09 08:57:23 base folder is /app/data
|
||||||
2022/08/10 08:57:23 main interface at http://6d87e8dc37a0:3000
|
2022/09/09 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 admin interface at http://6d87e8dc37a0:3000/admin
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also use docker-compose to start the server with this simple `docker-compose.yml`:
|
You can also use docker-compose to start the server with this simple `docker-compose.yml`:
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
| [Trunk Recorder](https://github.com/robotastic/trunk-recorder) | X | X |
|
||||||
| [RTLSDR-Airband](https://github.com/szpajder/RTLSDR-Airband) | | 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 | |
|
| [voxcall](https://github.com/aaknitt/voxcall) | X | |
|
||||||
| [ProScan](https://www.proscan.org/) | | X |
|
| [ProScan](https://www.proscan.org/) | | X |
|
||||||
|
| [DSDPlus Fast Lane](https://https://www.dsdplus.com/) | | X |
|
||||||
|
|
||||||
# Improve your experience on the go
|
# 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 ~ % 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.5.3.zip
|
> ~/Downloads/rdio-scanner-darwin-arm64-v6.5.4.zip
|
||||||
Archive: /Users/rdio/Downloads/rdio-scanner-darwin-arm64-v6.5.3.zip
|
Archive: /Users/rdio/Downloads/rdio-scanner-darwin-arm64-v6.5.4.zip
|
||||||
inflating: rdio-scanner
|
inflating: rdio-scanner
|
||||||
inflating: rdio-scanner.pdf
|
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@macos rdio-scanner % ./rdio-scanner
|
||||||
|
|
||||||
Rdio Scanner v6.5.3
|
Rdio Scanner v6.5.4
|
||||||
----------------------------------
|
----------------------------------
|
||||||
2022/08/10 08:38:06 server started
|
2022/09/09 08:38:06 server started
|
||||||
2022/08/10 08:38:06 main interface at http://macos.local:3000
|
2022/09/09 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 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 +78,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.5.3
|
Rdio Scanner v6.5.4
|
||||||
----------------------------------
|
----------------------------------
|
||||||
2022/08/10 08:48:03 server started
|
2022/09/09 08:48:03 server started
|
||||||
2022/08/10 08:48:03 main interface at http://macos.local
|
2022/09/09 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 admin interface at http://macos.local/admin
|
||||||
|
|
||||||
## Listening on a SSL port
|
## 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_key_file mykey.key \
|
||||||
> -ssl_listen :443
|
> -ssl_listen :443
|
||||||
|
|
||||||
Rdio Scanner v6.5.3
|
Rdio Scanner v6.5.4
|
||||||
----------------------------------
|
----------------------------------
|
||||||
2022/08/10 08:50:58 server started
|
2022/09/09 08:50:58 server started
|
||||||
2022/08/10 08:50:58 main interface at http://macos.local
|
2022/09/09 08:50:58 main interface at http://macos.local
|
||||||
2022/08/10 08:50:58 main interface at https://macos.local
|
2022/09/09 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 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 +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/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.
|
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@macos rdio-scanner % ./rdio-scanner
|
||||||
|
|
||||||
Rdio Scanner v6.5.3
|
Rdio Scanner v6.5.4
|
||||||
----------------------------------
|
----------------------------------
|
||||||
2022/08/10 08:54:08 server started
|
2022/09/09 08:54:08 server started
|
||||||
2022/08/10 08:54:08 main interface at http://macos.local
|
2022/09/09 08:54:08 main interface at http://macos.local
|
||||||
2022/08/10 08:54:08 main interface at https://macos.local
|
2022/09/09 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 admin interface at https://macos.local/admin
|
||||||
|
|
||||||
## Install Rdio Scanner as a service
|
## Install Rdio Scanner as a service
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
| [Trunk Recorder](https://github.com/robotastic/trunk-recorder) | X | X |
|
||||||
| [RTLSDR-Airband](https://github.com/szpajder/RTLSDR-Airband) | | 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 | |
|
| [voxcall](https://github.com/aaknitt/voxcall) | X | |
|
||||||
| [ProScan](https://www.proscan.org/) | | X |
|
| [ProScan](https://www.proscan.org/) | | X |
|
||||||
|
| [DSDPlus Fast Lane](https://https://www.dsdplus.com/) | | X |
|
||||||
|
|
||||||
# Improve your experience on the go
|
# 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:~ $ 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.5.3.zip
|
> ~/rdio-scanner-freebsd-amd64-v6.5.4.zip
|
||||||
Archive: ../rdio-scanner-freebsd-amd64-v6.5.3.zip
|
Archive: ../rdio-scanner-freebsd-amd64-v6.5.4.zip
|
||||||
extracting: rdio-scanner
|
extracting: rdio-scanner
|
||||||
extracting: rdio-scanner.pdf
|
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@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/09/09 08:16:36 server started
|
||||||
2022/08/10 08:16:36 main interface at http://pc-freebsd:3000
|
2022/09/09 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 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 +82,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.5.3
|
Rdio Scanner v6.5.4
|
||||||
----------------------------------
|
----------------------------------
|
||||||
2022/08/10 08:19:38 server started
|
2022/09/09 08:19:38 server started
|
||||||
2022/08/10 08:19:38 main interface at http://pc-freebsd
|
2022/09/09 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 admin interface at http://pc-freebsd/admin
|
||||||
|
|
||||||
## Listening on a SSL port
|
## 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_key_file mykey.key \
|
||||||
> -ssl_listen :443
|
> -ssl_listen :443
|
||||||
|
|
||||||
Rdio Scanner v6.5.3
|
Rdio Scanner v6.5.4
|
||||||
----------------------------------
|
----------------------------------
|
||||||
2022/08/10 10:34:29 server started
|
2022/09/09 10:34:29 server started
|
||||||
2022/08/10 10:34:29 main interface at http://pc-freebsd
|
2022/09/09 10:34:29 main interface at http://pc-freebsd
|
||||||
2022/08/10 10:34:29 main interface at https://pc-freebsd
|
2022/09/09 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 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 +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_auto_cert mydomain.com \
|
||||||
> -ssl_listen :443 \
|
> -ssl_listen :443 \
|
||||||
> -config_save
|
> -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.
|
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:
|
Password:
|
||||||
root@pc-freebsd:/home/rdio/rdio-scanner # ./rdio-scanner
|
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/09/09 10:38:28 server started
|
||||||
2022/08/10 10:38:29 main interface at http://pc-freebsd
|
2022/09/09 10:38:29 main interface at http://pc-freebsd
|
||||||
2022/08/10 10:38:29 main interface at https://pc-freebsd
|
2022/09/09 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:29 admin interface at https://pc-freebsd/admin
|
||||||
|
|
||||||
## Install Rdio Scanner as a service
|
## Install Rdio Scanner as a service
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
| [Trunk Recorder](https://github.com/robotastic/trunk-recorder) | X | X |
|
||||||
| [RTLSDR-Airband](https://github.com/szpajder/RTLSDR-Airband) | | 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 | |
|
| [voxcall](https://github.com/aaknitt/voxcall) | X | |
|
||||||
| [ProScan](https://www.proscan.org/) | | X |
|
| [ProScan](https://www.proscan.org/) | | X |
|
||||||
|
| [DSDPlus Fast Lane](https://https://www.dsdplus.com/) | | X |
|
||||||
|
|
||||||
# Improve your experience on the go
|
# 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 ~]$ 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.5.3.zip
|
> ~/Downloads/rdio-scanner-linux-amd64-v6.5.4.zip
|
||||||
Archive: /home/rdio/Downloads/rdio-scanner-linux-amd64-v6.5.3.zip
|
Archive: /home/rdio/Downloads/rdio-scanner-linux-amd64-v6.5.4.zip
|
||||||
inflating: rdio-scanner
|
inflating: rdio-scanner
|
||||||
inflating: rdio-scanner.pdf
|
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@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/09/09 09:11:48 server started
|
||||||
2022/08/10 09:11:48 main interface at http://pc-linux:3000
|
2022/09/09 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 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 +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
|
[rdio@pc-linux rdio-scanner]$ sudo ./rdio-scanner -listen :80
|
||||||
[sudo] password for rdio:
|
[sudo] password for rdio:
|
||||||
|
|
||||||
Rdio Scanner v6.5.3
|
Rdio Scanner v6.5.4
|
||||||
----------------------------------
|
----------------------------------
|
||||||
2022/08/10 09:14:00 server started
|
2022/09/09 09:14:00 server started
|
||||||
2022/08/10 09:14:00 main interface at http://pc-linux
|
2022/09/09 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 admin interface at http://pc-linux/admin
|
||||||
|
|
||||||
## Listening on a SSL port
|
## 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
|
> -ssl_listen :443
|
||||||
[sudo] password for rdio:
|
[sudo] password for rdio:
|
||||||
|
|
||||||
Rdio Scanner v6.5.3
|
Rdio Scanner v6.5.4
|
||||||
----------------------------------
|
----------------------------------
|
||||||
2022/08/10 09:16:47 server started
|
2022/09/09 09:16:47 server started
|
||||||
2022/08/10 09:16:47 main interface at http://pc-linux
|
2022/09/09 09:16:47 main interface at http://pc-linux
|
||||||
2022/08/10 09:16:47 main interface at https://pc-linux
|
2022/09/09 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 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 +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_auto_cert mydomain.com \
|
||||||
> -ssl_listen :443 \
|
> -ssl_listen :443 \
|
||||||
> -config_save
|
> -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.
|
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
|
[rdio@pc-linux rdio-scanner]$ sudo ./rdio-scanner
|
||||||
[sudo] Mot de passe de rdio :
|
[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/09/09 09:20:40 server started
|
||||||
2022/08/10 09:20:40 main interface at http://pc-linux
|
2022/09/09 09:20:40 main interface at http://pc-linux
|
||||||
2022/08/10 09:20:40 main interface at https://pc-linux
|
2022/09/09 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 admin interface at https://pc-linux/admin
|
||||||
|
|
||||||
## Install Rdio Scanner as a service
|
## Install Rdio Scanner as a service
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
| [Trunk Recorder](https://github.com/robotastic/trunk-recorder) | X | X |
|
||||||
| [RTLSDR-Airband](https://github.com/szpajder/RTLSDR-Airband) | | 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 | |
|
| [voxcall](https://github.com/aaknitt/voxcall) | X | |
|
||||||
| [ProScan](https://www.proscan.org/) | | X |
|
| [ProScan](https://www.proscan.org/) | | X |
|
||||||
|
| [DSDPlus Fast Lane](https://https://www.dsdplus.com/) | | X |
|
||||||
|
|
||||||
# Improve your experience on the go
|
# 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> cd rdio-scanner
|
||||||
|
|
||||||
C:\Users\rdio\rdio-scanner> tar -xvf ^
|
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.exe
|
||||||
x rdio-scanner.pdf
|
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
|
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/09/09 13:48:48 server started
|
||||||
2022/08/10 13:48:48 main interface at http://pc-windows:3000
|
2022/09/09 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 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 +79,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.5.3
|
Rdio Scanner v6.5.4
|
||||||
----------------------------------
|
----------------------------------
|
||||||
2022/08/10 10:53:31 server started
|
2022/09/09 10:53:31 server started
|
||||||
2022/08/10 10:53:31 main interface at http://pc-windows
|
2022/09/09 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 admin interface at http://pc-windows/admin
|
||||||
|
|
||||||
## Listening on a SSL port
|
## 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_key_file meykey.key ^
|
||||||
-ssl_listen :443
|
-ssl_listen :443
|
||||||
|
|
||||||
Rdio Scanner v6.5.3
|
Rdio Scanner v6.5.4
|
||||||
----------------------------------
|
----------------------------------
|
||||||
2022/08/10 11:05:43 server started
|
2022/09/09 11:05:43 server started
|
||||||
2022/08/10 11:05:43 main interface at http://pc-windows
|
2022/09/09 11:05:43 main interface at http://pc-windows
|
||||||
2022/08/10 11:05:43 main interface at https://pc-windows
|
2022/09/09 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 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 +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/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.
|
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
|
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/09/09 11:11:28 server started
|
||||||
2022/08/10 11:11:28 main interface at http://pc-windows
|
2022/09/09 11:11:28 main interface at http://pc-windows
|
||||||
2022/08/10 11:11:28 main interface at https://pc-windows
|
2022/09/09 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 admin interface at https://pc-windows/admin
|
||||||
|
|
||||||
\pagebreak{}
|
\pagebreak{}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -338,6 +338,8 @@ func (calls *Calls) Search(searchOptions *CallsSearchOptions, client *Client) (*
|
||||||
|
|
||||||
if t, err = db.ParseDateTime(dateTime); err == nil {
|
if t, err = db.ParseDateTime(dateTime); err == nil {
|
||||||
searchResults.DateStop = t
|
searchResults.DateStop = t
|
||||||
|
} else {
|
||||||
|
searchResults.DateStop = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v := searchOptions.Sort.(type) {
|
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)
|
stop = start.Add(time.Hour*24 - time.Millisecond)
|
||||||
|
|
||||||
} else {
|
} 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()))
|
start = time.Date(v.Year(), v.Month(), v.Day(), v.Hour(), v.Minute(), 0, 0, time.UTC).Add(time.Hour*-24 + time.Millisecond)
|
||||||
stop = start.Add(time.Hour*24 - time.Millisecond - time.Duration(v.Hour())).Add(time.Minute * time.Duration(-v.Minute()))
|
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))
|
where += fmt.Sprintf(" and (`dateTime` between '%v' and '%v')", start.Format(df), stop.Format(df))
|
||||||
|
|
|
||||||
|
|
@ -206,61 +206,64 @@ func (client *Client) SendListenersCount(count int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Clients struct {
|
type Clients struct {
|
||||||
Map sync.Map
|
Map map[*Client]bool
|
||||||
count int
|
mutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClients() *Clients {
|
func NewClients() *Clients {
|
||||||
return &Clients{
|
return &Clients{
|
||||||
Map: sync.Map{},
|
Map: map[*Client]bool{},
|
||||||
count: 0,
|
mutex: sync.Mutex{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (clients *Clients) AccessCount(client *Client) int {
|
func (clients *Clients) AccessCount(client *Client) int {
|
||||||
|
clients.mutex.Lock()
|
||||||
|
defer clients.mutex.Unlock()
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
|
|
||||||
clients.Map.Range(func(k any, _ any) bool {
|
for c := range clients.Map {
|
||||||
switch c := k.(type) {
|
|
||||||
case *Client:
|
|
||||||
if c.Access == client.Access {
|
if c.Access == client.Access {
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (clients *Clients) Add(client *Client) {
|
func (clients *Clients) Add(client *Client) {
|
||||||
clients.count = clients.count + 1
|
clients.mutex.Lock()
|
||||||
clients.Map.Store(client, true)
|
defer clients.mutex.Unlock()
|
||||||
|
|
||||||
|
clients.Map[client] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (clients *Clients) Count() int {
|
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) {
|
func (clients *Clients) EmitCall(call *Call, restricted bool) {
|
||||||
clients.Map.Range(func(k any, _ any) bool {
|
clients.mutex.Lock()
|
||||||
switch c := k.(type) {
|
defer clients.mutex.Unlock()
|
||||||
case *Client:
|
|
||||||
|
for c := range clients.Map {
|
||||||
if (!restricted || c.Access.HasAccess(call)) && c.Livefeed.IsEnabled(call) {
|
if (!restricted || c.Access.HasAccess(call)) && c.Livefeed.IsEnabled(call) {
|
||||||
c.Send <- &Message{Command: MessageCommandCall, Payload: call}
|
c.Send <- &Message{Command: MessageCommandCall, Payload: call}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
count := clients.Count()
|
clients.mutex.Lock()
|
||||||
|
defer clients.mutex.Unlock()
|
||||||
|
|
||||||
clients.Map.Range(func(k any, _ any) bool {
|
count := len(clients.Map)
|
||||||
switch c := k.(type) {
|
|
||||||
case *Client:
|
for c := range clients.Map {
|
||||||
if restricted {
|
if restricted {
|
||||||
c.Send <- &Message{Command: MessageCommandPin}
|
c.Send <- &Message{Command: MessageCommandPin}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -271,26 +274,22 @@ func (clients *Clients) EmitConfig(groups *Groups, options *Options, systems *Sy
|
||||||
c.SendListenersCount(count)
|
c.SendListenersCount(count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (clients *Clients) EmitListenersCount() {
|
func (clients *Clients) EmitListenersCount() {
|
||||||
count := clients.Count()
|
clients.mutex.Lock()
|
||||||
|
defer clients.mutex.Unlock()
|
||||||
|
|
||||||
clients.Map.Range(func(k any, _ any) bool {
|
count := len(clients.Map)
|
||||||
switch c := k.(type) {
|
|
||||||
case *Client:
|
for c := range clients.Map {
|
||||||
c.SendListenersCount(count)
|
c.SendListenersCount(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (clients *Clients) Remove(client *Client) {
|
func (clients *Clients) Remove(client *Client) {
|
||||||
if _, loaded := clients.Map.LoadAndDelete(client); loaded {
|
clients.mutex.Lock()
|
||||||
clients.count = clients.count - 1
|
defer clients.mutex.Unlock()
|
||||||
}
|
|
||||||
|
delete(clients.Map, client)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ func (command *Command) Do(action string) {
|
||||||
command.url = readVal()
|
command.url = readVal()
|
||||||
|
|
||||||
if err != nil || !regexp.MustCompile(`^https?://`).Match([]byte(command.url)) {
|
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:
|
default:
|
||||||
command.exitWithError(errors.New("Invalid response"))
|
command.exitWithError(errors.New("invalid response"))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
command.exitWithError(err)
|
command.exitWithError(err)
|
||||||
|
|
@ -314,10 +314,10 @@ func (command *Command) login() {
|
||||||
}
|
}
|
||||||
fmt.Println("Logged in.")
|
fmt.Println("Logged in.")
|
||||||
default:
|
default:
|
||||||
command.exitWithError(errors.New("No token in response"))
|
command.exitWithError(errors.New("no token in response"))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
command.exitWithError(errors.New("Invalid response"))
|
command.exitWithError(errors.New("invalid response"))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
command.exitWithError(err)
|
command.exitWithError(err)
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -48,7 +47,6 @@ type Controller struct {
|
||||||
Register chan *Client
|
Register chan *Client
|
||||||
Unregister chan *Client
|
Unregister chan *Client
|
||||||
Ingest chan *Call
|
Ingest chan *Call
|
||||||
ingestMutex sync.Mutex
|
|
||||||
running bool
|
running bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,7 +269,7 @@ func (controller *Controller) IngestCall(call *Call) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if system == nil || talkgroup == nil {
|
if system == nil || talkgroup == nil {
|
||||||
logCall(call, LogLevelInfo, "no matching system/talkgroup")
|
logCall(call, LogLevelWarn, "no matching system/talkgroup")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -516,7 +514,7 @@ func (controller *Controller) Start() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
c := make(chan os.Signal)
|
c := make(chan os.Signal, 8)
|
||||||
signal.Notify(c, os.Interrupt)
|
signal.Notify(c, os.Interrupt)
|
||||||
<-c
|
<-c
|
||||||
controller.Terminate()
|
controller.Terminate()
|
||||||
|
|
@ -536,7 +534,10 @@ func (controller *Controller) Start() error {
|
||||||
if timer != nil {
|
if timer != nil {
|
||||||
timer.Stop()
|
timer.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
timer = time.AfterFunc(time.Second, func() {
|
timer = time.AfterFunc(time.Second, func() {
|
||||||
|
timer = nil
|
||||||
|
|
||||||
controller.LogClientsCount()
|
controller.LogClientsCount()
|
||||||
|
|
||||||
if controller.Options.ShowListenersCount {
|
if controller.Options.ShowListenersCount {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"io/ioutil"
|
|
||||||
"math"
|
"math"
|
||||||
"mime"
|
"mime"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -38,9 +37,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DirwatchKindDefault = "default"
|
DirwatchTypeDefault = "default"
|
||||||
DirwatchKindSdrTrunk = "sdr-trunk"
|
DirwatchTypeDSDPlus = "dsdplus"
|
||||||
DirwatchKindTrunkRecorder = "trunk-recorder"
|
DirwatchTypeSdrTrunk = "sdr-trunk"
|
||||||
|
DirwatchTypeTrunkRecorder = "trunk-recorder"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Dirwatch struct {
|
type Dirwatch struct {
|
||||||
|
|
@ -59,9 +59,19 @@ type Dirwatch struct {
|
||||||
UsePolling bool `json:"usePolling"`
|
UsePolling bool `json:"usePolling"`
|
||||||
controller *Controller
|
controller *Controller
|
||||||
dirs map[string]bool
|
dirs map[string]bool
|
||||||
|
mutex sync.Mutex
|
||||||
|
timers map[string]*time.Timer
|
||||||
watcher *fsnotify.Watcher
|
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 {
|
func (dirwatch *Dirwatch) FromMap(m map[string]any) *Dirwatch {
|
||||||
switch v := m["_id"].(type) {
|
switch v := m["_id"].(type) {
|
||||||
case float64:
|
case float64:
|
||||||
|
|
@ -135,16 +145,18 @@ func (dirwatch *Dirwatch) Ingest(p string) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
switch dirwatch.Kind {
|
switch dirwatch.Kind {
|
||||||
case DirwatchKindTrunkRecorder:
|
case DirwatchTypeDSDPlus:
|
||||||
|
err = dirwatch.ingestDSDPlus(p)
|
||||||
|
case DirwatchTypeTrunkRecorder:
|
||||||
err = dirwatch.ingestTrunkRecorder(p)
|
err = dirwatch.ingestTrunkRecorder(p)
|
||||||
case DirwatchKindSdrTrunk:
|
case DirwatchTypeSdrTrunk:
|
||||||
err = dirwatch.ingestSdrTrunk(p)
|
err = dirwatch.ingestSdrTrunk(p)
|
||||||
default:
|
default:
|
||||||
err = dirwatch.ingestDefault(p)
|
err = dirwatch.ingestDefault(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
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) {
|
switch v := dirwatch.Extension.(type) {
|
||||||
case string:
|
case string:
|
||||||
|
if len(v) > 0 {
|
||||||
ext = fmt.Sprintf(".%s", v)
|
ext = fmt.Sprintf(".%s", v)
|
||||||
|
} else {
|
||||||
|
ext = ".wav"
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
ext = ".wav"
|
ext = ".wav"
|
||||||
}
|
}
|
||||||
|
|
@ -202,7 +218,7 @@ func (dirwatch *Dirwatch) ingestDefault(p string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dirwatch *Dirwatch) ingestSdrTrunk(p string) error {
|
func (dirwatch *Dirwatch) ingestDSDPlus(p string) error {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
ext string
|
ext string
|
||||||
|
|
@ -229,6 +245,48 @@ func (dirwatch *Dirwatch) ingestSdrTrunk(p string) error {
|
||||||
call.AudioType = mime.TypeByExtension(path.Ext(p))
|
call.AudioType = mime.TypeByExtension(path.Ext(p))
|
||||||
call.Frequency = dirwatch.Frequency
|
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 {
|
if call.Audio, err = os.ReadFile(p); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -290,11 +348,11 @@ func (dirwatch *Dirwatch) ingestTrunkRecorder(p string) error {
|
||||||
call.System = v
|
call.System = v
|
||||||
}
|
}
|
||||||
|
|
||||||
if call.Audio, err = ioutil.ReadFile(audioName); err != nil {
|
if call.Audio, err = os.ReadFile(audioName); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if b, err = ioutil.ReadFile(p); err != nil {
|
if b, err = os.ReadFile(p); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -550,15 +608,16 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
var timers = sync.Map{}
|
|
||||||
|
|
||||||
logError := func(err error) {
|
logError := func(err error) {
|
||||||
controller.Logs.LogEvent(LogLevelError, fmt.Sprintf("dirwatch.watcher: %v", err.Error()))
|
controller.Logs.LogEvent(LogLevelError, fmt.Sprintf("dirwatch.watcher: %v", err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
newTimer := func(eventName string) *time.Timer {
|
newTimer := func(eventName string) *time.Timer {
|
||||||
return time.AfterFunc(delay, func() {
|
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 {
|
if _, err := os.Stat(eventName); err == nil {
|
||||||
dirwatch.Ingest(eventName)
|
dirwatch.Ingest(eventName)
|
||||||
|
|
@ -567,31 +626,35 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
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) {
|
switch v := recover().(type) {
|
||||||
case error:
|
case error:
|
||||||
controller.Logs.LogEvent(LogLevelError, v.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 {
|
for {
|
||||||
if dirwatch.watcher == nil {
|
if dirwatch.watcher == nil {
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case event, ok := <-dirwatch.watcher.Events:
|
case event, ok := <-dirwatch.watcher.Events:
|
||||||
if ok {
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
switch event.Op {
|
switch event.Op {
|
||||||
case fsnotify.Create:
|
case fsnotify.Create:
|
||||||
if dirwatch.isDir(event.Name) {
|
if dirwatch.isDir(event.Name) {
|
||||||
|
|
@ -600,13 +663,10 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if f, ok := timers.LoadAndDelete(event.Name); ok {
|
if dirwatch.timers[event.Name] != nil {
|
||||||
switch v := f.(type) {
|
dirwatch.timers[event.Name].Stop()
|
||||||
case *time.Timer:
|
|
||||||
v.Stop()
|
|
||||||
}
|
}
|
||||||
}
|
dirwatch.timers[event.Name] = newTimer(event.Name)
|
||||||
timers.Store(event.Name, newTimer(event.Name))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case fsnotify.Remove:
|
case fsnotify.Remove:
|
||||||
|
|
@ -619,24 +679,20 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
case fsnotify.Write:
|
case fsnotify.Write:
|
||||||
if f, ok := timers.LoadAndDelete(event.Name); ok {
|
if dirwatch.timers[event.Name] != nil {
|
||||||
switch v := f.(type) {
|
dirwatch.timers[event.Name].Stop()
|
||||||
case *time.Timer:
|
|
||||||
v.Stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
timers.Store(event.Name, newTimer(event.Name))
|
|
||||||
}
|
}
|
||||||
|
dirwatch.timers[event.Name] = newTimer(event.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
case err, ok := <-dirwatch.watcher.Errors:
|
case err, ok := <-dirwatch.watcher.Errors:
|
||||||
if ok {
|
if ok {
|
||||||
logError(err)
|
logError(err)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
|
@ -671,8 +727,9 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
|
||||||
|
|
||||||
func (dirwatch *Dirwatch) Stop() {
|
func (dirwatch *Dirwatch) Stop() {
|
||||||
if dirwatch.watcher != nil {
|
if dirwatch.watcher != nil {
|
||||||
dirwatch.watcher.Close()
|
w := dirwatch.watcher
|
||||||
dirwatch.watcher = nil
|
dirwatch.watcher = nil
|
||||||
|
w.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -699,8 +756,7 @@ func (dirwatches *Dirwatches) FromMap(f []any) *Dirwatches {
|
||||||
for _, f := range f {
|
for _, f := range f {
|
||||||
switch v := f.(type) {
|
switch v := f.(type) {
|
||||||
case map[string]any:
|
case map[string]any:
|
||||||
dirwatch := &Dirwatch{}
|
dirwatch := NewDirwatch().FromMap(v)
|
||||||
dirwatch.FromMap(v)
|
|
||||||
dirwatches.List = append(dirwatches.List, dirwatch)
|
dirwatches.List = append(dirwatches.List, dirwatch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -739,7 +795,7 @@ func (dirwatches *Dirwatches) Read(db *Database) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for rows.Next() {
|
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 {
|
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
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ func (ffmpeg *FFMpeg) Convert(call *Call, systems *Systems, tags *Tags, mode uin
|
||||||
if !ffmpeg.warned {
|
if !ffmpeg.warned {
|
||||||
ffmpeg.warned = true
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
@ -198,7 +198,7 @@ func main() {
|
||||||
TLSConfig: tlsConfig,
|
TLSConfig: tlsConfig,
|
||||||
ReadTimeout: 30 * time.Second,
|
ReadTimeout: 30 * time.Second,
|
||||||
WriteTimeout: 30 * time.Second,
|
WriteTimeout: 30 * time.Second,
|
||||||
ErrorLog: log.New(ioutil.Discard, "", 0),
|
ErrorLog: log.New(io.Discard, "", 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
s.SetKeepAlivesEnabled(true)
|
s.SetKeepAlivesEnabled(true)
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,70 @@ import (
|
||||||
"mime"
|
"mime"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/dhowden/tag"
|
"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 {
|
func ParseSdrTrunkMeta(call *Call, controller *Controller) error {
|
||||||
var (
|
var (
|
||||||
s []string
|
s []string
|
||||||
|
|
@ -43,7 +100,7 @@ func ParseSdrTrunkMeta(call *Call, controller *Controller) error {
|
||||||
|
|
||||||
s = regexp.MustCompile(`^([0-9]+) ?(.*)$`).FindStringSubmatch(m.Artist())
|
s = regexp.MustCompile(`^([0-9]+) ?(.*)$`).FindStringSubmatch(m.Artist())
|
||||||
if len(s) >= 2 {
|
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
|
return err
|
||||||
}
|
}
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,4 @@
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
const Version = "6.5.3"
|
const Version = "6.5.4"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue