Version 6.0.4

This commit is contained in:
Chrystian Huot 2021-12-08 11:08:39 -05:00
parent 04fda4558e
commit b76567ec19
31 changed files with 983 additions and 960 deletions

View file

@ -37,6 +37,13 @@ _v6.0.3_
- Fix strconv.Atoi: invalid syntax for dirwatch type sdrtrunk. - Fix strconv.Atoi: invalid syntax for dirwatch type sdrtrunk.
- Fix the new version available dialog opening more than once. - Fix the new version available dialog opening more than once.
_v6.0.4_
- Fix wrong time calculation in prune scheduler.
- More fix on the SQL_BUSY error (issue #67).
- Support files (certs, db, ini) are now created in the same folder as the executable, if the folder is writable, or under a `Rdio Scanner` folder in the user's home folder.
- Some code refactoring.
## Version 5.2 ## Version 5.2
- Change to how the server reports version. - Change to how the server reports version.

35
COMPILING.md Normal file
View file

@ -0,0 +1,35 @@
# How to compile
The official version of [Rdio Scanner](https://github.com/chuot/rdio-scanner) is compiled on a PC using the current version of Fedora Workstation. You should have no problem building on another platform as long as the prerequisites are available and installed.
## Install the prerequisites
Your os distribution may have all of the following prerequisites available in its own package repository. Make sure, however, that they are at the latest version. This is especially not the case with Ubuntu and its Node.js.
- latest version of git ([here](https://git-scm.com/downloads))
- latest version of gnu make ([here](https://www.gnu.org/software/make/))
- 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 Pandoc with pandoc-pdf ([here](https://pandoc.org/installing.html))
- latest version of Info-Zip ([here](http://infozip.sourceforge.net/))
## Compile the app
Clone the official repository on your computer and start the build process.
git clone https://github.com/chuot/rdio-scanner.git
cd rdio-scanner
make
When finished, you will find the precompiled versions for various platforms in the `dist` folder.
rdio-scanner-darwin-amd64-v6.0.4.zip
rdio-scanner-darwin-arm64-v6.0.4.zip
rdio-scanner-freebsd-amd64-v6.0.4.zip
rdio-scanner-linux-386-v6.0.4.zip
rdio-scanner-linux-amd64-v6.0.4.zip
rdio-scanner-linux-arm64-v6.0.4.zip
rdio-scanner-linux-arm-v6.0.4.zip
rdio-scanner-windows-amd64-v6.0.4.zip
**Happy Rdio scanning !**

View file

@ -16,7 +16,8 @@
################################################################################ ################################################################################
app := rdio-scanner app := rdio-scanner
ver := 6.0.3 date := 2021/12/08
ver := 6.0.4
client := $(wildcard client/*.json client/*.ts) client := $(wildcard client/*.json client/*.ts)
server := $(wildcard server/*.go) server := $(wildcard server/*.go)
@ -25,7 +26,7 @@ build = @cd server && GOOS=$(1) GOARCH=$(2) go build -o ../dist/$(1)-$(2)/$(3)
pandoc = @test -d dist/$(1)-$(2) || mkdir -p dist/$(1)-$(2) && pandoc -f markdown -o dist/$(1)-$(2)/$(3) --resource-path docs:docs/platforms $(4) docs/webapp.md CHANGELOG.md pandoc = @test -d dist/$(1)-$(2) || mkdir -p dist/$(1)-$(2) && pandoc -f markdown -o dist/$(1)-$(2)/$(3) --resource-path docs:docs/platforms $(4) docs/webapp.md CHANGELOG.md
zip = @cd dist/$(1)-$(2) && zip -q ../$(app)-$(1)-$(2)-v$(ver).zip * && cd .. && rm -fr $(1)-$(2) zip = @cd dist/$(1)-$(2) && zip -q ../$(app)-$(1)-$(2)-v$(ver).zip * && cd .. && rm -fr $(1)-$(2)
.PHONY: all clean dist setver .PHONY: all clean dist sed
.PHONY: darwin darwin-amd64 darwin-arm64 .PHONY: darwin darwin-amd64 darwin-arm64
.PHONY: freebsd freebsd-amd64 .PHONY: freebsd freebsd-amd64
.PHONY: linux linux-386 linux-amd64 linux-arm linux-arm64 .PHONY: linux linux-386 linux-amd64 linux-arm linux-arm64
@ -38,10 +39,11 @@ clean:
dist: darwin freebsd linux windows dist: darwin freebsd linux windows
setver: sed:
@sed -i -re "s/^(\s*\"version\":).*$$/\1 \"$(ver)\"/" client/package.json @sed -i -re "s|^(\s*\"version\":).*$$|\1 \"$(ver)\"|" client/package.json
@sed -i -re "s/^(const\s+Version\s+=).*$$/\1 \"$(ver)\"/" server/version.go @sed -i -re "s|^(const\s+Version\s+=).*$$|\1 \"$(ver)\"|" server/version.go
@sed -i -re "s/v[0-9]\.[0-9]\.[0-9]/v$(ver)/" docs/platforms/*.md @sed -i -re "s|v[0-9]\.[0-9]\.[0-9]|v$(ver)|" COMPILING.md docs/platforms/*.md
@sed -i -re "s|[0-9]{4}/[0-9]{2}/[0-9]{2}|$(date)|" docs/platforms/*.md
webapp: server/webapp/index.html webapp: server/webapp/index.html

834
client/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,31 +1,31 @@
{ {
"author": "Chrystian Huot <chrystian.huot@saubeo.solutions>", "author": "Chrystian Huot <chrystian.huot@saubeo.solutions>",
"dependencies": { "dependencies": {
"@angular/animations": "^13.0.2", "@angular/animations": "^13.0.3",
"@angular/cdk": "^13.0.2", "@angular/cdk": "^13.0.3",
"@angular/common": "~13.0.2", "@angular/common": "~13.0.3",
"@angular/compiler": "~13.0.2", "@angular/compiler": "~13.0.3",
"@angular/core": "~13.0.2", "@angular/core": "~13.0.3",
"@angular/forms": "~13.0.2", "@angular/forms": "~13.0.3",
"@angular/material": "^13.0.2", "@angular/material": "^13.0.3",
"@angular/platform-browser": "~13.0.2", "@angular/platform-browser": "~13.0.3",
"@angular/platform-browser-dynamic": "~13.0.2", "@angular/platform-browser-dynamic": "~13.0.3",
"@angular/router": "^13.0.2", "@angular/router": "^13.0.3",
"@angular/service-worker": "^13.0.2", "@angular/service-worker": "^13.0.3",
"rxjs": "~7.4.0", "rxjs": "~7.4.0",
"tslib": "^2.3.1", "tslib": "^2.3.1",
"zone.js": "~0.11.4" "zone.js": "~0.11.4"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "~13.0.3", "@angular-devkit/build-angular": "~13.0.4",
"@angular/cli": "^13.0.3", "@angular/cli": "^13.0.4",
"@angular/compiler-cli": "~13.0.2", "@angular/compiler-cli": "~13.0.3",
"@types/node": "~16.11.11", "@types/node": "~16.11.12",
"@typescript-eslint/eslint-plugin": "^5.5.0", "@typescript-eslint/eslint-plugin": "^5.6.0",
"@typescript-eslint/parser": "^5.5.0", "@typescript-eslint/parser": "^5.6.0",
"ajv-keywords": "^5.1.0", "ajv-keywords": "^5.1.0",
"eslint": "^8.3.0", "eslint": "^8.4.1",
"npm-check-updates": "^12.0.2", "npm-check-updates": "^12.0.3",
"ts-node": "~10.4.0", "ts-node": "~10.4.0",
"typescript": "~4.4.4" "typescript": "~4.4.4"
}, },
@ -39,5 +39,5 @@
"build": "ng build --base-href ./ --configuration production", "build": "ng build --base-href ./ --configuration production",
"start": "ng serve" "start": "ng serve"
}, },
"version": "6.0.3" "version": "6.0.4"
} }

View file

@ -757,8 +757,6 @@ export class RdioScannerService implements OnDestroy {
} }
}; };
this.websocket.onerror = () => this.event.emit({ linked: false });
this.websocket.onopen = () => { this.websocket.onopen = () => {
this.event.emit({ linked: true }); this.event.emit({ linked: true });

View file

@ -25,8 +25,8 @@ Here is a list of recorders known to work with [Rdio Scanner](https://github.com
You can enjoy your [Rdio Scanner](https://github.com/chuot/rdio-scanner) on the go on your mobile device with the native app. You can enjoy your [Rdio Scanner](https://github.com/chuot/rdio-scanner) on the go on your mobile device with the native app.
[![Available on the App Store](./docs/images/app-store-badge.png?raw=true)](https://apps.apple.com/us/app/rdio-scanner/id1563065667#?platform=iphone) [![Available on the App Store](../images/app-store-badge.png?raw=true)](https://apps.apple.com/us/app/rdio-scanner/id1563065667#?platform=iphone)
[![Get it on Google Play](./docs/images/google-play-badge.png?raw=true)](https://play.google.com/store/apps/details?id=solutions.saubeo.rdioScanner) [![Get it on Google Play](../images/google-play-badge.png?raw=true)](https://play.google.com/store/apps/details?id=solutions.saubeo.rdioScanner)
\pagebreak{} \pagebreak{}
@ -41,8 +41,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.0.3.zip > ~/Downloads/rdio-scanner-darwin-arm64-v6.0.4.zip
Archive: /Users/rdio/Downloads/rdio-scanner-darwin-arm64-v6.0.3.zip Archive: /Users/rdio/Downloads/rdio-scanner-darwin-arm64-v6.0.4.zip
inflating: rdio-scanner inflating: rdio-scanner
inflating: rdio-scanner.pdf inflating: rdio-scanner.pdf
@ -50,11 +50,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.0.3 Rdio Scanner v6.0.4
---------------------------------- ----------------------------------
2021/11/29 08:38:06 server started 2021/12/08 08:38:06 server started
2021/11/29 08:38:06 main interface at http://macos.local:3000 2021/12/08 08:38:06 main interface at http://macos.local:3000
2021/11/29 08:38:06 admin interface at http://macos.local:3000/admin 2021/12/08 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.
@ -76,11 +76,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.0.3 Rdio Scanner v6.0.4
---------------------------------- ----------------------------------
2021/11/29 08:48:03 server started 2021/12/08 08:48:03 server started
2021/11/29 08:48:03 main interface at http://macos.local 2021/12/08 08:48:03 main interface at http://macos.local
2021/11/29 08:48:03 admin interface at http://macos.local/admin 2021/12/08 08:48:03 admin interface at http://macos.local/admin
## Listening with SSL with self-signed certificates ## Listening with SSL with self-signed certificates
@ -89,8 +89,8 @@ It is advised to share your [Rdio Scanner](https://github.com/chuot/rdio-scanner
We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument. We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument.
rdio@macos rdio-scanner % ./rdio-scanner -ssl_create rdio@macos rdio-scanner % ./rdio-scanner -ssl_create
2021/11/29 08:49:16 generating ssl certificate files 2021/12/08 08:49:16 generating ssl certificate files
2021/11/29 08:49:17 ssl files created 2021/12/08 08:49:17 ssl files created
This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable. This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable.
@ -107,12 +107,12 @@ Now we use the self-signed server certificate to run our instance.
> -ssl_key_file server.key \ > -ssl_key_file server.key \
> -ssl_listen :443 > -ssl_listen :443
Rdio Scanner v6.0.3 Rdio Scanner v6.0.4
---------------------------------- ----------------------------------
2021/11/29 08:50:58 server started 2021/12/08 08:50:58 server started
2021/11/29 08:50:58 main interface at http://macos.local 2021/12/08 08:50:58 main interface at http://macos.local
2021/11/29 08:50:58 main interface at https://macos.local 2021/12/08 08:50:58 main interface at https://macos.local
2021/11/29 08:50:58 admin interface at https://macos.local/admin 2021/12/08 08:50:58 admin interface at https://macos.local/admin
## Save your advanced configuration to a config file ## Save your advanced configuration to a config file
@ -124,7 +124,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y
> -ssl_key_file server.key \ > -ssl_key_file server.key \
> -ssl_listen :443 \ > -ssl_listen :443 \
> -config_save > -config_save
2021/11/29 08:52:24 rdio-scanner.ini file created 2021/12/08 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.
@ -139,12 +139,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.0.3 Rdio Scanner v6.0.4
---------------------------------- ----------------------------------
2021/11/29 08:54:08 server started 2021/12/08 08:54:08 server started
2021/11/29 08:54:08 main interface at http://macos.local 2021/12/08 08:54:08 main interface at http://macos.local
2021/11/29 08:54:08 main interface at https://macos.local 2021/12/08 08:54:08 main interface at https://macos.local
2021/11/29 08:54:08 admin interface at https://macos.local/admin 2021/12/08 08:54:08 admin interface at https://macos.local/admin
## Install Rdio Scanner as a service ## Install Rdio Scanner as a service

View file

@ -25,8 +25,8 @@ Here is a list of recorders known to work with [Rdio Scanner](https://github.com
You can enjoy your [Rdio Scanner](https://github.com/chuot/rdio-scanner) on the go on your mobile device with the native app. You can enjoy your [Rdio Scanner](https://github.com/chuot/rdio-scanner) on the go on your mobile device with the native app.
[![Available on the App Store](./docs/images/app-store-badge.png?raw=true)](https://apps.apple.com/us/app/rdio-scanner/id1563065667#?platform=iphone) [![Available on the App Store](../images/app-store-badge.png?raw=true)](https://apps.apple.com/us/app/rdio-scanner/id1563065667#?platform=iphone)
[![Get it on Google Play](./docs/images/google-play-badge.png?raw=true)](https://play.google.com/store/apps/details?id=solutions.saubeo.rdioScanner) [![Get it on Google Play](../images/google-play-badge.png?raw=true)](https://play.google.com/store/apps/details?id=solutions.saubeo.rdioScanner)
\pagebreak{} \pagebreak{}
@ -41,8 +41,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.0.3.zip > ~/rdio-scanner-freebsd-amd64-v6.0.4.zip
Archive: ../rdio-scanner-freebsd-amd64-v6.0.3.zip Archive: ../rdio-scanner-freebsd-amd64-v6.0.4.zip
extracting: rdio-scanner extracting: rdio-scanner
extracting: rdio-scanner.pdf extracting: rdio-scanner.pdf
@ -50,11 +50,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.0.3 Rdio Scanner v6.0.4
---------------------------------- ----------------------------------
2021/11/28 08:16:36 server started 2021/12/08 08:16:36 server started
2021/11/28 08:16:36 main interface at http://pc-freebsd:3000 2021/12/08 08:16:36 main interface at http://pc-freebsd:3000
2021/11/28 08:16:36 admin interface at http://pc-freebsd:3000/admin 2021/12/08 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.
@ -80,11 +80,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.0.3 Rdio Scanner v6.0.4
---------------------------------- ----------------------------------
2021/11/28 08:19:38 server started 2021/12/08 08:19:38 server started
2021/11/28 08:19:38 main interface at http://pc-freebsd 2021/12/08 08:19:38 main interface at http://pc-freebsd
2021/11/28 08:19:38 admin interface at http://pc-freebsd/admin 2021/12/08 08:19:38 admin interface at http://pc-freebsd/admin
## Listening with SSL with self-signed certificates ## Listening with SSL with self-signed certificates
@ -93,8 +93,8 @@ It is advised to share your [Rdio Scanner](https://github.com/chuot/rdio-scanner
We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument. We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument.
rdio@pc-freebsd:~/rdio-scanner $ ./rdio-scanner -ssl_create rdio@pc-freebsd:~/rdio-scanner $ ./rdio-scanner -ssl_create
2021/11/28 10:32:17 generating ssl certificate files 2021/12/08 10:32:17 generating ssl certificate files
2021/11/28 10:32:19 ssl files created 2021/12/08 10:32:19 ssl files created
This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable. This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable.
@ -113,12 +113,12 @@ Now we use the self-signed server certificate to run our instance.
> -ssl_key_file server.key \ > -ssl_key_file server.key \
> -ssl_listen :443 > -ssl_listen :443
Rdio Scanner v6.0.3 Rdio Scanner v6.0.4
---------------------------------- ----------------------------------
2021/11/28 10:34:29 server started 2021/12/08 10:34:29 server started
2021/11/28 10:34:29 main interface at http://pc-freebsd 2021/12/08 10:34:29 main interface at http://pc-freebsd
2021/11/28 10:34:29 main interface at https://pc-freebsd 2021/12/08 10:34:29 main interface at https://pc-freebsd
2021/11/28 10:34:29 admin interface at https://pc-freebsd/admin 2021/12/08 10:34:29 admin interface at https://pc-freebsd/admin
## Save your advanced configuration to a config file ## Save your advanced configuration to a config file
@ -130,7 +130,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y
> -ssl_key_file server.key \ > -ssl_key_file server.key \
> -ssl_listen :443 \ > -ssl_listen :443 \
> -config_save > -config_save
2021/11/28 10:37:00 rdio-scanner.ini file created 2021/12/08 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.
@ -147,12 +147,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.0.3 Rdio Scanner v6.0.4
---------------------------------- ----------------------------------
2021/11/28 10:38:28 server started 2021/12/08 10:38:28 server started
2021/11/28 10:38:29 main interface at http://pc-freebsd 2021/12/08 10:38:29 main interface at http://pc-freebsd
2021/11/28 10:38:29 main interface at https://pc-freebsd 2021/12/08 10:38:29 main interface at https://pc-freebsd
2021/11/28 10:38:29 admin interface at https://pc-freebsd/admin 2021/12/08 10:38:29 admin interface at https://pc-freebsd/admin
## Install Rdio Scanner as a service ## Install Rdio Scanner as a service

View file

@ -25,8 +25,8 @@ Here is a list of recorders known to work with [Rdio Scanner](https://github.com
You can enjoy your [Rdio Scanner](https://github.com/chuot/rdio-scanner) on the go on your mobile device with the native app. You can enjoy your [Rdio Scanner](https://github.com/chuot/rdio-scanner) on the go on your mobile device with the native app.
[![Available on the App Store](./docs/images/app-store-badge.png?raw=true)](https://apps.apple.com/us/app/rdio-scanner/id1563065667#?platform=iphone) [![Available on the App Store](../images/app-store-badge.png?raw=true)](https://apps.apple.com/us/app/rdio-scanner/id1563065667#?platform=iphone)
[![Get it on Google Play](./docs/images/google-play-badge.png?raw=true)](https://play.google.com/store/apps/details?id=solutions.saubeo.rdioScanner) [![Get it on Google Play](../images/google-play-badge.png?raw=true)](https://play.google.com/store/apps/details?id=solutions.saubeo.rdioScanner)
\pagebreak{} \pagebreak{}
@ -41,8 +41,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.0.3.zip > ~/Downloads/rdio-scanner-linux-amd64-v6.0.4.zip
Archive: /home/rdio/Downloads/rdio-scanner-linux-amd64-v6.0.3.zip Archive: /home/rdio/Downloads/rdio-scanner-linux-amd64-v6.0.4.zip
inflating: rdio-scanner inflating: rdio-scanner
inflating: rdio-scanner.pdf inflating: rdio-scanner.pdf
@ -50,11 +50,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.0.3 Rdio Scanner v6.0.4
---------------------------------- ----------------------------------
2021/11/29 09:11:48 server started 2021/12/08 09:11:48 server started
2021/11/29 09:11:48 main interface at http://pc-linux:3000 2021/12/08 09:11:48 main interface at http://pc-linux:3000
2021/11/29 09:11:48 admin interface at http://pc-linux:3000/admin 2021/12/08 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.
@ -77,11 +77,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.0.3 Rdio Scanner v6.0.4
---------------------------------- ----------------------------------
2021/11/29 09:14:00 server started 2021/12/08 09:14:00 server started
2021/11/29 09:14:00 main interface at http://pc-linux 2021/12/08 09:14:00 main interface at http://pc-linux
2021/11/29 09:14:00 admin interface at http://pc-linux/admin 2021/12/08 09:14:00 admin interface at http://pc-linux/admin
## Listening with SSL with self-signed certificates ## Listening with SSL with self-signed certificates
@ -90,8 +90,8 @@ It is advised to share your [Rdio Scanner](https://github.com/chuot/rdio-scanner
We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument. We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument.
[rdio@pc-linux rdio-scanner]$ ./rdio-scanner -ssl_create [rdio@pc-linux rdio-scanner]$ ./rdio-scanner -ssl_create
2021/11/29 09:15:03 generating ssl certificate files 2021/12/08 09:15:03 generating ssl certificate files
2021/11/29 09:15:05 ssl files created 2021/12/08 09:15:05 ssl files created
This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable. This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable.
@ -109,12 +109,12 @@ Now we use the self-signed server certificate to run our instance.
> -ssl_listen :443 > -ssl_listen :443
[sudo] password for rdio: [sudo] password for rdio:
Rdio Scanner v6.0.3 Rdio Scanner v6.0.4
---------------------------------- ----------------------------------
2021/11/29 09:16:47 server started 2021/12/08 09:16:47 server started
2021/11/29 09:16:47 main interface at http://pc-linux 2021/12/08 09:16:47 main interface at http://pc-linux
2021/11/29 09:16:47 main interface at https://pc-linux 2021/12/08 09:16:47 main interface at https://pc-linux
2021/11/29 09:16:47 admin interface at https://pc-linux/admin 2021/12/08 09:16:47 admin interface at https://pc-linux/admin
## Save your advanced configuration to a config file ## Save your advanced configuration to a config file
@ -126,7 +126,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y
> -ssl_key_file server.key \ > -ssl_key_file server.key \
> -ssl_listen :443 \ > -ssl_listen :443 \
> -config_save > -config_save
2021/11/29 09:19:29 rdio-scanner.ini file created 2021/12/08 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.
@ -142,12 +142,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.0.3 Rdio Scanner v6.0.4
---------------------------------- ----------------------------------
2021/11/29 09:20:40 server started 2021/12/08 09:20:40 server started
2021/11/29 09:20:40 main interface at http://pc-linux 2021/12/08 09:20:40 main interface at http://pc-linux
2021/11/29 09:20:40 main interface at https://pc-linux 2021/12/08 09:20:40 main interface at https://pc-linux
2021/11/29 09:20:40 admin interface at https://pc-linux/admin 2021/12/08 09:20:40 admin interface at https://pc-linux/admin
## Install Rdio Scanner as a service ## Install Rdio Scanner as a service

View file

@ -25,8 +25,8 @@ Here is a list of recorders known to work with [Rdio Scanner](https://github.com
You can enjoy your [Rdio Scanner](https://github.com/chuot/rdio-scanner) on the go on your mobile device with the native app. You can enjoy your [Rdio Scanner](https://github.com/chuot/rdio-scanner) on the go on your mobile device with the native app.
[![Available on the App Store](./docs/images/app-store-badge.png?raw=true)](https://apps.apple.com/us/app/rdio-scanner/id1563065667#?platform=iphone) [![Available on the App Store](../images/app-store-badge.png?raw=true)](https://apps.apple.com/us/app/rdio-scanner/id1563065667#?platform=iphone)
[![Get it on Google Play](./docs/images/google-play-badge.png?raw=true)](https://play.google.com/store/apps/details?id=solutions.saubeo.rdioScanner) [![Get it on Google Play](../images/google-play-badge.png?raw=true)](https://play.google.com/store/apps/details?id=solutions.saubeo.rdioScanner)
\pagebreak{} \pagebreak{}
@ -43,7 +43,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.0.3.zip ..\downloads\rdio-scanner-windows-amd64-v6.0.4.zip
x rdio-scanner.exe x rdio-scanner.exe
x rdio-scanner.pdf x rdio-scanner.pdf
@ -51,11 +51,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.0.3 Rdio Scanner v6.0.4
---------------------------------- ----------------------------------
2021/11/25 13:48:48 server started 2021/12/08 13:48:48 server started
2021/11/25 13:48:48 main interface at http://pc-windows:3000 2021/12/08 13:48:48 main interface at http://pc-windows:3000
2021/11/25 13:48:48 admin interface at http://pc-windows:3000/admin 2021/12/08 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.
@ -77,11 +77,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.0.3 Rdio Scanner v6.0.4
---------------------------------- ----------------------------------
2021/11/26 10:53:31 server started 2021/12/08 10:53:31 server started
2021/11/26 10:53:31 main interface at http://pc-windows 2021/12/08 10:53:31 main interface at http://pc-windows
2021/11/26 10:53:31 admin interface at http://pc-windows/admin 2021/12/08 10:53:31 admin interface at http://pc-windows/admin
## Listening with SSL with self-signed certificates ## Listening with SSL with self-signed certificates
@ -90,8 +90,8 @@ It is advised to share your [Rdio Scanner](https://github.com/chuot/rdio-scanner
We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument. We can create such self-signed certificates by running [Rdio Scanner](https://github.com/chuot/rdio-scanner) with the **-ssl_create** argument.
C:\Users\rdio\rdio-scanner>rdio-scanner -ssl_create C:\Users\rdio\rdio-scanner>rdio-scanner -ssl_create
2021/11/26 10:58:51 generating ssl certificate files 2021/12/08 10:58:51 generating ssl certificate files
2021/11/26 10:58:54 ssl files created 2021/12/08 10:58:54 ssl files created
This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable. This has generated these files in the same folder as your [Rdio Scanner](https://github.com/chuot/rdio-scanner) executable.
@ -108,12 +108,12 @@ Now we use the self-signed server certificate to run our instance.
-ssl_key_file server.key ^ -ssl_key_file server.key ^
-ssl_listen :443 -ssl_listen :443
Rdio Scanner v6.0.3 Rdio Scanner v6.0.4
---------------------------------- ----------------------------------
2021/11/26 11:05:43 server started 2021/12/08 11:05:43 server started
2021/11/26 11:05:43 main interface at http://pc-windows 2021/12/08 11:05:43 main interface at http://pc-windows
2021/11/26 11:05:43 main interface at https://pc-windows 2021/12/08 11:05:43 main interface at https://pc-windows
2021/11/26 11:05:43 admin interface at https://pc-windows/admin 2021/12/08 11:05:43 admin interface at https://pc-windows/admin
## Save your advanced configuration to a config file ## Save your advanced configuration to a config file
@ -125,7 +125,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y
-ssl_key_file server.key ^ -ssl_key_file server.key ^
-ssl_listen :443 ^ -ssl_listen :443 ^
-config_save -config_save
2021/11/26 11:08:28 rdio-scanner.ini file created 2021/12/08 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.
@ -140,12 +140,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.0.3 Rdio Scanner v6.0.4
---------------------------------- ----------------------------------
2021/11/26 11:11:28 server started 2021/12/08 11:11:28 server started
2021/11/26 11:11:28 main interface at http://pc-windows 2021/12/08 11:11:28 main interface at http://pc-windows
2021/11/26 11:11:28 main interface at https://pc-windows 2021/12/08 11:11:28 main interface at https://pc-windows
2021/11/26 11:11:28 admin interface at https://pc-windows/admin 2021/12/08 11:11:28 admin interface at https://pc-windows/admin
\pagebreak{} \pagebreak{}

View file

@ -202,11 +202,9 @@ func (accesses *Accesses) Read(db *Database) error {
*accesses = append(*accesses, access) *accesses = append(*accesses, access)
} }
if err != nil { rows.Close()
return formatError(err)
}
if err = rows.Close(); err != nil { if err != nil {
return formatError(err) return formatError(err)
} }
@ -270,12 +268,9 @@ func (accesses *Accesses) Write(db *Database) error {
} }
} }
if err != nil {
rows.Close() rows.Close()
return formatError(err)
}
if err = rows.Close(); err != nil { if err != nil {
return formatError(err) return formatError(err)
} }

View file

@ -37,7 +37,6 @@ const (
) )
type Admin struct { type Admin struct {
initialized bool
Attempts AdminLoginAttempts Attempts AdminLoginAttempts
AttemptsMax uint AttemptsMax uint
AttemptsMaxDelay time.Duration AttemptsMaxDelay time.Duration
@ -47,6 +46,7 @@ type Admin struct {
Register chan *websocket.Conn Register chan *websocket.Conn
Tokens []string Tokens []string
Unregister chan *websocket.Conn Unregister chan *websocket.Conn
running bool
} }
type AdminLoginAttempt struct { type AdminLoginAttempt struct {
@ -56,6 +56,20 @@ type AdminLoginAttempt struct {
type AdminLoginAttempts map[string]*AdminLoginAttempt type AdminLoginAttempts map[string]*AdminLoginAttempt
func NewAdmin(controller *Controller) *Admin {
return &Admin{
Attempts: AdminLoginAttempts{},
AttemptsMax: uint(3),
AttemptsMaxDelay: time.Duration(time.Duration.Minutes(10)),
Broadcast: make(chan *[]byte, 100),
Conns: make(map[*websocket.Conn]bool),
Controller: controller,
Register: make(chan *websocket.Conn, 100),
Tokens: []string{},
Unregister: make(chan *websocket.Conn, 100),
}
}
func (admin *Admin) BroadcastConfig() { func (admin *Admin) BroadcastConfig() {
if b, err := json.Marshal(admin.GetConfig()); err == nil { if b, err := json.Marshal(admin.GetConfig()); err == nil {
@ -298,52 +312,6 @@ func (admin *Admin) GetConfig() map[string]interface{} {
} }
} }
func (admin *Admin) Init(controller *Controller) error {
if admin.initialized {
return errors.New("admin already initialized")
}
admin.initialized = true
admin.Attempts = AdminLoginAttempts{}
admin.AttemptsMax = uint(3)
admin.AttemptsMaxDelay = time.Duration(time.Duration.Minutes(10))
admin.Broadcast = make(chan *[]byte, 100)
admin.Conns = make(map[*websocket.Conn]bool)
admin.Controller = controller
admin.Register = make(chan *websocket.Conn, 100)
admin.Tokens = []string{}
admin.Unregister = make(chan *websocket.Conn, 100)
go func() {
for {
select {
case data, ok := <-admin.Broadcast:
if !ok {
return
}
for conn := range admin.Conns {
err := conn.WriteMessage(websocket.TextMessage, *data)
if err != nil {
admin.Unregister <- conn
}
}
case conn := <-admin.Register:
admin.Conns[conn] = true
case conn := <-admin.Unregister:
if _, ok := admin.Conns[conn]; ok {
delete(admin.Conns, conn)
conn.Close()
}
}
}
}()
return nil
}
func (admin *Admin) LogsHandler(w http.ResponseWriter, r *http.Request) { func (admin *Admin) LogsHandler(w http.ResponseWriter, r *http.Request) {
t := admin.GetAuthorization(r) t := admin.GetAuthorization(r)
if !admin.ValidateToken(t) { if !admin.ValidateToken(t) {
@ -570,6 +538,43 @@ func (admin *Admin) SendConfig(w http.ResponseWriter) {
} }
} }
func (admin *Admin) Start() error {
if admin.running {
return errors.New("admin already running")
} else {
admin.running = true
}
go func() {
for {
select {
case data, ok := <-admin.Broadcast:
if !ok {
return
}
for conn := range admin.Conns {
err := conn.WriteMessage(websocket.TextMessage, *data)
if err != nil {
admin.Unregister <- conn
}
}
case conn := <-admin.Register:
admin.Conns[conn] = true
case conn := <-admin.Unregister:
if _, ok := admin.Conns[conn]; ok {
delete(admin.Conns, conn)
conn.Close()
}
}
}
}()
return nil
}
func (admin *Admin) ValidateToken(sToken string) bool { func (admin *Admin) ValidateToken(sToken string) bool {
found := false found := false
for _, t := range admin.Tokens { for _, t := range admin.Tokens {

View file

@ -17,7 +17,6 @@ package main
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"mime" "mime"
@ -35,18 +34,11 @@ const (
) )
type Api struct { type Api struct {
initialized bool
Controller *Controller Controller *Controller
} }
func (api *Api) Init(controller *Controller) error { func NewApi(controller *Controller) *Api {
if api.initialized { return &Api{Controller: controller}
return errors.New("api already initialized")
}
api.Controller = controller
return nil
} }
func (api *Api) CallUploadHandler(w http.ResponseWriter, r *http.Request) { func (api *Api) CallUploadHandler(w http.ResponseWriter, r *http.Request) {

View file

@ -174,10 +174,10 @@ func (apikeys *Apikeys) Read(db *Database) error {
rows.Close() rows.Close()
if err != nil { if err != nil {
err = formatError(err) return formatError(err)
} }
return err return nil
} }
func (apikeys *Apikeys) Write(db *Database) error { func (apikeys *Apikeys) Write(db *Database) error {
@ -239,12 +239,9 @@ func (apikeys *Apikeys) Write(db *Database) error {
} }
} }
if err != nil {
rows.Close() rows.Close()
return formatError(err)
}
if err = rows.Close(); err != nil { if err != nil {
return formatError(err) return formatError(err)
} }

View file

@ -49,7 +49,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, 100) client.Send = make(chan *Message, 8)
client.LivefeedMap = LivefeedMap{} client.LivefeedMap = LivefeedMap{}
controller.Register <- client controller.Register <- client

View file

@ -35,6 +35,7 @@ const (
) )
type Config struct { type Config struct {
BaseDir string
ConfigFile string ConfigFile string
DbType string DbType string
DbFile string DbFile string
@ -53,43 +54,7 @@ type Config struct {
newAdminPassword string newAdminPassword string
} }
func (config *Config) GetConfigFilePath() string { func NewConfig() *Config {
return config.GetPath(config.ConfigFile)
}
func (config *Config) GetDbFilePath() string {
return config.GetPath(config.DbFile)
}
func (config *Config) GetPath(p string) string {
if !path.IsAbs(p) {
if exe, err := os.Executable(); err == nil {
if !regexp.MustCompile(`go-build[0-9]+`).Match([]byte(exe)) {
return path.Join(filepath.Dir(exe), p)
}
}
}
return p
}
func (config *Config) GetSslCaCertFilePath() string {
return config.GetPath(config.SslCaCertFile)
}
func (config *Config) GetSslCaKeyFilePath() string {
return config.GetPath(config.SslCaKeyFile)
}
func (config *Config) GetSslCertFilePath() string {
return config.GetPath(config.SslCertFile)
}
func (config *Config) GetSslKeyFilePath() string {
return config.GetPath(config.SslKeyFile)
}
func (config *Config) Init() (ok bool, error error) {
const ( const (
defaultAdminUrl = "/admin" defaultAdminUrl = "/admin"
defaultConfigFile = "rdio-scanner.ini" defaultConfigFile = "rdio-scanner.ini"
@ -101,6 +66,7 @@ func (config *Config) Init() (ok bool, error error) {
) )
var ( var (
config = &Config{}
configSave = flag.Bool("config_save", false, fmt.Sprintf("save configuration to %s", defaultConfigFile)) configSave = flag.Bool("config_save", false, fmt.Sprintf("save configuration to %s", defaultConfigFile))
serviceAction = flag.String("service", "", "service command, one of start, stop, restart, install, uninstall") serviceAction = flag.String("service", "", "service command, one of start, stop, restart, install, uninstall")
sslCreate = flag.Bool("ssl_create", false, "create self-signed certificates") sslCreate = flag.Bool("ssl_create", false, "create self-signed certificates")
@ -121,33 +87,50 @@ func (config *Config) Init() (ok bool, error error) {
flag.StringVar(&config.SslKeyFile, "ssl_key_file", "", "ssl PEM formated key") flag.StringVar(&config.SslKeyFile, "ssl_key_file", "", "ssl PEM formated key")
flag.StringVar(&config.SslListen, "ssl_listen", "", "listening address for ssl") flag.StringVar(&config.SslListen, "ssl_listen", "", "listening address for ssl")
flag.StringVar(&config.newAdminPassword, "admin_password", "", "change admin password") flag.StringVar(&config.newAdminPassword, "admin_password", "", "change admin password")
flag.Parse() flag.Parse()
if exe, err := os.Executable(); err == nil {
if !regexp.MustCompile(`go-build[0-9]+`).Match([]byte(exe)) {
config.BaseDir = filepath.Dir(exe)
if !config.isBaseDirWritable() {
if h, err := os.UserHomeDir(); err == nil {
config.BaseDir = filepath.Join(h, "Rdio Scanner")
if _, err := os.Stat(config.BaseDir); os.IsNotExist(err) {
os.MkdirAll(config.BaseDir, 0770)
}
}
}
}
}
if !config.isBaseDirWritable() {
log.Fatalf("no write permissions in %s", config.BaseDir)
}
switch { switch {
case *configSave: case *configSave:
if err := config.saveConfig(); err == nil { if err := config.saveConfig(); err == nil {
log.Printf("%s file created", config.ConfigFile) fmt.Printf("%s file created\n", config.ConfigFile)
return false, nil os.Exit(0)
} else { } else {
return false, err fmt.Printf("error: %s\n", err.Error())
os.Exit(-1)
} }
case *sslCreate: case *sslCreate:
log.Print("generating ssl certificate files") fmt.Println("generating ssl certificate files")
if err := CreateSelfSignedCert(config); err == nil { if err := CreateSelfSignedCert(config); err == nil {
log.Print("ssl files created") fmt.Println("ssl files created")
return false, nil os.Exit(0)
} else { } else {
return false, err fmt.Printf("error: %s", err.Error())
os.Exit(-1)
} }
case *version: case *version:
fmt.Println(Version) fmt.Println(Version)
return false, nil os.Exit(0)
default: default:
if cfg, err := ini.Load(config.GetConfigFilePath()); err == nil { if cfg, err := ini.Load(config.GetConfigFilePath()); err == nil {
@ -201,15 +184,56 @@ func (config *Config) Init() (ok bool, error error) {
} }
if !(config.DbType == DbTypeMariadb || config.DbType == DbTypeMysql || config.DbType == DbTypeSqlite) { if !(config.DbType == DbTypeMariadb || config.DbType == DbTypeMysql || config.DbType == DbTypeSqlite) {
return false, fmt.Errorf("unknown database type %s", config.DbType) fmt.Printf("unknown database type %s\n", config.DbType)
return nil
} }
} }
if *serviceAction != "" { if *serviceAction != "" {
return NewDaemon().Control(*serviceAction) NewDaemon().Control(*serviceAction)
} }
return true, nil return config
}
func (config *Config) GetConfigFilePath() string {
return config.GetPath(config.ConfigFile)
}
func (config *Config) GetDbFilePath() string {
return config.GetPath(config.DbFile)
}
func (config *Config) GetPath(p string) string {
if path.IsAbs(p) {
return p
}
return path.Join(config.BaseDir, p)
}
func (config *Config) GetSslCaCertFilePath() string {
return config.GetPath(config.SslCaCertFile)
}
func (config *Config) GetSslCaKeyFilePath() string {
return config.GetPath(config.SslCaKeyFile)
}
func (config *Config) GetSslCertFilePath() string {
return config.GetPath(config.SslCertFile)
}
func (config *Config) GetSslKeyFilePath() string {
return config.GetPath(config.SslKeyFile)
}
func (config *Config) isBaseDirWritable() bool {
if f, err := os.CreateTemp(config.BaseDir, ".tmp*"); err == nil {
f.Close()
os.Remove(f.Name())
return true
}
return false
} }
func (config *Config) saveConfig() error { func (config *Config) saveConfig() error {

View file

@ -48,9 +48,34 @@ type Controller struct {
Register chan *Client Register chan *Client
Unregister chan *Client Unregister chan *Client
Ingest chan *Call Ingest chan *Call
initialized bool
ffmpeg bool ffmpeg bool
ffmpegWarned bool ffmpegWarned bool
running bool
}
func NewController(config *Config) *Controller {
controller := &Controller{
Config: config,
Accesses: &Accesses{},
Apikeys: &Apikeys{},
Dirwatches: &Dirwatches{},
Downstreams: &Downstreams{},
Groups: &Groups{},
Options: &Options{},
Systems: &Systems{},
Tags: &Tags{},
Clients: make(map[*Client]bool),
Register: make(chan *Client, 64),
Unregister: make(chan *Client, 64),
Ingest: make(chan *Call, 64),
}
controller.Admin = NewAdmin(controller)
controller.Api = NewApi(controller)
controller.Database = NewDatabase(config)
controller.Scheduler = NewScheduler(controller)
return controller
} }
func (controller *Controller) CheckDuplicate(call *Call) bool { func (controller *Controller) CheckDuplicate(call *Call) bool {
@ -68,7 +93,7 @@ func (controller *Controller) CheckDuplicate(call *Call) bool {
return count > 0 return count > 0
} }
func (controller *Controller) ConvertAudio(call *Call) error { func (controller *Controller) ConvertAudio(call *Call) {
var ( var (
args = []string{"-i", "-"} args = []string{"-i", "-"}
err error err error
@ -80,8 +105,7 @@ func (controller *Controller) ConvertAudio(call *Call) error {
LogEvent(controller.Database, LogLevelWarn, "ffmpeg is not accessible, no audio conversion will be performed.") LogEvent(controller.Database, LogLevelWarn, "ffmpeg is not accessible, no audio conversion will be performed.")
} }
return
return nil
} }
if system, ok := controller.Systems.GetSystem(call.System); ok { if system, ok := controller.Systems.GetSystem(call.System); ok {
@ -106,6 +130,9 @@ func (controller *Controller) ConvertAudio(call *Call) error {
stdout := bytes.NewBuffer([]byte(nil)) stdout := bytes.NewBuffer([]byte(nil))
cmd.Stdout = stdout cmd.Stdout = stdout
stderr := bytes.NewBuffer([]byte(nil))
cmd.Stderr = stderr
if err = cmd.Run(); err == nil { if err = cmd.Run(); err == nil {
call.Audio = stdout.Bytes() call.Audio = stdout.Bytes()
call.AudioType = "audio/mp4" call.AudioType = "audio/mp4"
@ -114,9 +141,10 @@ func (controller *Controller) ConvertAudio(call *Call) error {
case string: case string:
call.AudioName = fmt.Sprintf("%v.m4a", strings.TrimSuffix(v, path.Ext((v)))) call.AudioName = fmt.Sprintf("%v.m4a", strings.TrimSuffix(v, path.Ext((v))))
} }
}
return err } else {
fmt.Println(stderr.String())
}
} }
func (controller *Controller) EmitCall(call *Call) { func (controller *Controller) EmitCall(call *Call) {
@ -323,10 +351,7 @@ func (controller *Controller) IngestCall(call *Call) {
} }
if !controller.Options.DisableAudioConversion { if !controller.Options.DisableAudioConversion {
if err = controller.ConvertAudio(call); err != nil { controller.ConvertAudio(call)
logError(fmt.Errorf("convertaudio: %s", err.Error()))
return
}
} }
if id, err = call.Write(controller.Database); err == nil { if id, err = call.Write(controller.Database); err == nil {
@ -339,111 +364,6 @@ func (controller *Controller) IngestCall(call *Call) {
} }
} }
func (controller *Controller) Init(config *Config, database *Database) error {
if controller.initialized {
return errors.New("controller already initialized")
}
var err error
controller.initialized = true
controller.Admin = &Admin{}
controller.Api = &Api{}
controller.Config = config
controller.Database = database
controller.Accesses = &Accesses{}
controller.Apikeys = &Apikeys{}
controller.Dirwatches = &Dirwatches{}
controller.Downstreams = &Downstreams{}
controller.Groups = &Groups{}
controller.Options = &Options{}
controller.Scheduler = &Scheduler{}
controller.Systems = &Systems{}
controller.Tags = &Tags{}
controller.Clients = make(map[*Client]bool)
controller.Register = make(chan *Client, 64)
controller.Unregister = make(chan *Client, 64)
controller.Ingest = make(chan *Call, 64)
if err = controller.Accesses.Read(database); err != nil {
return err
}
if err = controller.Apikeys.Read(database); err != nil {
return err
}
if err = controller.Dirwatches.Read(database); err != nil {
return err
}
if err = controller.Downstreams.Read(database); err != nil {
return err
}
if err = controller.Groups.Read(database); err != nil {
return err
}
if err = controller.Options.Read(database); err != nil {
return err
}
if err = controller.Systems.Read(database); err != nil {
return err
}
if err = controller.Tags.Read(database); err != nil {
return err
}
if err = controller.Admin.Init(controller); err != nil {
return err
}
if err = controller.Api.Init(controller); err != nil {
return err
}
if err = controller.Scheduler.Init(controller); err != nil {
return err
}
cmd := exec.Command("ffmpeg", "-version")
if err := cmd.Run(); err == nil {
controller.ffmpeg = true
} else {
controller.ffmpeg = false
}
if err = controller.Scheduler.Start(); err != nil {
log.Printf("scheduler: %s", err.Error())
}
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
controller.Terminate()
}()
go func() {
for {
select {
case call := <-controller.Ingest:
controller.IngestCall(call)
case client := <-controller.Register:
controller.Clients[client] = true
controller.LogClientsCount()
case client := <-controller.Unregister:
if _, ok := controller.Clients[client]; ok {
delete(controller.Clients, client)
close(client.Send)
controller.LogClientsCount()
}
}
}
}()
controller.Dirwatches.Start(controller)
return nil
}
func (controller *Controller) LogClientsCount() { func (controller *Controller) LogClientsCount() {
LogEvent( LogEvent(
controller.Database, controller.Database,
@ -629,6 +549,93 @@ func (controller *Controller) SendClientConfig(client *Client) {
} }
} }
func (controller *Controller) Start() error {
var err error
if controller.running {
return errors.New("controller already running")
} else {
controller.running = true
}
LogEvent(controller.Database, LogLevelWarn, "server started")
if len(controller.Config.BaseDir) > 0 {
LogEvent(controller.Database, LogLevelWarn, fmt.Sprintf("base folder is %s", controller.Config.BaseDir))
}
if err = controller.Accesses.Read(controller.Database); err != nil {
return err
}
if err = controller.Apikeys.Read(controller.Database); err != nil {
return err
}
if err = controller.Dirwatches.Read(controller.Database); err != nil {
return err
}
if err = controller.Downstreams.Read(controller.Database); err != nil {
return err
}
if err = controller.Groups.Read(controller.Database); err != nil {
return err
}
if err = controller.Options.Read(controller.Database); err != nil {
return err
}
if err = controller.Systems.Read(controller.Database); err != nil {
return err
}
if err = controller.Tags.Read(controller.Database); err != nil {
return err
}
if err = controller.Admin.Start(); err != nil {
return err
}
if err = controller.Scheduler.Start(); err != nil {
return err
}
cmd := exec.Command("ffmpeg", "-version")
if err := cmd.Run(); err == nil {
controller.ffmpeg = true
} else {
controller.ffmpeg = false
}
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
controller.Terminate()
}()
go func() {
for {
select {
case call := <-controller.Ingest:
controller.IngestCall(call)
case client := <-controller.Register:
controller.Clients[client] = true
controller.LogClientsCount()
case client := <-controller.Unregister:
if _, ok := controller.Clients[client]; ok {
delete(controller.Clients, client)
close(client.Send)
controller.LogClientsCount()
}
}
}
}()
controller.Dirwatches.Start(controller)
return nil
}
func (controller *Controller) Terminate() { func (controller *Controller) Terminate() {
if err := controller.Database.Sql.Close(); err != nil { if err := controller.Database.Sql.Close(); err != nil {
log.Println(err) log.Println(err)
@ -636,6 +643,10 @@ func (controller *Controller) Terminate() {
controller.Dirwatches.Stop() controller.Dirwatches.Stop()
for c := range controller.Clients {
c.Conn.Close()
}
log.Println("terminated") log.Println("terminated")
os.Exit(0) os.Exit(0)

View file

@ -62,14 +62,16 @@ func NewDaemon() *Daemon {
return &d return &d
} }
func (d *Daemon) Control(action string) (bool, error) { func (d *Daemon) Control(action string) {
if action == "run" { if action == "run" {
go d.Service.Run() go d.Service.Run()
return true, nil } else if err := service.Control(d.Service, action); err == nil {
} os.Exit(0)
return false, service.Control(d.Service, action) } else {
os.Exit(-1)
}
} }
type DaemonInterface struct{} type DaemonInterface struct{}

View file

@ -17,7 +17,6 @@ package main
import ( import (
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"log" "log"
"time" "time"
@ -27,59 +26,52 @@ import (
) )
type Database struct { type Database struct {
initialized bool
Config *Config Config *Config
DateTimeFormat string DateTimeFormat string
Sql *sql.DB Sql *sql.DB
} }
func (db *Database) Init(config *Config) error { func NewDatabase(config *Config) *Database {
var err error var err error
if db.initialized { database := &Database{Config: config}
return errors.New("database already initialized")
}
db.initialized = true switch config.DbType {
db.Config = config
switch db.Config.DbType {
case DbTypeSqlite: case DbTypeSqlite:
db.DateTimeFormat = "2006-01-02 15:04:05.000 -07:00" database.DateTimeFormat = "2006-01-02 15:04:05.000 -07:00"
dsn := fmt.Sprintf("file:%s", db.Config.GetDbFilePath()) dsn := fmt.Sprintf("file:%s", config.GetDbFilePath())
if db.Sql, err = sql.Open("sqlite", dsn); err != nil { if database.Sql, err = sql.Open("sqlite", dsn); err != nil {
return err log.Fatal(err)
} }
case DbTypeMariadb, DbTypeMysql: case DbTypeMariadb, DbTypeMysql:
db.DateTimeFormat = "2006-01-02 15:04:05" database.DateTimeFormat = "2006-01-02 15:04:05"
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", db.Config.DbUsername, db.Config.DbPassword, db.Config.DbHost, db.Config.DbPort, db.Config.DbName) dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", config.DbUsername, config.DbPassword, config.DbHost, config.DbPort, config.DbName)
if db.Sql, err = sql.Open("mysql", dsn); err != nil { if database.Sql, err = sql.Open("mysql", dsn); err != nil {
return err log.Fatal(err)
} }
default: default:
return fmt.Errorf("unknown database type %s", db.Config.DbType) log.Fatalf("unknown database type %s\n", config.DbType)
} }
db.Sql.SetConnMaxLifetime(0) database.Sql.SetConnMaxLifetime(0)
db.Sql.SetMaxIdleConns(10) database.Sql.SetMaxIdleConns(10)
db.Sql.SetMaxOpenConns(10) database.Sql.SetMaxOpenConns(10)
if err = db.migrate(); err != nil { if err = database.migrate(); err != nil {
return err log.Fatal(err)
} }
if err = db.seed(); err != nil { if err = database.seed(); err != nil {
return err log.Fatal(err)
} }
return nil return database
} }
func (db *Database) ParseDateTime(f interface{}) (time.Time, error) { func (db *Database) ParseDateTime(f interface{}) (time.Time, error) {
@ -102,7 +94,7 @@ func (db *Database) Prune(controller *Controller) error {
LogEvent(controller.Database, LogLevelInfo, "database pruning") LogEvent(controller.Database, LogLevelInfo, "database pruning")
date := time.Now().Truncate(time.Duration(controller.Options.PruneDays*24) * time.Hour).Format(db.DateTimeFormat) date := time.Now().Add(-24 * time.Hour * time.Duration(controller.Options.PruneDays)).Format(db.DateTimeFormat)
if _, err := controller.Database.Sql.Exec("delete from `rdioScannerCalls` where `dateTime` < ?", date); err != nil { if _, err := controller.Database.Sql.Exec("delete from `rdioScannerCalls` where `dateTime` < ?", date); err != nil {
return err return err

View file

@ -274,7 +274,7 @@ func (dirwatch *Dirwatch) ingestTrunkRecorder(p string) error {
} }
if call.Audio, err = ioutil.ReadFile(audioName); err != nil { if call.Audio, err = ioutil.ReadFile(audioName); err != nil {
return err return nil
} }
if b, err = ioutil.ReadFile(p); err != nil { if b, err = ioutil.ReadFile(p); err != nil {
@ -494,9 +494,10 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
} }
}() }()
fileSystem := os.DirFS(dirwatch.Directory) go func() {
time.Sleep(2 * time.Second)
return fs.WalkDir(fileSystem, ".", func(p string, d fs.DirEntry, err error) error { fs.WalkDir(os.DirFS(dirwatch.Directory), ".", func(p string, d fs.DirEntry, err error) error {
fp := path.Join(dirwatch.Directory, p) fp := path.Join(dirwatch.Directory, p)
if dirwatch.isDir(fp) { if dirwatch.isDir(fp) {
@ -508,6 +509,9 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
} }
return err return err
}) })
}()
return nil
} }
func (dirwatch *Dirwatch) Stop() { func (dirwatch *Dirwatch) Stop() {
@ -582,11 +586,9 @@ func (dirwatches *Dirwatches) Read(db *Database) error {
*dirwatches = append(*dirwatches, dirwatch) *dirwatches = append(*dirwatches, dirwatch)
} }
if err != nil { rows.Close()
return formatError(err)
}
if err = rows.Close(); err != nil { if err != nil {
return formatError(err) return formatError(err)
} }
@ -641,8 +643,7 @@ func (dirwatches *Dirwatches) Write(db *Database) error {
return formatError(err) return formatError(err)
} }
rows, err = db.Sql.Query("select `_id` from `rdioScannerDirWatches`") if rows, err = db.Sql.Query("select `_id` from `rdioScannerDirWatches`"); err != nil {
if err != nil {
return formatError(err) return formatError(err)
} }
@ -661,12 +662,9 @@ func (dirwatches *Dirwatches) Write(db *Database) error {
} }
} }
if err != nil {
rows.Close() rows.Close()
return formatError(err)
}
if err = rows.Close(); err != nil { if err != nil {
return formatError(err) return formatError(err)
} }
@ -674,28 +672,13 @@ func (dirwatches *Dirwatches) Write(db *Database) error {
} }
func (dirwatch *Dirwatch) isDir(d string) bool { func (dirwatch *Dirwatch) isDir(d string) bool {
var isDir bool if fi, err := os.Stat(d); err == nil {
logError := func(err error) {
LogEvent(dirwatch.controller.Database, LogLevelError, fmt.Sprintf("dirwatch.isDir: %s", err.Error()))
}
if f, err := os.Open(d); err == nil {
if fi, err := f.Stat(); err == nil {
if fi.IsDir() { if fi.IsDir() {
isDir = true return true
} }
} else {
logError(err)
}
if err := f.Close(); err != nil {
logError(err)
}
} else {
logError(err)
} }
return isDir return false
} }
func (dirwatch *Dirwatch) walkDir(d string) error { func (dirwatch *Dirwatch) walkDir(d string) error {

View file

@ -337,23 +337,24 @@ func (downstreams *Downstreams) FromMap(f []interface{}) {
func (downstreams *Downstreams) Read(db *Database) error { func (downstreams *Downstreams) Read(db *Database) error {
var ( var (
apikey interface{}
err error err error
id uint
rows *sql.Rows rows *sql.Rows
systems interface{}
) )
*downstreams = Downstreams{} *downstreams = Downstreams{}
formatError := func(err error) error {
return fmt.Errorf("downstreams.read: %v", err)
}
if rows, err = db.Sql.Query("select `_id`, `apiKey`, `disabled`, `order`, `systems`, `url` from `rdioScannerDownstreams`"); err != nil { if rows, err = db.Sql.Query("select `_id`, `apiKey`, `disabled`, `order`, `systems`, `url` from `rdioScannerDownstreams`"); err != nil {
return err return formatError(err)
} }
for rows.Next() { for rows.Next() {
var (
apikey interface{}
id uint
systems interface{}
)
downstream := Downstream{} downstream := Downstream{}
if err = rows.Scan(&id, &apikey, &downstream.Disabled, &downstream.Order, &systems, &downstream.Url); err != nil { if err = rows.Scan(&id, &apikey, &downstream.Disabled, &downstream.Order, &systems, &downstream.Url); err != nil {
break break
@ -383,12 +384,10 @@ func (downstreams *Downstreams) Read(db *Database) error {
*downstreams = append(*downstreams, downstream) *downstreams = append(*downstreams, downstream)
} }
if err != nil { rows.Close()
return err
}
if err = rows.Close(); err != nil { if err != nil {
return err return formatError(err)
} }
return nil return nil
@ -473,12 +472,9 @@ func (downstreams *Downstreams) Write(db *Database) error {
} }
} }
if err != nil {
rows.Close() rows.Close()
return formatError(err)
}
if err = rows.Close(); err != nil { if err != nil {
return formatError(err) return formatError(err)
} }

View file

@ -21,15 +21,15 @@ 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.5.1 // indirect golang.org/x/mod v0.5.1 // indirect
golang.org/x/net v0.0.0-20211205041911-012df41ee64c // indirect golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b // indirect
golang.org/x/sys v0.0.0-20211204120058-94396e421777 // indirect golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.8 // indirect golang.org/x/tools v0.1.8 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
lukechampine.com/uint128 v1.1.1 // indirect lukechampine.com/uint128 v1.1.1 // indirect
modernc.org/cc/v3 v3.35.18 // indirect modernc.org/cc/v3 v3.35.18 // indirect
modernc.org/ccgo/v3 v3.12.86 // indirect modernc.org/ccgo/v3 v3.12.87 // indirect
modernc.org/libc v1.11.88 // indirect modernc.org/libc v1.11.89 // indirect
modernc.org/mathutil v1.4.1 // indirect modernc.org/mathutil v1.4.1 // indirect
modernc.org/memory v1.0.5 // indirect modernc.org/memory v1.0.5 // indirect
modernc.org/opt v0.1.1 // indirect modernc.org/opt v0.1.1 // indirect

View file

@ -47,8 +47,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
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-20211205041911-012df41ee64c h1:7SfqwP5fxEtl/P02w5IhKc86ziJ+A25yFrkVgoy2FT8= golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b h1:MWaHNqZy3KTpuTMAGvv+Kw+ylsEpmyJZizz1dqxnu28=
golang.org/x/net v0.0.0-20211205041911-012df41ee64c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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=
@ -65,8 +65,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/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-20211204120058-94396e421777 h1:QAkhGVjOxMa+n4mlsAWeAU+BMZmimQAaNiMu+iUi94E= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E=
golang.org/x/sys v0.0.0-20211204120058-94396e421777/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -141,8 +141,9 @@ modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3
modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY= modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY=
modernc.org/ccgo/v3 v3.12.82/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w= modernc.org/ccgo/v3 v3.12.82/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w= modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
modernc.org/ccgo/v3 v3.12.86 h1:zN0MkWIf1a2SgP6w9V3EG5r30CSrpRunyzJ5wcYRLFM=
modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU= modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU=
modernc.org/ccgo/v3 v3.12.87 h1:d8IVZadjkPWICHmMWEaPAL37zBrZ7rE7/+Plegf0cyw=
modernc.org/ccgo/v3 v3.12.87/go.mod h1:55tmwIw8bqNbqYCZ2DIXGqB1I0goRe6tq2qHqr1FaQU=
modernc.org/ccorpus v1.11.1 h1:K0qPfpVG1MJh5BYazccnmhywH4zHuOgJXgbjzyp6dWA= modernc.org/ccorpus v1.11.1 h1:K0qPfpVG1MJh5BYazccnmhywH4zHuOgJXgbjzyp6dWA=
modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
@ -182,8 +183,9 @@ 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.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE= 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.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
modernc.org/libc v1.11.88 h1:ibbHksDVITfJtBTSCENSXRt/pL4P/QJP+EzJUimW268=
modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ= modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ=
modernc.org/libc v1.11.89 h1:41X0GhYT3+0WbXvsOMDguwnmfY6692gXkSlOuSaO9jc=
modernc.org/libc v1.11.89/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= 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.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=

View file

@ -166,11 +166,9 @@ func (groups *Groups) Read(db *Database) error {
*groups = append(*groups, group) *groups = append(*groups, group)
} }
if err != nil { rows.Close()
return formatError(err)
}
if err = rows.Close(); err != nil { if err != nil {
return formatError(err) return formatError(err)
} }
@ -228,12 +226,9 @@ func (groups *Groups) Write(db *Database) error {
} }
} }
if err != nil {
rows.Close() rows.Close()
return formatError(err)
}
if err = rows.Close(); err != nil { if err != nil {
return formatError(err) return formatError(err)
} }

View file

@ -229,12 +229,10 @@ func NewLogResults(logOptions *LogOptions, db *Database) (*LogResults, error) {
logResults.Logs = append(logResults.Logs, log) logResults.Logs = append(logResults.Logs, log)
} }
if err != nil { rows.Close()
return logResults, formatError(err)
}
if err = rows.Close(); err != nil { if err != nil {
return logResults, formatError(err) return nil, formatError(err)
} }
return logResults, nil return logResults, nil

View file

@ -40,27 +40,16 @@ func main() {
sslPort string sslPort string
) )
config := &Config{} config := NewConfig()
ok, err := config.Init()
if err != nil {
log.Fatal(err)
}
if !ok {
os.Exit(0)
}
database := &Database{} controller := NewController(config)
if err = database.Init(config); err != nil {
log.Fatal(err)
}
controller := &Controller{}
if err = controller.Init(config, database); err != nil {
log.Fatal(err)
}
if config.newAdminPassword != "" { if config.newAdminPassword != "" {
if hash, err := bcrypt.GenerateFromPassword([]byte(config.newAdminPassword), bcrypt.DefaultCost); err == nil { if hash, err := bcrypt.GenerateFromPassword([]byte(config.newAdminPassword), bcrypt.DefaultCost); err == nil {
if err := controller.Options.Read(controller.Database); err != nil {
log.Fatal(err)
}
controller.Options.adminPassword = string(hash) controller.Options.adminPassword = string(hash)
controller.Options.adminPasswordNeedChange = config.newAdminPassword == defaults.adminPassword controller.Options.adminPasswordNeedChange = config.newAdminPassword == defaults.adminPassword
@ -68,10 +57,6 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
if err := controller.Options.Read(controller.Database); err != nil {
log.Fatal(err)
}
LogEvent(controller.Database, LogLevelInfo, "admin password changed.") LogEvent(controller.Database, LogLevelInfo, "admin password changed.")
os.Exit(0) os.Exit(0)
@ -84,7 +69,9 @@ func main() {
fmt.Printf("\nRdio Scanner v%s\n", Version) fmt.Printf("\nRdio Scanner v%s\n", Version)
fmt.Printf("----------------------------------\n") fmt.Printf("----------------------------------\n")
LogEvent(controller.Database, LogLevelWarn, "server started") if err := controller.Start(); err != nil {
log.Fatal(err)
}
if h, err := os.Hostname(); err == nil { if h, err := os.Hostname(); err == nil {
hostname = h hostname = h

View file

@ -22,22 +22,17 @@ import (
) )
type Scheduler struct { type Scheduler struct {
initialized bool
cancel chan interface{}
Controller *Controller Controller *Controller
Ticker *time.Ticker Ticker *time.Ticker
cancel chan interface{}
running bool
} }
func (scheduler *Scheduler) Init(controller *Controller) error { func NewScheduler(controller *Controller) *Scheduler {
if scheduler.initialized { return &Scheduler{
return errors.New("scheduler already initialized") Controller: controller,
cancel: make(chan interface{}),
} }
scheduler.Controller = controller
scheduler.cancel = make(chan interface{})
scheduler.initialized = true
return nil
} }
func (scheduler *Scheduler) run() { func (scheduler *Scheduler) run() {
@ -55,14 +50,10 @@ func (scheduler *Scheduler) run() {
} }
func (scheduler *Scheduler) Start() error { func (scheduler *Scheduler) Start() error {
if !scheduler.initialized { if scheduler.running {
return errors.New("scheduler not initialized") return errors.New("scheduler already running")
} } else {
scheduler.running = true
if scheduler.Ticker != nil {
if err := scheduler.Stop(); err != nil {
return err
}
} }
scheduler.Ticker = time.NewTicker(time.Hour) scheduler.Ticker = time.NewTicker(time.Hour)
@ -83,12 +74,13 @@ func (scheduler *Scheduler) Start() error {
} }
func (scheduler *Scheduler) Stop() error { func (scheduler *Scheduler) Stop() error {
if !scheduler.initialized { if !scheduler.running {
return errors.New("scheduler not initialized") return errors.New("scheduler not running")
} }
scheduler.Ticker.Stop() scheduler.Ticker.Stop()
scheduler.Ticker = nil scheduler.Ticker = nil
scheduler.running = false
return nil return nil
} }

View file

@ -283,8 +283,10 @@ func NewSearchResults(searchOptions *SearchOptions, client *Client) (*SearchResu
searchResults.Results = append(searchResults.Results, searchResult) searchResults.Results = append(searchResults.Results, searchResult)
} }
rows.Close()
if err != nil { if err != nil {
err = formatError(err) return nil, formatError(err)
} }
return searchResults, err return searchResults, err

View file

@ -358,11 +358,9 @@ func (systems *Systems) Read(db *Database) error {
*systems = append(*systems, system) *systems = append(*systems, system)
} }
if err != nil { rows.Close()
return formatError(err)
}
if err = rows.Close(); err != nil { if err != nil {
return formatError(err) return formatError(err)
} }
@ -437,12 +435,9 @@ func (systems *Systems) Write(db *Database) error {
} }
} }
if err != nil {
rows.Close() rows.Close()
return formatError(err)
}
if err = rows.Close(); err != nil { if err != nil {
return formatError(err) return formatError(err)
} }

View file

@ -166,11 +166,9 @@ func (tags *Tags) Read(db *Database) error {
*tags = append(*tags, tag) *tags = append(*tags, tag)
} }
if err != nil { rows.Close()
return formatError(err)
}
if err = rows.Close(); err != nil { if err != nil {
return formatError(err) return formatError(err)
} }
@ -227,12 +225,9 @@ func (tags *Tags) Write(db *Database) error {
} }
} }
if err != nil {
rows.Close() rows.Close()
return formatError(err)
}
if err = rows.Close(); err != nil { if err != nil {
return formatError(err) return formatError(err)
} }

View file

@ -15,4 +15,4 @@
package main package main
const Version = "6.0.3" const Version = "6.0.4"