Version 6.3.0

This commit is contained in:
Chrystian Huot 2022-05-19 10:15:33 -04:00
parent 0fbe97469b
commit 508ab1d700
31 changed files with 2257 additions and 1792 deletions

View file

@ -1,5 +1,13 @@
# Change log # Change log
## Version 6.3
- Change scroll speed when drag droping talkgroups or units in a system (discussion #170).
- System Ids listed in the `Config / Options / AFS Systems` will have their talkgroup Ids displayed in AFS format (issue #163).
- New dirwatch meta tags #GROUP #SYSLBL #TAG #TGAFS and #UNIT for better ProScan compatibility (issue #164).
- Playback mode will now catch up to live (issue #175).
- Dirwatch code rewrite (issue #177).
## Version 6.2 ## Version 6.2
- New max clients options which is 200 by default. - New max clients options which is 200 by default.

View file

@ -13,6 +13,7 @@ Your os distribution may have all of the following prerequisites available in it
- latest long-term support version of Node.js ([here](https://nodejs.org/en/)) - latest long-term support version of Node.js ([here](https://nodejs.org/en/))
- latest version of Go ([here](https://go.dev/dl/)) - latest version of Go ([here](https://go.dev/dl/))
- latest version of Pandoc with pandoc-pdf ([here](https://pandoc.org/installing.html)) - latest version of Pandoc with pandoc-pdf ([here](https://pandoc.org/installing.html))
- latest version of TeX Live ([here](https://www.tug.org/texlive/))
- latest version of Info-Zip ([here](http://infozip.sourceforge.net/)) - latest version of Info-Zip ([here](http://infozip.sourceforge.net/))
- latest version of podman ([here](https://podman.io/)), only for building containers - latest version of podman ([here](https://podman.io/)), only for building containers
@ -26,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.2.5.zip rdio-scanner-darwin-amd64-v6.3.0.zip
rdio-scanner-darwin-arm64-v6.2.5.zip rdio-scanner-darwin-arm64-v6.3.0.zip
rdio-scanner-freebsd-amd64-v6.2.5.zip rdio-scanner-freebsd-amd64-v6.3.0.zip
rdio-scanner-linux-386-v6.2.5.zip rdio-scanner-linux-386-v6.3.0.zip
rdio-scanner-linux-amd64-v6.2.5.zip rdio-scanner-linux-amd64-v6.3.0.zip
rdio-scanner-linux-arm64-v6.2.5.zip rdio-scanner-linux-arm64-v6.3.0.zip
rdio-scanner-linux-arm-v6.2.5.zip rdio-scanner-linux-arm-v6.3.0.zip
rdio-scanner-windows-amd64-v6.2.5.zip rdio-scanner-windows-amd64-v6.3.0.zip
**Happy Rdio scanning !** **Happy Rdio scanning !**

View file

@ -16,8 +16,8 @@
################################################################################ ################################################################################
app := rdio-scanner app := rdio-scanner
date := 2022/04/06 date := 2022/05/19
ver := 6.2.5 ver := 6.3.0
client := $(wildcard client/*.json client/*.ts) client := $(wildcard client/*.json client/*.ts)
server := $(wildcard server/*.go) server := $(wildcard server/*.go)

3189
client/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,32 +1,32 @@
{ {
"author": "Chrystian Huot <chrystian.huot@saubeo.solutions>", "author": "Chrystian Huot <chrystian.huot@saubeo.solutions>",
"dependencies": { "dependencies": {
"@angular/animations": "^13.3.1", "@angular/animations": "^13.3.9",
"@angular/cdk": "^13.3.2", "@angular/cdk": "^13.3.7",
"@angular/common": "~13.3.1", "@angular/common": "~13.3.9",
"@angular/compiler": "~13.3.1", "@angular/compiler": "~13.3.9",
"@angular/core": "~13.3.1", "@angular/core": "~13.3.9",
"@angular/forms": "~13.3.1", "@angular/forms": "~13.3.9",
"@angular/material": "^13.3.2", "@angular/material": "^13.3.7",
"@angular/platform-browser": "~13.3.1", "@angular/platform-browser": "~13.3.9",
"@angular/platform-browser-dynamic": "~13.3.1", "@angular/platform-browser-dynamic": "~13.3.9",
"@angular/router": "^13.3.1", "@angular/router": "^13.3.9",
"@angular/service-worker": "^13.3.1", "@angular/service-worker": "^13.3.9",
"rxjs": "~7.4.0", "rxjs": "~7.5.5",
"tslib": "^2.3.1", "tslib": "^2.4.0",
"zone.js": "~0.11.5" "zone.js": "~0.11.5"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^13.3.1", "@angular-devkit/build-angular": "^13.3.6",
"@angular/cli": "^13.3.1", "@angular/cli": "^13.3.6",
"@angular/compiler-cli": "~13.3.1", "@angular/compiler-cli": "~13.3.9",
"@types/node": "^17.0.23", "@types/node": "^17.0.34",
"@typescript-eslint/eslint-plugin": "^5.18.0", "@typescript-eslint/eslint-plugin": "^5.25.0",
"@typescript-eslint/parser": "^5.18.0", "@typescript-eslint/parser": "^5.25.0",
"ajv-keywords": "^5.1.0", "ajv-keywords": "^5.1.0",
"eslint": "^8.12.0", "eslint": "^8.15.0",
"ts-node": "~10.7.0", "ts-node": "~10.7.0",
"typescript": "~4.4.4" "typescript": "~4.6.4"
}, },
"engines": { "engines": {
"node": ">=16.0" "node": ">=16.0"
@ -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.2.5" "version": "6.3.0"
} }

View file

@ -129,6 +129,7 @@ export interface LogsQueryOptions {
} }
export interface Options { export interface Options {
afsSystems?: string;
autoPopulate?: boolean; autoPopulate?: boolean;
dimmerDelay?: number; dimmerDelay?: number;
disableAudioConversion?: boolean; disableAudioConversion?: boolean;
@ -491,6 +492,7 @@ export class RdioScannerAdminService implements OnDestroy {
newOptionsForm(options?: Options): FormGroup { newOptionsForm(options?: Options): FormGroup {
return this.ngFormBuilder.group({ return this.ngFormBuilder.group({
afsSystems: [options?.afsSystems, this.validateAfsSystems()],
autoPopulate: [options?.autoPopulate], autoPopulate: [options?.autoPopulate],
dimmerDelay: [options?.dimmerDelay, [Validators.required, Validators.min(0)]], dimmerDelay: [options?.dimmerDelay, [Validators.required, Validators.min(0)]],
disableAudioConversion: [options?.disableAudioConversion], disableAudioConversion: [options?.disableAudioConversion],
@ -595,6 +597,12 @@ export class RdioScannerAdminService implements OnDestroy {
}; };
} }
private validateAfsSystems(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
return typeof control.value === 'string' && control.value.length ? /^[0-9]+(,[0-9]+)*$/.test(control.value) ? null : { invalid: true } : null;
};
}
private validateApiKey(): ValidatorFn { private validateApiKey(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => { return (control: AbstractControl): ValidationErrors | null => {
if (typeof control.value !== 'string' || !control.value.length) { if (typeof control.value !== 'string' || !control.value.length) {
@ -629,20 +637,6 @@ export class RdioScannerAdminService implements OnDestroy {
}; };
} }
private validateDownstreamUrl(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (typeof control.value !== 'string' || !control.value.length) {
return null;
}
const downstream: Downstream[] = control.parent?.parent?.getRawValue() || [];
const count = downstream.reduce((c, a) => c += a.url === control.value ? 1 : 0, 0);
return count > 1 ? { duplicate: true } : null;
};
}
private validateDirwatchSystemId(): ValidatorFn { private validateDirwatchSystemId(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => { return (control: AbstractControl): ValidationErrors | null => {
const dirwatch = control.parent?.getRawValue() || {}; const dirwatch = control.parent?.getRawValue() || {};
@ -667,6 +661,20 @@ export class RdioScannerAdminService implements OnDestroy {
}; };
} }
private validateDownstreamUrl(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (typeof control.value !== 'string' || !control.value.length) {
return null;
}
const downstream: Downstream[] = control.parent?.parent?.getRawValue() || [];
const count = downstream.reduce((c, a) => c += a.url === control.value ? 1 : 0, 0);
return count > 1 ? { duplicate: true } : null;
};
}
private validateExtension(): ValidatorFn { private validateExtension(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => { return (control: AbstractControl): ValidationErrors | null => {
if (typeof control.value !== 'string' || !control.value.length) { if (typeof control.value !== 'string' || !control.value.length) {
@ -709,7 +717,7 @@ export class RdioScannerAdminService implements OnDestroy {
return null; return null;
} }
const masks = ['#DATE', '#HZ', '#KHZ', '#MHZ', '#SYS', '#TIME', '#TG', '#TGHZ', '#TGKHZ', '#TGMHZ', '#UNIT', '#ZTIME']; const masks = ['#DATE', '#GROUP', '#HZ', '#KHZ', '#MHZ', '#SYS', '#SYSLBL', '#TAG', '#TG', '#TGAFS', '#TGHZ', '#TGKHZ', '#TGMHZ', '#TIME', '#UNIT', '#ZTIME'];
const metas = control.value.match(/(#[A-Z]+)/g) || []; const metas = control.value.match(/(#[A-Z]+)/g) || [];

View file

@ -6,7 +6,7 @@
</div> </div>
<p *ngIf="!accesses.length" class="mat-small text-center">No access is defined, your instance is open and accessible to <p *ngIf="!accesses.length" class="mat-small text-center">No access is defined, your instance is open and accessible to
all</p> all</p>
<mat-accordion displayMode="flat" cdkDropList [cdkDropListData]="accesses" (cdkDropListDropped)="drop($event)"> <mat-accordion displayMode="flat" cdkDropList [cdkDropListAutoScrollStep]=64 [cdkDropListData]="accesses" (cdkDropListDropped)="drop($event)">
<mat-expansion-panel *ngFor="let access of accesses; index as i" cdkDrag> <mat-expansion-panel *ngFor="let access of accesses; index as i" cdkDrag>
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title> <mat-panel-title>

View file

@ -4,7 +4,7 @@
<button type="button" mat-button color="accent" (click)="add()">New API key</button> <button type="button" mat-button color="accent" (click)="add()">New API key</button>
</div> </div>
<p *ngIf="!apiKeys.length" class="mat-small text-center">No defined API keys</p> <p *ngIf="!apiKeys.length" class="mat-small text-center">No defined API keys</p>
<mat-accordion displayMode="flat" cdkDropList [cdkDropListData]="apiKeys" (cdkDropListDropped)="drop($event)"> <mat-accordion displayMode="flat" cdkDropList [cdkDropListAutoScrollStep]=64 [cdkDropListData]="apiKeys" (cdkDropListDropped)="drop($event)">
<mat-expansion-panel *ngFor="let apiKey of apiKeys; index as i" cdkDrag> <mat-expansion-panel *ngFor="let apiKey of apiKeys; index as i" cdkDrag>
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title> <mat-panel-title>

View file

@ -2,7 +2,7 @@
<p class="mat-body">Define a dirwatch to monitor new audio files from any directory.</p> <p class="mat-body">Define a dirwatch to monitor new audio files from any directory.</p>
<button type="button" mat-button color="accent" (click)="add()">New dir watch</button> <button type="button" mat-button color="accent" (click)="add()">New dir watch</button>
</div> </div>
<mat-accordion displayMode="flat" cdkDropList [cdkDropListData]="dirWatches" (cdkDropListDropped)="drop($event)"> <mat-accordion displayMode="flat" cdkDropList [cdkDropListAutoScrollStep]=64 [cdkDropListData]="dirWatches" (cdkDropListDropped)="drop($event)">
<mat-expansion-panel *ngFor="let dirWatch of dirWatches; index as i" cdkDrag> <mat-expansion-panel *ngFor="let dirWatch of dirWatches; index as i" cdkDrag>
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title> <mat-panel-title>
@ -121,17 +121,21 @@
<li><b>#DATE</b> - extract the date like 20201231&nbsp;(YYYYMMMDD), <li><b>#DATE</b> - extract the date like 20201231&nbsp;(YYYYMMMDD),
2020-12-31&nbsp;(YYYY-MM-DD) or 2020_12_31&nbsp;(YYYY_MM_DD). 2020-12-31&nbsp;(YYYY-MM-DD) or 2020_12_31&nbsp;(YYYY_MM_DD).
</li> </li>
<li><b>#GROUP</b> - extract the group label.</li>
<li><b>#HZ</b> - extract the frequency in hertz like 119100000.</li> <li><b>#HZ</b> - extract the frequency in hertz like 119100000.</li>
<li><b>#KHZ</b> - extract the frequency in kilohertz like 119100.</li> <li><b>#KHZ</b> - extract the frequency in kilohertz like 119100.</li>
<li><b>#MHZ</b> - extract the frequency in megahertz like 119.100.</li> <li><b>#MHZ</b> - extract the frequency in megahertz like 119.100.</li>
<li><b>#SYS</b> - extract the system id like 11.</li> <li><b>#SYS</b> - extract the system id like 11.</li>
<li><b>#TIME</b> - extract the local time like 0853439&nbsp;(HHMMSS), 08-34-39&nbsp;(HH-MM-SS) or 08:34:39&nbsp;(HH:MM:SS).</li> <li><b>#SYSLBL</b> - extract the system label.</li>
<li><b>#TG</b> - extract the talkgroup id like 54241.</li> <li><b>#TAG</b> - extract the tag label.</li>
<li><b>#TG</b> - extract the talkgroup id like 1457 in decimal format.</li>
<li><b>#TGAFS</b> - extract the talkgroup id like 11-061 in AFS (agency-fleet-subfleet) format.</li>
<li><b>#TGHZ</b> - extract the frequency in hertz like 119100000 and set the talkgroup id to 119100.</li> <li><b>#TGHZ</b> - extract the frequency in hertz like 119100000 and set the talkgroup id to 119100.</li>
<li><b>#TGKHZ</b> - extract the frequency in kilohertz like 119100.000 or 119100 and set the talkgroup id to 119100.</li> <li><b>#TGKHZ</b> - extract the frequency in kilohertz like 119100.000 or 119100 and set the talkgroup id to 119100.</li>
<li><b>#TGMHZ</b> - extract the frequency in megahertz like 119.100000 or 119 and set the talkgroup id to 119100.</li> <li><b>#TGMHZ</b> - extract the frequency in megahertz like 119.100000 or 119 and set the talkgroup id to 119100.</li>
<li><b>#TIME</b> - extract the local time like 0853439&nbsp;(HHMMSS), 08-34-39&nbsp;(HH-MM-SS) or 08:34:39&nbsp;(HH:MM:SS).</li>
<li><b>#UNIT</b> - extract the unit id like 4424001.</li> <li><b>#UNIT</b> - extract the unit id like 4424001.</li>
<li><b>#ZTIME</b> - extract the zulu time like 0453439, 08-34-39 or 04:34:39.</li> <li><b>#ZTIME</b> - extract the zulu time like 0453439&nbsp;(HHMMSS), 08-34-39&nbsp;(HH-MM-SS) or 04:34:39&nbsp;(HH:MM:SS).</li>
</ul> </ul>
Example: cymx_#TG_#DATE_#TIME_#HZ Example: cymx_#TG_#DATE_#TIME_#HZ
</span> </span>

View file

@ -3,7 +3,7 @@
<button type="button" mat-button color="accent" (click)="add()">New downstream</button> <button type="button" mat-button color="accent" (click)="add()">New downstream</button>
</div> </div>
<p *ngIf="!downstreams.length" class="mat-small text-center">No defined downstreams</p> <p *ngIf="!downstreams.length" class="mat-small text-center">No defined downstreams</p>
<mat-accordion displayMode="flat" cdkDropList [cdkDropListData]="downstreams" (cdkDropListDropped)="drop($event)"> <mat-accordion displayMode="flat" cdkDropList [cdkDropListAutoScrollStep]=64 [cdkDropListData]="downstreams" (cdkDropListDropped)="drop($event)">
<mat-expansion-panel *ngFor="let downstream of downstreams; index as i" cdkDrag> <mat-expansion-panel *ngFor="let downstream of downstreams; index as i" cdkDrag>
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title> <mat-panel-title>

View file

@ -106,7 +106,8 @@
<div class="row"> <div class="row">
<p> <p>
<span class="mat-body">Search Patched Talkgroups</span><br> <span class="mat-body">Search Patched Talkgroups</span><br>
<span class="mat-caption">Search for patched talkgroups when using the search panel. Be aware this has a negative impact on search response time.</span> <span class="mat-caption">Search for patched talkgroups when using the search panel. Be aware this has a
negative impact on search response time.</span>
</p> </p>
<div> <div>
<mat-slide-toggle color="primary" formControlName="searchPatchedTalkgroups"></mat-slide-toggle> <mat-slide-toggle color="primary" formControlName="searchPatchedTalkgroups"></mat-slide-toggle>
@ -139,4 +140,16 @@
<mat-slide-toggle color="primary" formControlName="tagsToggle"></mat-slide-toggle> <mat-slide-toggle color="primary" formControlName="tagsToggle"></mat-slide-toggle>
</div> </div>
</div> </div>
<div class="row">
<p>
<span class="mat-body">AFS Systems</span><br>
<span class="mat-caption">A comma separated list of system Ids where talkgroup Ids should be displayed in AFS (agency-fleet-subfleet) format.</span>
</p>
<mat-form-field floatLabel="never">
<textarea type="text" matInput formControlName="afsSystems" placeholder="AFS systems"></textarea>
<mat-error *ngIf="form?.get('afsSystems')?.hasError('invalid')">
Comma separated list of system Ids
</mat-error>
</mat-form-field>
</div>
</ng-container> </ng-container>

View file

@ -79,7 +79,7 @@
option is enabled.</p> option is enabled.</p>
<button type="button" mat-button color="accent" (click)="addTalkgroup()">New talkgroup</button> <button type="button" mat-button color="accent" (click)="addTalkgroup()">New talkgroup</button>
</div> </div>
<mat-accordion displayMode="flat" cdkDropList [cdkDropListData]="talkgroups" <mat-accordion displayMode="flat" cdkDropList [cdkDropListAutoScrollStep]=64 [cdkDropListData]="talkgroups"
(cdkDropListDropped)="drop($event)"> (cdkDropListDropped)="drop($event)">
<mat-expansion-panel *ngFor="let talkgroup of talkgroups; index as i" [formGroup]="talkgroup" <mat-expansion-panel *ngFor="let talkgroup of talkgroups; index as i" [formGroup]="talkgroup"
cdkDrag> cdkDrag>
@ -114,7 +114,7 @@
screen.<br>You can drag and drop them to rearrange</p> screen.<br>You can drag and drop them to rearrange</p>
<button type="button" mat-button color="accent" (click)="addUnit()">New unit</button> <button type="button" mat-button color="accent" (click)="addUnit()">New unit</button>
</div> </div>
<mat-accordion displayMode="flat" cdkDropList [cdkDropListData]="units" <mat-accordion displayMode="flat" cdkDropList [cdkDropListAutoScrollStep]=64 [cdkDropListData]="units"
(cdkDropListDropped)="drop($event)"> (cdkDropListDropped)="drop($event)">
<mat-expansion-panel *ngFor="let unit of units; index as i" [formGroup]="unit" cdkDrag> <mat-expansion-panel *ngFor="let unit of units; index as i" [formGroup]="unit" cdkDrag>
<mat-expansion-panel-header> <mat-expansion-panel-header>

View file

@ -4,7 +4,7 @@
systems to rearrange their order.</p> systems to rearrange their order.</p>
<button type="button" mat-button color="accent" (click)="add()">New system</button> <button type="button" mat-button color="accent" (click)="add()">New system</button>
</div> </div>
<mat-accordion displayMode="flat" cdkDropList [cdkDropListData]="systems" (cdkDropListDropped)="drop($event)"> <mat-accordion displayMode="flat" cdkDropList [cdkDropListAutoScrollStep]=64 [cdkDropListData]="systems" (cdkDropListDropped)="drop($event)">
<mat-expansion-panel *ngFor="let system of systems; index as i" [formGroup]="system" <mat-expansion-panel *ngFor="let system of systems; index as i" [formGroup]="system"
(afterCollapse)="systemComponent.closeAll()" cdkDrag> (afterCollapse)="systemComponent.closeAll()" cdkDrag>
<mat-expansion-panel-header> <mat-expansion-panel-header>

View file

@ -425,6 +425,10 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
this.updateDisplay(); this.updateDisplay();
} }
private formatAfs(n: number): string {
return `${(n >> 7 & 15).toString().padStart(2, '0')}-${(n >> 3 & 15).toString().padStart(2, '0')}${n & 7}`;
}
private formatFrequency(frequency: number | undefined): string { private formatFrequency(frequency: number | undefined): string {
return typeof frequency === 'number' ? frequency return typeof frequency === 'number' ? frequency
.toString() .toString()
@ -433,6 +437,14 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
.concat(' Hz') : ''; .concat(' Hz') : '';
} }
private isAfsSystem(talkgroupId: number): boolean {
if (typeof this.config?.afs !== 'string') {
return false;
}
return this.config.afs.split(',').includes(talkgroupId.toString());
}
private syncClock(): void { private syncClock(): void {
this.clockTimer?.unsubscribe(); this.clockTimer?.unsubscribe();
@ -461,6 +473,8 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
private updateDisplay(time = this.callTime): void { private updateDisplay(time = this.callTime): void {
if (this.call) { if (this.call) {
let isAfs = this.isAfsSystem(this.call.system);
this.callProgress = new Date(this.call.dateTime); this.callProgress = new Date(this.call.dateTime);
this.callProgress.setSeconds(this.callProgress.getSeconds() + time); this.callProgress.setSeconds(this.callProgress.getSeconds() + time);
@ -468,7 +482,7 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
this.callTag = this.call.talkgroupData?.tag || ''; this.callTag = this.call.talkgroupData?.tag || '';
this.callTalkgroup = this.call.talkgroupData?.label || `${this.call.talkgroup}`; this.callTalkgroup = this.call.talkgroupData?.label || `${isAfs ? this.formatAfs(this.call.talkgroup) : this.call.talkgroup}`;
this.callTalkgroupName = this.call.talkgroupData?.name || this.formatFrequency(this.call?.frequency); this.callTalkgroupName = this.call.talkgroupData?.name || this.formatFrequency(this.call?.frequency);
@ -494,7 +508,7 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
if (Array.isArray(this.call.sources) && this.call.sources.length) { if (Array.isArray(this.call.sources) && this.call.sources.length) {
const source = this.call.sources.reduce((p, v) => (v.pos || 0) <= time ? v : p, {}); const source = this.call.sources.reduce((p, v) => (v.pos || 0) <= time ? v : p, {});
this.callTalkgroupId = `${this.call.talkgroup}`; this.callTalkgroupId = isAfs ? this.formatAfs(this.call.talkgroup) : this.call.talkgroup.toString();
if (typeof source.src === 'number' && Array.isArray(this.call.systemData?.units)) { if (typeof source.src === 'number' && Array.isArray(this.call.systemData?.units)) {
const callUnit = this.call?.systemData?.units?.find((u) => u.id === source.src); const callUnit = this.call?.systemData?.units?.find((u) => u.id === source.src);
@ -506,7 +520,7 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
} }
} else { } else {
this.callTalkgroupId = this.call.talkgroup.toString(); this.callTalkgroupId = isAfs ? this.formatAfs(this.call.talkgroup) : this.call.talkgroup.toString();
this.callUnit = typeof this.call.source === 'number' ? `${this.call.source}` : ''; this.callUnit = typeof this.call.source === 'number' ? `${this.call.source}` : '';
} }

View file

@ -96,6 +96,7 @@ export class RdioScannerService implements OnDestroy {
private playbackList: RdioScannerPlaybackList | undefined; private playbackList: RdioScannerPlaybackList | undefined;
private playbackPending: number | undefined; private playbackPending: number | undefined;
private playbackRefreshing = false;
private skipDelay: Subscription | undefined; private skipDelay: Subscription | undefined;
@ -580,6 +581,8 @@ export class RdioScannerService implements OnDestroy {
stopPlaybackMode(): void { stopPlaybackMode(): void {
this.livefeedMode = RdioScannerLivefeedMode.Offline; this.livefeedMode = RdioScannerLivefeedMode.Offline;
this.playbackRefreshing = false;
this.clearQueue(); this.clearQueue();
this.event.emit({ livefeedMode: this.livefeedMode, queue: 0 }); this.event.emit({ livefeedMode: this.livefeedMode, queue: 0 });
@ -833,6 +836,10 @@ export class RdioScannerService implements OnDestroy {
tagsToggle: typeof config.tagsToggle === 'boolean' ? config.tagsToggle : false, tagsToggle: typeof config.tagsToggle === 'boolean' ? config.tagsToggle : false,
}; };
if (typeof config.afs === 'string' && config.afs.length) {
this.config['afs'] = config.afs;
}
this.rebuildLivefeedMap(); this.rebuildLivefeedMap();
if (this.livefeedMode === RdioScannerLivefeedMode.Online) { if (this.livefeedMode === RdioScannerLivefeedMode.Online) {
@ -902,7 +909,12 @@ export class RdioScannerService implements OnDestroy {
} else if (index === 0) { } else if (index === 0) {
if (this.playbackList.options.offset < this.playbackList.options.limit) { if (this.playbackList.options.offset < this.playbackList.options.limit) {
this.stopPlaybackMode(); if (this.playbackRefreshing) {
this.stopPlaybackMode();
} else {
this.playbackRefreshing = true;
this.searchCalls(this.playbackList.options);
}
} else { } else {
this.searchCalls(Object.assign({}, this.playbackList.options, { this.searchCalls(Object.assign({}, this.playbackList.options, {
@ -924,8 +936,12 @@ export class RdioScannerService implements OnDestroy {
offset: this.playbackList.options.offset + this.playbackList.options.limit, offset: this.playbackList.options.offset + this.playbackList.options.limit,
})); }));
} else { } else if (this.playbackRefreshing) {
this.stopPlaybackMode(); this.stopPlaybackMode();
} else {
this.playbackRefreshing = true;
this.searchCalls(this.playbackList.options);
} }
} else { } else {

View file

@ -89,6 +89,7 @@ export enum RdioScannerCategoryType {
} }
export interface RdioScannerConfig { export interface RdioScannerConfig {
afs?: string;
dimmerDelay: number | false; dimmerDelay: number | false;
groups: { [key: string]: { [key: number]: number[] } }; groups: { [key: string]: { [key: number]: number[] } };
keypadBeeps: RdioScannerKeypadBeeps | false; keypadBeeps: RdioScannerKeypadBeeps | false;

View file

@ -1,33 +1,34 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{ {
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "es2017",
"module": "es2020",
"lib": [
"es2020",
"dom"
],
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
},
"angularCompilerOptions": { "angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false, "enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true, "strictInjectionParameters": true,
"strictInputAccessModifiers": true, "strictInputAccessModifiers": true,
"strictTemplates": true "strictTemplates": true
}, }
"compileOnSave": false,
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"baseUrl": "./",
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"importHelpers": true,
"lib": [
"es2020",
"dom"
],
"module": "es2020",
"moduleResolution": "node",
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"outDir": "./dist/out-tsc",
"resolveJsonModule": true,
"sourceMap": true,
"strict": true,
"target": "es2017"
},
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -57,12 +57,12 @@ func NewAdmin(controller *Controller) *Admin {
Attempts: AdminLoginAttempts{}, Attempts: AdminLoginAttempts{},
AttemptsMax: uint(3), AttemptsMax: uint(3),
AttemptsMaxDelay: time.Duration(time.Duration.Minutes(10)), AttemptsMaxDelay: time.Duration(time.Duration.Minutes(10)),
Broadcast: make(chan *[]byte, 100), Broadcast: make(chan *[]byte),
Conns: make(map[*websocket.Conn]bool), Conns: make(map[*websocket.Conn]bool),
Controller: controller, Controller: controller,
Register: make(chan *websocket.Conn, 100), Register: make(chan *websocket.Conn),
Tokens: []string{}, Tokens: []string{},
Unregister: make(chan *websocket.Conn, 100), Unregister: make(chan *websocket.Conn),
mutex: sync.Mutex{}, mutex: sync.Mutex{},
} }
} }

View file

@ -57,7 +57,7 @@ func (client *Client) Init(controller *Controller, conn *websocket.Conn) error {
client.Access = &Access{} client.Access = &Access{}
client.Controller = controller client.Controller = controller
client.Conn = conn client.Conn = conn
client.Send = make(chan *Message, 8) client.Send = make(chan *Message)
client.Livefeed = NewLivefeed() client.Livefeed = NewLivefeed()
controller.Register <- client controller.Register <- client
@ -153,18 +153,21 @@ func (client *Client) SendConfig(groups *Groups, options *Options, systems *Syst
client.GroupsMap = groups.GetGroupsMap(&client.SystemsMap) client.GroupsMap = groups.GetGroupsMap(&client.SystemsMap)
client.TagsMap = tags.GetTagsMap(&client.SystemsMap) client.TagsMap = tags.GetTagsMap(&client.SystemsMap)
client.Send <- &Message{ var payload = map[string]interface{}{
Command: MessageCommandConfig, "dimmerDelay": options.DimmerDelay,
Payload: map[string]interface{}{ "groups": client.GroupsMap,
"dimmerDelay": options.DimmerDelay, "keypadBeeps": GetKeypadBeeps(options),
"groups": client.GroupsMap, "showListenersCount": options.ShowListenersCount,
"keypadBeeps": GetKeypadBeeps(options), "systems": client.SystemsMap,
"showListenersCount": options.ShowListenersCount, "tags": client.TagsMap,
"systems": client.SystemsMap, "tagsToggle": options.TagsToggle,
"tags": client.TagsMap,
"tagsToggle": options.TagsToggle,
},
} }
if len(options.AfsSystems) > 0 {
payload["afs"] = options.AfsSystems
}
client.Send <- &Message{Command: MessageCommandConfig, Payload: payload}
} }
func (client *Client) SendListenersCount(count int) { func (client *Client) SendListenersCount(count int) {

View file

@ -71,9 +71,9 @@ func NewController(config *Config) *Controller {
Systems: NewSystems(), Systems: NewSystems(),
Tags: NewTags(), Tags: NewTags(),
Clients: NewClients(), Clients: NewClients(),
Register: make(chan *Client, 128), Register: make(chan *Client),
Unregister: make(chan *Client, 128), Unregister: make(chan *Client),
Ingest: make(chan *Call, 128), Ingest: make(chan *Call),
ingestMutex: sync.Mutex{}, ingestMutex: sync.Mutex{},
} }
@ -601,7 +601,7 @@ func (controller *Controller) Start() error {
} }
go func() { go func() {
c := make(chan os.Signal, 1) c := make(chan os.Signal)
signal.Notify(c, os.Interrupt) signal.Notify(c, os.Interrupt)
<-c <-c
controller.Terminate() controller.Terminate()

View file

@ -46,7 +46,7 @@ func NewDaemon() *Daemon {
p, _ := os.FindProcess(os.Getpid()) p, _ := os.FindProcess(os.Getpid())
d := Daemon{ d := Daemon{
Errors: make(chan error, 5), Errors: make(chan error),
Interface: &DaemonInterface{Process: p}, Interface: &DaemonInterface{Process: p},
} }

View file

@ -61,7 +61,7 @@ func NewDatabase(config *Config) *Database {
log.Fatalf("unknown database type %s\n", config.DbType) log.Fatalf("unknown database type %s\n", config.DbType)
} }
database.Sql.SetConnMaxLifetime(0) database.Sql.SetConnMaxLifetime(time.Minute)
database.Sql.SetMaxIdleConns(25) database.Sql.SetMaxIdleConns(25)
database.Sql.SetMaxOpenConns(25) database.Sql.SetMaxOpenConns(25)

View file

@ -60,7 +60,6 @@ type Dirwatch struct {
controller *Controller controller *Controller
dirs map[string]bool dirs map[string]bool
pending map[string]*time.Timer pending map[string]*time.Timer
running bool
watcher *fsnotify.Watcher watcher *fsnotify.Watcher
} }
@ -327,15 +326,19 @@ func (dirwatch *Dirwatch) ingestTrunkRecorder(p string) error {
func (dirwatch *Dirwatch) parseMask(call *Call) { func (dirwatch *Dirwatch) parseMask(call *Call) {
var meta = [][]string{ var meta = [][]string{
{"date", "#DATE", `[\d-_]+`}, {"date", "#DATE", `[\d-_]+`},
{"hz", "#HZ", `[\d]+`}, {"group", "#GROUP", `[a-zA-Z0-9\ \.-]+`},
{"hz", "#HZ", `\d+`},
{"khz", "#KHZ", `[\d\.]+`}, {"khz", "#KHZ", `[\d\.]+`},
{"mhz", "#MHZ", `[\d\.]+`}, {"mhz", "#MHZ", `[\d\.]+`},
{"system", "#SYS", `\d+`}, {"syslbl", "#SYSLBL", `[a-zA-Z0-9\ \.-]+`},
{"time", "#TIME", `[\d-:]+`}, {"sys", "#SYS", `\d+`},
{"tag", "#TAG", `[a-zA-Z0-9\ \.-]+`},
{"tgafs", "#TGAFS", `\d{2}-\d{3}`},
{"tghz", "#TGHZ", `\d+`}, {"tghz", "#TGHZ", `\d+`},
{"tgkhz", "#TGKHZ", `[\d\.]+`}, {"tgkhz", "#TGKHZ", `[\d\.]+`},
{"tgmhz", "#TGMHZ", `[\d\.]+`}, {"tgmhz", "#TGMHZ", `[\d\.]+`},
{"talkgroup", "#TG", `\d+`}, {"tg", "#TG", `\d+`},
{"time", "#TIME", `[\d-:]+`},
{"unit", "#UNIT", `\d+`}, {"unit", "#UNIT", `\d+`},
{"ztime", "#ZTIME", `[\d-:]+`}, {"ztime", "#ZTIME", `[\d-:]+`},
} }
@ -405,6 +408,13 @@ func (dirwatch *Dirwatch) parseMask(call *Call) {
} }
} }
switch v := metaval["group"].(type) {
case string:
if len(v) > 0 && v != "-" {
call.talkgroupGroup = v
}
}
switch v := metaval["hz"].(type) { switch v := metaval["hz"].(type) {
case string: case string:
if hz, err := strconv.ParseFloat(v, 64); err == nil { if hz, err := strconv.ParseFloat(v, 64); err == nil {
@ -426,39 +436,69 @@ func (dirwatch *Dirwatch) parseMask(call *Call) {
} }
} }
switch v := metaval["system"].(type) { switch v := metaval["sys"].(type) {
case string: case string:
if i, err := strconv.Atoi(v); err == nil { if i, err := strconv.Atoi(v); err == nil {
call.System = uint(i) call.System = uint(i)
} }
default:
switch v := metaval["syslbl"].(type) {
case string:
if system, ok := dirwatch.controller.Systems.GetSystem(v); ok {
call.System = system.Id
} else {
call.System = dirwatch.controller.Systems.GetNewSystemId()
call.systemLabel = v
}
}
} }
switch v := metaval["talkgroup"].(type) { switch v := metaval["tag"].(type) {
case string:
if len(v) > 0 && v != "-" {
call.talkgroupGroup = v
}
}
switch v := metaval["tg"].(type) {
case string: case string:
if i, err := strconv.Atoi(v); err == nil { if i, err := strconv.Atoi(v); err == nil {
call.Talkgroup = uint(i) call.Talkgroup = uint(i)
} }
}
switch v := metaval["tghz"].(type) {
case string:
if hz, err := strconv.ParseFloat(v, 64); err == nil {
call.Frequency = uint(hz)
call.Talkgroup = uint(hz / 1e3)
}
default: default:
switch v := metaval["tgkhz"].(type) { switch v := metaval["tgafs"].(type) {
case string: case string:
if khz, err := strconv.ParseFloat(v, 64); err == nil { if len(v) == 6 && v[2] == '-' {
call.Frequency = uint(khz * 1e3) if a, err := strconv.Atoi(v[:2]); err == nil {
call.Talkgroup = uint(khz) if b, err := strconv.Atoi(v[3:5]); err == nil {
if c, err := strconv.Atoi(v[5:]); err == nil {
call.Talkgroup = uint(a<<7 | b<<3 | c)
}
}
}
} }
default: default:
switch v := metaval["tgmhz"].(type) { switch v := metaval["tghz"].(type) {
case string: case string:
if mhz, err := strconv.ParseFloat(v, 64); err == nil { if hz, err := strconv.ParseFloat(v, 64); err == nil {
call.Frequency = uint(mhz * 1e6) call.Frequency = uint(hz)
call.Talkgroup = uint(mhz * 1e3) call.Talkgroup = uint(hz / 1e3)
}
default:
switch v := metaval["tgkhz"].(type) {
case string:
if khz, err := strconv.ParseFloat(v, 64); err == nil {
call.Frequency = uint(khz * 1e3)
call.Talkgroup = uint(khz)
}
default:
switch v := metaval["tgmhz"].(type) {
case string:
if mhz, err := strconv.ParseFloat(v, 64); err == nil {
call.Frequency = uint(mhz * 1e6)
call.Talkgroup = uint(mhz * 1e3)
}
}
} }
} }
} }
@ -496,8 +536,6 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
return err return err
} }
dirwatch.running = true
switch v := dirwatch.Delay.(type) { switch v := dirwatch.Delay.(type) {
case uint: case uint:
delay = time.Duration(math.Max(float64(v), 2000)) * time.Millisecond delay = time.Duration(math.Max(float64(v), 2000)) * time.Millisecond
@ -505,93 +543,74 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
delay = time.Duration(2000) * time.Millisecond delay = time.Duration(2000) * time.Millisecond
} }
watcher := func() { go func() {
logError := func(err error) { logError := func(err error) {
controller.Logs.LogEvent(controller.Database, LogLevelError, fmt.Sprintf("dirwatch.watcher: %v", err.Error())) controller.Logs.LogEvent(controller.Database, 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() {
if dirwatch.running { delete(dirwatch.pending, eventName)
if _, err := os.Stat(eventName); err == nil {
dirwatch.Ingest(eventName) if _, err := os.Stat(eventName); err == nil {
} dirwatch.Ingest(eventName)
} }
}) })
} }
pending := map[string]*time.Timer{}
for { for {
if dirwatch.watcher == nil { if dirwatch.watcher == nil {
break break
} }
if event, ok := <-dirwatch.watcher.Events; ok { select {
switch event.Op { case event, ok := <-dirwatch.watcher.Events:
case fsnotify.Create: if ok {
if dirwatch.isDir(event.Name) { switch event.Op {
if err := dirwatch.walkDir(event.Name); err != nil { case fsnotify.Create:
logError(err) if dirwatch.isDir(event.Name) {
} if err := dirwatch.walkDir(event.Name); err != nil {
logError(err)
}
} else {
if pending[event.Name] != nil {
pending[event.Name].Stop()
}
pending[event.Name] = newTimer(event.Name)
}
case fsnotify.Remove:
if dirwatch.dirs[event.Name] {
if err := dirwatch.watcher.Remove(event.Name); err == nil {
delete(dirwatch.dirs, event.Name)
} else { } else {
logError(err) if dirwatch.pending[event.Name] != nil {
dirwatch.pending[event.Name].Stop()
}
dirwatch.pending[event.Name] = newTimer(event.Name)
}
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)
}
}
case fsnotify.Write:
if dirwatch.pending[event.Name] != nil {
if dirwatch.pending[event.Name] != nil {
dirwatch.pending[event.Name].Stop()
}
dirwatch.pending[event.Name] = newTimer(event.Name)
} }
} }
case fsnotify.Write:
if pending[event.Name] != nil {
if pending[event.Name] != nil {
pending[event.Name].Stop()
}
pending[event.Name] = newTimer(event.Name)
}
} }
} else if dirwatch.running { case err, ok := <-dirwatch.watcher.Errors:
if dirwatch.watcher != nil { if ok {
dirwatch.watcher.Close() logError(err)
} }
time.Sleep(2 * time.Second)
if dirwatch.watcher, err = fsnotify.NewWatcher(); err != nil {
controller.Logs.LogEvent(
controller.Database,
LogLevelError,
fmt.Sprintf("dirwatch.watcher.restart: %s", err.Error()),
)
}
} else {
dirwatch.Stop()
break
} }
} }
} }()
go watcher()
go func() { go func() {
time.Sleep(delay) time.Sleep(delay)
if err := fs.WalkDir(os.DirFS(dirwatch.Directory), ".", func(p string, d fs.DirEntry, err error) error { if err := fs.WalkDir(os.DirFS(dirwatch.Directory), ".", func(p string, d fs.DirEntry, err error) error {
if !dirwatch.running {
return nil
}
fp := filepath.Join(dirwatch.Directory, p) fp := filepath.Join(dirwatch.Directory, p)
if dirwatch.isDir(fp) { if dirwatch.isDir(fp) {
@ -616,16 +635,15 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
} }
func (dirwatch *Dirwatch) Stop() { func (dirwatch *Dirwatch) Stop() {
dirwatch.running = false
for k := range dirwatch.pending {
dirwatch.pending[k].Stop()
}
if dirwatch.watcher != nil { if dirwatch.watcher != nil {
dirwatch.watcher.Close() dirwatch.watcher.Close()
dirwatch.watcher = nil dirwatch.watcher = nil
} }
for k := range dirwatch.pending {
dirwatch.pending[k].Stop()
delete(dirwatch.pending, k)
}
} }
type Dirwatches struct { type Dirwatches struct {

View file

@ -4,15 +4,15 @@ go 1.17
require ( require (
github.com/dhowden/tag v0.0.0-20201120070457-d52dcb253c63 github.com/dhowden/tag v0.0.0-20201120070457-d52dcb253c63
github.com/fsnotify/fsnotify v1.5.1 github.com/fsnotify/fsnotify v1.5.4
github.com/go-sql-driver/mysql v1.6.0 github.com/go-sql-driver/mysql v1.6.0
github.com/golang-jwt/jwt/v4 v4.4.1 github.com/golang-jwt/jwt/v4 v4.4.1
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/gorilla/websocket v1.5.0 github.com/gorilla/websocket v1.5.0
github.com/kardianos/service v1.2.1 github.com/kardianos/service v1.2.1
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898
gopkg.in/ini.v1 v1.66.4 gopkg.in/ini.v1 v1.66.4
modernc.org/sqlite v1.16.0 modernc.org/sqlite v1.17.2
) )
require ( require (
@ -21,18 +21,18 @@ require (
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/stretchr/testify v1.7.0 // indirect github.com/stretchr/testify v1.7.0 // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b // indirect golang.org/x/net v0.0.0-20220517181318-183a9ca12b87 // indirect
golang.org/x/sys v0.0.0-20220405210540-1e041c57c461 // indirect golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.10 // indirect golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
lukechampine.com/uint128 v1.2.0 // indirect lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.35.24 // indirect modernc.org/cc/v3 v3.36.0 // indirect
modernc.org/ccgo/v3 v3.15.18 // indirect modernc.org/ccgo/v3 v3.16.6 // indirect
modernc.org/libc v1.14.12 // indirect modernc.org/libc v1.16.8 // indirect
modernc.org/mathutil v1.4.1 // indirect modernc.org/mathutil v1.4.1 // indirect
modernc.org/memory v1.0.7 // indirect modernc.org/memory v1.1.1 // indirect
modernc.org/opt v0.1.3 // indirect modernc.org/opt v0.1.3 // indirect
modernc.org/strutil v1.1.1 // indirect modernc.org/strutil v1.1.2 // indirect
modernc.org/token v1.0.0 // indirect modernc.org/token v1.0.0 // indirect
) )

View file

@ -4,8 +4,8 @@ github.com/dhowden/tag v0.0.0-20201120070457-d52dcb253c63 h1:/u5RVRk3Nh7Zw1QQnPt
github.com/dhowden/tag v0.0.0-20201120070457-d52dcb253c63/go.mod h1:SniNVYuaD1jmdEEvi+7ywb1QFR7agjeTdGKyFb0p7Rw= github.com/dhowden/tag v0.0.0-20201120070457-d52dcb253c63/go.mod h1:SniNVYuaD1jmdEEvi+7ywb1QFR7agjeTdGKyFb0p7Rw=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ= github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ=
@ -38,8 +38,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o= golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 h1:SLP7Q4Di66FONjDJbCYrCRrh97focO6sLogHO7/g8F0=
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
@ -49,8 +49,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b h1:vI32FkLJNAWtGD4BwkThwEy6XS7ZLLMHkSkYfF8M0W0= golang.org/x/net v0.0.0-20220517181318-183a9ca12b87 h1:cCR+9mKLOGyX4Zx+uBZDXEDAQsvKQ/XbW4vreG5v1jU=
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220517181318-183a9ca12b87/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -60,16 +60,15 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220405210540-1e041c57c461 h1:kHVeDEnfKn3T238CvrUcz6KeEsFHVaKh4kMTt6Wsysg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220405210540-1e041c57c461/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e h1:w36l2Uw3dRan1K3TyXriXvY+6T56GNmlKGcqiQUJDfM=
golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -85,8 +84,9 @@ golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@ -95,144 +95,39 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= modernc.org/cc/v3 v3.36.0 h1:0kmRkTmqNidmu3c7BNDSdVHCxXCkWLmWmCIVX4LUboo=
modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=
modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=
modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= modernc.org/ccgo/v3 v3.16.6 h1:3l18poV+iUemQ98O3X5OMr97LOqlzis+ytivU4NqGhA=
modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.24 h1:vlCqjhVwX15t1uwlMPpOpNRC7JTjMZ9lT9DYHKQTFuA=
modernc.org/cc/v3 v3.35.24/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag=
modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw=
modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ=
modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c=
modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo=
modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg=
modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I=
modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs=
modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8=
modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE=
modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk=
modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w=
modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE=
modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8=
modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc=
modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU=
modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE=
modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk=
modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI=
modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE=
modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg=
modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74=
modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU=
modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU=
modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc=
modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM=
modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ=
modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84=
modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ=
modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY=
modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU=
modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko=
modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA=
modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4=
modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0=
modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8=
modernc.org/ccgo/v3 v3.15.12/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I=
modernc.org/ccgo/v3 v3.15.14/go.mod h1:144Sz2iBCKogb9OKwsu7hQEub3EVgOlyI8wMUPGKUXQ=
modernc.org/ccgo/v3 v3.15.15/go.mod h1:z5qltXjU4PJl0pE5nhYQCvA9DhPHiWsl5GWl89+NSYE=
modernc.org/ccgo/v3 v3.15.16/go.mod h1:XbKRMeMWMdq712Tr5ECgATYMrzJ+g9zAZEj2ktzBe24=
modernc.org/ccgo/v3 v3.15.17/go.mod h1:bofnFkpRFf5gLY+mBZIyTW6FEcp26xi2lgOFk2Rlvs0=
modernc.org/ccgo/v3 v3.15.18 h1:X5ym656Ye7/ubL+wox0SeF9aRX5od1UDFn1tAbQR+90=
modernc.org/ccgo/v3 v3.15.18/go.mod h1:/2lv3WjHyanEr2sAPdGKRC38n6f0werut9BRXUjjX+A=
modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=
modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg= modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=
modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M= modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU= modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o=
modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE= modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso=
modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8=
modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8=
modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I=
modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk=
modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY=
modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE=
modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg=
modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM=
modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg=
modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo=
modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8=
modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ=
modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA=
modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM=
modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg=
modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE=
modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM=
modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU=
modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw=
modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M=
modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18=
modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8=
modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0=
modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE=
modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ=
modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34=
modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ=
modernc.org/libc v1.14.6/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak=
modernc.org/libc v1.14.7/go.mod h1:f8xfWXW8LW41qb4X5+huVQo5dcfPlq7Cbny2TDheMv0=
modernc.org/libc v1.14.8/go.mod h1:9+JCLb1MWSY23smyOpIPbd5ED+rSS/ieiDWUpdyO3mo=
modernc.org/libc v1.14.10/go.mod h1:y1MtIWhwpJFpLYm6grAThtuXJKEsY6xkdZmXbRngIdo=
modernc.org/libc v1.14.11/go.mod h1:l5/Mz/GrZwOqzwRHA3abgSCnSeJzzTl+Ify0bAwKbAw=
modernc.org/libc v1.14.12 h1:pUBZTYoISfbb4pCf4PECENpbvwDBxeKc+/dS9LyOWFM=
modernc.org/libc v1.14.12/go.mod h1:fJdoe23MHu2ruPQkFPPqCpToDi5cckzsbmkI6Ez0LqQ=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU=
modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM= modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/memory v1.0.6/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/memory v1.0.7 h1:UE3cxTRFa5tfUibAV7Jqq8P7zRY0OlJg+yWVIIaluEE=
modernc.org/memory v1.0.7/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.16.0 h1:DdvOGaWN0y+X7t2L7RUD63gcwbVjYZjcBZnA68g44EI= modernc.org/sqlite v1.17.2 h1:TjmF36Wi5QcPYqRoAacV1cAyJ7xB/CD0ExpVUEMebnw=
modernc.org/sqlite v1.16.0/go.mod h1:Jwe13ItpESZ+78K5WS6+AjXsUg+JvirsjN3iIDO4C8k= modernc.org/sqlite v1.17.2/go.mod h1:GOQmuiXd6pTTes1Fi2s9apiCcD/wbKQtBZ0Nw6/etjM=
modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/tcl v1.11.2 h1:mXpsx3AZqJt83uDiFu9UYQVBjNjaWKGCF1YDSlpCL6Y= modernc.org/strutil v1.1.2 h1:iFBDH6j1Z0bN/Q9udJnnFoFpENA4252qe/7/5woE5MI=
modernc.org/tcl v1.11.2/go.mod h1:BRzgpajcGdS2qTxniOx9c/dcxjlbA7p12eJNmiriQYo= modernc.org/strutil v1.1.2/go.mod h1:OYajnUAcI/MX+XD/Wx7v1bbdvcQSvxgtb0gC+u3d3eg=
modernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao=
modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.3.2 h1:4GWBVMa48UDC7KQ9tnaggN/yTlXg+CdCX9bhgHPQ9AM= modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM=
modernc.org/z v1.3.2/go.mod h1:PEU2oK2OEA1CfzDTd+8E908qEXhC9s0MfyKp5LZsd+k= modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=

View file

@ -25,6 +25,7 @@ import (
) )
type Options struct { type Options struct {
AfsSystems string `json:"afsSystems"`
AutoPopulate bool `json:"autoPopulate"` AutoPopulate bool `json:"autoPopulate"`
DimmerDelay uint `json:"dimmerDelay"` DimmerDelay uint `json:"dimmerDelay"`
DisableAudioConversion bool `json:"disableAudioConversion"` DisableAudioConversion bool `json:"disableAudioConversion"`
@ -88,6 +89,11 @@ func (options *Options) FromMap(m map[string]interface{}) {
options.DuplicateDetectionTimeFrame = defaults.options.duplicateDetectionTimeFrame options.DuplicateDetectionTimeFrame = defaults.options.duplicateDetectionTimeFrame
} }
switch v := m["afsSystems"].(type) {
case string:
options.AfsSystems = v
}
switch v := m["keypadBeeps"].(type) { switch v := m["keypadBeeps"].(type) {
case string: case string:
options.KeypadBeeps = v options.KeypadBeeps = v
@ -204,6 +210,11 @@ func (options *Options) Read(db *Database) error {
if err = json.Unmarshal([]byte(v), &f); err == nil { if err = json.Unmarshal([]byte(v), &f); err == nil {
switch v := f.(type) { switch v := f.(type) {
case map[string]interface{}: case map[string]interface{}:
switch v := v["afsSystems"].(type) {
case string:
options.AfsSystems = v
}
switch v := v["autoPopulate"].(type) { switch v := v["autoPopulate"].(type) {
case bool: case bool:
options.AutoPopulate = v options.AutoPopulate = v
@ -322,6 +333,7 @@ func (options *Options) Write(db *Database) error {
} }
if b, err = json.Marshal(map[string]interface{}{ if b, err = json.Marshal(map[string]interface{}{
"afsSystems": options.AfsSystems,
"autoPopulate": options.AutoPopulate, "autoPopulate": options.AutoPopulate,
"dimmerDelay": options.DimmerDelay, "dimmerDelay": options.DimmerDelay,
"disableAudioConversion": options.DisableAudioConversion, "disableAudioConversion": options.DisableAudioConversion,

View file

@ -15,4 +15,4 @@
package main package main
const Version = "6.2.5" const Version = "6.3.0"