mirror of
https://github.com/chuot/rdio-scanner.git
synced 2026-08-13 16:33:01 -06:00
Version 6.3.4
This commit is contained in:
parent
4d0dbd0e8e
commit
60fcf1e066
|
|
@ -28,6 +28,12 @@ _v6.3.3_
|
|||
- Show the real IP address in the logs taking into account if behind a proxy.
|
||||
- Fix panic when emitting a call to clients.
|
||||
|
||||
_v6.3.4_
|
||||
|
||||
- Fix ffmpeg audio filter not available on older version (issue #189).
|
||||
- Improved logging when run as a service, Windows users can now see these logs in the events viewer.
|
||||
- Dirwatch now catches panic errors and logs them.
|
||||
|
||||
## Version 6.2
|
||||
|
||||
- New max clients options which is 200 by default.
|
||||
|
|
|
|||
16
COMPILING.md
16
COMPILING.md
|
|
@ -27,13 +27,13 @@ Clone the official repository on your computer and start the build process.
|
|||
|
||||
When finished, you will find the precompiled versions for various platforms in the `dist` folder.
|
||||
|
||||
rdio-scanner-darwin-amd64-v6.3.3.zip
|
||||
rdio-scanner-darwin-arm64-v6.3.3.zip
|
||||
rdio-scanner-freebsd-amd64-v6.3.3.zip
|
||||
rdio-scanner-linux-386-v6.3.3.zip
|
||||
rdio-scanner-linux-amd64-v6.3.3.zip
|
||||
rdio-scanner-linux-arm64-v6.3.3.zip
|
||||
rdio-scanner-linux-arm-v6.3.3.zip
|
||||
rdio-scanner-windows-amd64-v6.3.3.zip
|
||||
rdio-scanner-darwin-amd64-v6.3.4.zip
|
||||
rdio-scanner-darwin-arm64-v6.3.4.zip
|
||||
rdio-scanner-freebsd-amd64-v6.3.4.zip
|
||||
rdio-scanner-linux-386-v6.3.4.zip
|
||||
rdio-scanner-linux-amd64-v6.3.4.zip
|
||||
rdio-scanner-linux-arm64-v6.3.4.zip
|
||||
rdio-scanner-linux-arm-v6.3.4.zip
|
||||
rdio-scanner-windows-amd64-v6.3.4.zip
|
||||
|
||||
**Happy Rdio scanning !**
|
||||
4
Makefile
4
Makefile
|
|
@ -16,8 +16,8 @@
|
|||
################################################################################
|
||||
|
||||
app := rdio-scanner
|
||||
date := 2022/05/24
|
||||
ver := 6.3.3
|
||||
date := 2022/05/25
|
||||
ver := 6.3.4
|
||||
|
||||
client := $(wildcard client/*.json client/*.ts)
|
||||
server := $(wildcard server/*.go)
|
||||
|
|
|
|||
4
client/package-lock.json
generated
4
client/package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "rdio-scanner",
|
||||
"version": "6.3.3",
|
||||
"version": "6.3.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "rdio-scanner",
|
||||
"version": "6.3.3",
|
||||
"version": "6.3.4",
|
||||
"license": "LICENSE",
|
||||
"dependencies": {
|
||||
"@angular/animations": "^13.3.9",
|
||||
|
|
|
|||
|
|
@ -38,5 +38,5 @@
|
|||
"build": "ng build --base-href ./ --configuration production",
|
||||
"start": "ng serve"
|
||||
},
|
||||
"version": "6.3.3"
|
||||
"version": "6.3.4"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ export class RdioScannerMainComponent implements OnDestroy, OnInit {
|
|||
} else {
|
||||
this.rdioScannerService.replay()
|
||||
}
|
||||
} else {
|
||||
} else if (this.replayOffset < this.callHistory.length) {
|
||||
this.rdioScannerService.play(this.callHistory[this.replayOffset]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
|
|||
rdio@macos ~ % mkdir rdio-scanner
|
||||
rdio@macos ~ % cd rdio-scanner
|
||||
rdio@macos rdio-scanner % unzip \
|
||||
> ~/Downloads/rdio-scanner-darwin-arm64-v6.3.3.zip
|
||||
Archive: /Users/rdio/Downloads/rdio-scanner-darwin-arm64-v6.3.3.zip
|
||||
> ~/Downloads/rdio-scanner-darwin-arm64-v6.3.4.zip
|
||||
Archive: /Users/rdio/Downloads/rdio-scanner-darwin-arm64-v6.3.4.zip
|
||||
inflating: rdio-scanner
|
||||
inflating: rdio-scanner.pdf
|
||||
|
||||
|
|
@ -51,11 +51,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
|
|||
|
||||
rdio@macos rdio-scanner % ./rdio-scanner
|
||||
|
||||
Rdio Scanner v6.3.3
|
||||
Rdio Scanner v6.3.4
|
||||
----------------------------------
|
||||
2022/05/24 08:38:06 server started
|
||||
2022/05/24 08:38:06 main interface at http://macos.local:3000
|
||||
2022/05/24 08:38:06 admin interface at http://macos.local:3000/admin
|
||||
2022/05/25 08:38:06 server started
|
||||
2022/05/25 08:38:06 main interface at http://macos.local:3000
|
||||
2022/05/25 08:38:06 admin interface at http://macos.local:3000/admin
|
||||
|
||||
4. Access the administrative dashboard to finalize the configuration.
|
||||
|
||||
|
|
@ -77,11 +77,11 @@ Here we want our [Rdio Scanner](https://guthub.com/chuot/rdio-scanner) instance
|
|||
|
||||
rdio@macos rdio-scanner % ./rdio-scanner --listen :80
|
||||
|
||||
Rdio Scanner v6.3.3
|
||||
Rdio Scanner v6.3.4
|
||||
----------------------------------
|
||||
2022/05/24 08:48:03 server started
|
||||
2022/05/24 08:48:03 main interface at http://macos.local
|
||||
2022/05/24 08:48:03 admin interface at http://macos.local/admin
|
||||
2022/05/25 08:48:03 server started
|
||||
2022/05/25 08:48:03 main interface at http://macos.local
|
||||
2022/05/25 08:48:03 admin interface at http://macos.local/admin
|
||||
|
||||
## Listening on a SSL port
|
||||
|
||||
|
|
@ -95,12 +95,12 @@ You can use your own SSL certificates that match your domain name with `-ssl_cer
|
|||
> -ssl_key_file mykey.key \
|
||||
> -ssl_listen :443
|
||||
|
||||
Rdio Scanner v6.3.3
|
||||
Rdio Scanner v6.3.4
|
||||
----------------------------------
|
||||
2022/05/24 08:50:58 server started
|
||||
2022/05/24 08:50:58 main interface at http://macos.local
|
||||
2022/05/24 08:50:58 main interface at https://macos.local
|
||||
2022/05/24 08:50:58 admin interface at https://macos.local/admin
|
||||
2022/05/25 08:50:58 server started
|
||||
2022/05/25 08:50:58 main interface at http://macos.local
|
||||
2022/05/25 08:50:58 main interface at https://macos.local
|
||||
2022/05/25 08:50:58 admin interface at https://macos.local/admin
|
||||
|
||||
If you don't want to worry about SSL certificates, you can use the built-in Let's Encrypt auto-cert feature. This requires that you have both port 80 (HTTP) and port 443 (HTTPS) open to the world. Also, your domain name should point to your IP address where [Rdio Scanner](https://github.com/chuot/rdio-scanner/) is running. The advantage of this approach is that everything is done automatically, no certificate request, no certificate renewal.
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y
|
|||
> -ssl_auto_cert mydomain.com \
|
||||
> -ssl_listen :443 \
|
||||
> -config_save
|
||||
2022/05/24 08:52:24 rdio-scanner.ini file created
|
||||
2022/05/25 08:52:24 rdio-scanner.ini file created
|
||||
|
||||
All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list.
|
||||
|
||||
|
|
@ -132,12 +132,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an
|
|||
|
||||
rdio@macos rdio-scanner % ./rdio-scanner
|
||||
|
||||
Rdio Scanner v6.3.3
|
||||
Rdio Scanner v6.3.4
|
||||
----------------------------------
|
||||
2022/05/24 08:54:08 server started
|
||||
2022/05/24 08:54:08 main interface at http://macos.local
|
||||
2022/05/24 08:54:08 main interface at https://macos.local
|
||||
2022/05/24 08:54:08 admin interface at https://macos.local/admin
|
||||
2022/05/25 08:54:08 server started
|
||||
2022/05/25 08:54:08 main interface at http://macos.local
|
||||
2022/05/25 08:54:08 main interface at https://macos.local
|
||||
2022/05/25 08:54:08 admin interface at https://macos.local/admin
|
||||
|
||||
## Install Rdio Scanner as a service
|
||||
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
|
|||
rdio@pc-freebsd:~ $ mkdir rdio-scanner
|
||||
rdio@pc-freebsd:~ $ cd rdio-scanner
|
||||
rdio@pc-freebsd:~/rdio-scanner $ unzip \
|
||||
> ~/rdio-scanner-freebsd-amd64-v6.3.3.zip
|
||||
Archive: ../rdio-scanner-freebsd-amd64-v6.3.3.zip
|
||||
> ~/rdio-scanner-freebsd-amd64-v6.3.4.zip
|
||||
Archive: ../rdio-scanner-freebsd-amd64-v6.3.4.zip
|
||||
extracting: rdio-scanner
|
||||
extracting: rdio-scanner.pdf
|
||||
|
||||
|
|
@ -51,11 +51,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
|
|||
|
||||
rdio@pc-freebsd:~/rdio-scanner $ ./rdio-scanner
|
||||
|
||||
Rdio Scanner v6.3.3
|
||||
Rdio Scanner v6.3.4
|
||||
----------------------------------
|
||||
2022/05/24 08:16:36 server started
|
||||
2022/05/24 08:16:36 main interface at http://pc-freebsd:3000
|
||||
2022/05/24 08:16:36 admin interface at http://pc-freebsd:3000/admin
|
||||
2022/05/25 08:16:36 server started
|
||||
2022/05/25 08:16:36 main interface at http://pc-freebsd:3000
|
||||
2022/05/25 08:16:36 admin interface at http://pc-freebsd:3000/admin
|
||||
|
||||
4. Access the administrative dashboard to finalize the configuration.
|
||||
|
||||
|
|
@ -81,11 +81,11 @@ Here we want our [Rdio Scanner](https://guthub.com/chuot/rdio-scanner) instance
|
|||
Password:
|
||||
root@pc-freebsd:/home/rdio/rdio-scanner # ./rdio-scanner -listen :80
|
||||
|
||||
Rdio Scanner v6.3.3
|
||||
Rdio Scanner v6.3.4
|
||||
----------------------------------
|
||||
2022/05/24 08:19:38 server started
|
||||
2022/05/24 08:19:38 main interface at http://pc-freebsd
|
||||
2022/05/24 08:19:38 admin interface at http://pc-freebsd/admin
|
||||
2022/05/25 08:19:38 server started
|
||||
2022/05/25 08:19:38 main interface at http://pc-freebsd
|
||||
2022/05/25 08:19:38 admin interface at http://pc-freebsd/admin
|
||||
|
||||
## Listening on a SSL port
|
||||
|
||||
|
|
@ -101,12 +101,12 @@ You can use your own SSL certificates that match your domain name with `-ssl_cer
|
|||
> -ssl_key_file mykey.key \
|
||||
> -ssl_listen :443
|
||||
|
||||
Rdio Scanner v6.3.3
|
||||
Rdio Scanner v6.3.4
|
||||
----------------------------------
|
||||
2022/05/24 10:34:29 server started
|
||||
2022/05/24 10:34:29 main interface at http://pc-freebsd
|
||||
2022/05/24 10:34:29 main interface at https://pc-freebsd
|
||||
2022/05/24 10:34:29 admin interface at https://pc-freebsd/admin
|
||||
2022/05/25 10:34:29 server started
|
||||
2022/05/25 10:34:29 main interface at http://pc-freebsd
|
||||
2022/05/25 10:34:29 main interface at https://pc-freebsd
|
||||
2022/05/25 10:34:29 admin interface at https://pc-freebsd/admin
|
||||
|
||||
If you don't want to worry about SSL certificates, you can use the built-in Let's Encrypt auto-cert feature. This requires that you have both port 80 (HTTP) and port 443 (HTTPS) open to the world. Also, your domain name should point to your IP address where [Rdio Scanner](https://github.com/chuot/rdio-scanner/) is running. The advantage of this approach is that everything is done automatically, no certificate request, no certificate renewal.
|
||||
|
||||
|
|
@ -126,7 +126,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y
|
|||
> -ssl_auto_cert mydomain.com \
|
||||
> -ssl_listen :443 \
|
||||
> -config_save
|
||||
2022/05/24 10:37:00 rdio-scanner.ini file created
|
||||
2022/05/25 10:37:00 rdio-scanner.ini file created
|
||||
|
||||
All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list.
|
||||
|
||||
|
|
@ -142,12 +142,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an
|
|||
Password:
|
||||
root@pc-freebsd:/home/rdio/rdio-scanner # ./rdio-scanner
|
||||
|
||||
Rdio Scanner v6.3.3
|
||||
Rdio Scanner v6.3.4
|
||||
----------------------------------
|
||||
2022/05/24 10:38:28 server started
|
||||
2022/05/24 10:38:29 main interface at http://pc-freebsd
|
||||
2022/05/24 10:38:29 main interface at https://pc-freebsd
|
||||
2022/05/24 10:38:29 admin interface at https://pc-freebsd/admin
|
||||
2022/05/25 10:38:28 server started
|
||||
2022/05/25 10:38:29 main interface at http://pc-freebsd
|
||||
2022/05/25 10:38:29 main interface at https://pc-freebsd
|
||||
2022/05/25 10:38:29 admin interface at https://pc-freebsd/admin
|
||||
|
||||
## Install Rdio Scanner as a service
|
||||
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
|
|||
[rdio@pc-linux ~]$ mkdir rdio-scanner
|
||||
[rdio@pc-linux ~]$ cd rdio-scanner
|
||||
[rdio@pc-linux rdio-scanner]$ unzip \
|
||||
> ~/Downloads/rdio-scanner-linux-amd64-v6.3.3.zip
|
||||
Archive: /home/rdio/Downloads/rdio-scanner-linux-amd64-v6.3.3.zip
|
||||
> ~/Downloads/rdio-scanner-linux-amd64-v6.3.4.zip
|
||||
Archive: /home/rdio/Downloads/rdio-scanner-linux-amd64-v6.3.4.zip
|
||||
inflating: rdio-scanner
|
||||
inflating: rdio-scanner.pdf
|
||||
|
||||
|
|
@ -51,11 +51,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
|
|||
|
||||
[rdio@pc-linux rdio-scanner]$ ./rdio-scanner
|
||||
|
||||
Rdio Scanner v6.3.3
|
||||
Rdio Scanner v6.3.4
|
||||
----------------------------------
|
||||
2022/05/24 09:11:48 server started
|
||||
2022/05/24 09:11:48 main interface at http://pc-linux:3000
|
||||
2022/05/24 09:11:48 admin interface at http://pc-linux:3000/admin
|
||||
2022/05/25 09:11:48 server started
|
||||
2022/05/25 09:11:48 main interface at http://pc-linux:3000
|
||||
2022/05/25 09:11:48 admin interface at http://pc-linux:3000/admin
|
||||
|
||||
4. Access the administrative dashboard to finalize the configuration.
|
||||
|
||||
|
|
@ -78,11 +78,11 @@ Here we want our [Rdio Scanner](https://guthub.com/chuot/rdio-scanner) instance
|
|||
[rdio@pc-linux rdio-scanner]$ sudo ./rdio-scanner -listen :80
|
||||
[sudo] password for rdio:
|
||||
|
||||
Rdio Scanner v6.3.3
|
||||
Rdio Scanner v6.3.4
|
||||
----------------------------------
|
||||
2022/05/24 09:14:00 server started
|
||||
2022/05/24 09:14:00 main interface at http://pc-linux
|
||||
2022/05/24 09:14:00 admin interface at http://pc-linux/admin
|
||||
2022/05/25 09:14:00 server started
|
||||
2022/05/25 09:14:00 main interface at http://pc-linux
|
||||
2022/05/25 09:14:00 admin interface at http://pc-linux/admin
|
||||
|
||||
## Listening on a SSL port
|
||||
|
||||
|
|
@ -97,12 +97,12 @@ You can use your own SSL certificates that match your domain name with `-ssl_cer
|
|||
> -ssl_listen :443
|
||||
[sudo] password for rdio:
|
||||
|
||||
Rdio Scanner v6.3.3
|
||||
Rdio Scanner v6.3.4
|
||||
----------------------------------
|
||||
2022/05/24 09:16:47 server started
|
||||
2022/05/24 09:16:47 main interface at http://pc-linux
|
||||
2022/05/24 09:16:47 main interface at https://pc-linux
|
||||
2022/05/24 09:16:47 admin interface at https://pc-linux/admin
|
||||
2022/05/25 09:16:47 server started
|
||||
2022/05/25 09:16:47 main interface at http://pc-linux
|
||||
2022/05/25 09:16:47 main interface at https://pc-linux
|
||||
2022/05/25 09:16:47 admin interface at https://pc-linux/admin
|
||||
|
||||
If you don't want to worry about SSL certificates, you can use the built-in Let's Encrypt auto-cert feature. This requires that you have both port 80 (HTTP) and port 443 (HTTPS) open to the world. Also, your domain name should point to your IP address where [Rdio Scanner](https://github.com/chuot/rdio-scanner/) is running. The advantage of this approach is that everything is done automatically, no certificate request, no certificate renewal.
|
||||
|
||||
|
|
@ -120,7 +120,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y
|
|||
> -ssl_auto_cert mydomain.com \
|
||||
> -ssl_listen :443 \
|
||||
> -config_save
|
||||
2022/05/24 09:19:29 rdio-scanner.ini file created
|
||||
2022/05/25 09:19:29 rdio-scanner.ini file created
|
||||
|
||||
All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list.
|
||||
|
||||
|
|
@ -135,12 +135,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an
|
|||
[rdio@pc-linux rdio-scanner]$ sudo ./rdio-scanner
|
||||
[sudo] Mot de passe de rdio :
|
||||
|
||||
Rdio Scanner v6.3.3
|
||||
Rdio Scanner v6.3.4
|
||||
----------------------------------
|
||||
2022/05/24 09:20:40 server started
|
||||
2022/05/24 09:20:40 main interface at http://pc-linux
|
||||
2022/05/24 09:20:40 main interface at https://pc-linux
|
||||
2022/05/24 09:20:40 admin interface at https://pc-linux/admin
|
||||
2022/05/25 09:20:40 server started
|
||||
2022/05/25 09:20:40 main interface at http://pc-linux
|
||||
2022/05/25 09:20:40 main interface at https://pc-linux
|
||||
2022/05/25 09:20:40 admin interface at https://pc-linux/admin
|
||||
|
||||
## Install Rdio Scanner as a service
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
|
|||
C:\Users\rdio> cd rdio-scanner
|
||||
|
||||
C:\Users\rdio\rdio-scanner> tar -xvf ^
|
||||
..\downloads\rdio-scanner-windows-amd64-v6.3.3.zip
|
||||
..\downloads\rdio-scanner-windows-amd64-v6.3.4.zip
|
||||
x rdio-scanner.exe
|
||||
x rdio-scanner.pdf
|
||||
|
||||
|
|
@ -52,11 +52,11 @@ ALWAYS DOWNLOAD THE LATEST VERSION OF [RDIO SCANNER](https://github.com/chuot/rd
|
|||
|
||||
C:\Users\rdio\rdio-scanner>rdio-scanner
|
||||
|
||||
Rdio Scanner v6.3.3
|
||||
Rdio Scanner v6.3.4
|
||||
----------------------------------
|
||||
2022/05/24 13:48:48 server started
|
||||
2022/05/24 13:48:48 main interface at http://pc-windows:3000
|
||||
2022/05/24 13:48:48 admin interface at http://pc-windows:3000/admin
|
||||
2022/05/25 13:48:48 server started
|
||||
2022/05/25 13:48:48 main interface at http://pc-windows:3000
|
||||
2022/05/25 13:48:48 admin interface at http://pc-windows:3000/admin
|
||||
|
||||
4. Access the administrative dashboard to finalize the configuration.
|
||||
|
||||
|
|
@ -78,11 +78,11 @@ Here we want our [Rdio Scanner](https://guthub.com/chuot/rdio-scanner) instance
|
|||
|
||||
C:\Users\rdio\rdio-scanner>rdio-scanner -listen :80
|
||||
|
||||
Rdio Scanner v6.3.3
|
||||
Rdio Scanner v6.3.4
|
||||
----------------------------------
|
||||
2022/05/24 10:53:31 server started
|
||||
2022/05/24 10:53:31 main interface at http://pc-windows
|
||||
2022/05/24 10:53:31 admin interface at http://pc-windows/admin
|
||||
2022/05/25 10:53:31 server started
|
||||
2022/05/25 10:53:31 main interface at http://pc-windows
|
||||
2022/05/25 10:53:31 admin interface at http://pc-windows/admin
|
||||
|
||||
## Listening on a SSL port
|
||||
|
||||
|
|
@ -96,12 +96,12 @@ You can use your own SSL certificates that match your domain name with `-ssl_cer
|
|||
-ssl_key_file meykey.key ^
|
||||
-ssl_listen :443
|
||||
|
||||
Rdio Scanner v6.3.3
|
||||
Rdio Scanner v6.3.4
|
||||
----------------------------------
|
||||
2022/05/24 11:05:43 server started
|
||||
2022/05/24 11:05:43 main interface at http://pc-windows
|
||||
2022/05/24 11:05:43 main interface at https://pc-windows
|
||||
2022/05/24 11:05:43 admin interface at https://pc-windows/admin
|
||||
2022/05/25 11:05:43 server started
|
||||
2022/05/25 11:05:43 main interface at http://pc-windows
|
||||
2022/05/25 11:05:43 main interface at https://pc-windows
|
||||
2022/05/25 11:05:43 admin interface at https://pc-windows/admin
|
||||
|
||||
If you don't want to worry about SSL certificates, you can use the built-in Let's Encrypt auto-cert feature. This requires that you have both port 80 (HTTP) and port 443 (HTTPS) open to the world. Also, your domain name should point to your IP address where [Rdio Scanner](https://github.com/chuot/rdio-scanner/) is running. The advantage of this approach is that everything is done automatically, no certificate request, no certificate renewal.
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ You don't want to have to type everytime a long list of arguments. No problem, y
|
|||
-ssl_auto_cert mydomain.com ^
|
||||
-ssl_listen :443 ^
|
||||
-config_save
|
||||
2022/05/24 11:08:28 rdio-scanner.ini file created
|
||||
2022/05/25 11:08:28 rdio-scanner.ini file created
|
||||
|
||||
All of your parameters passed as arguments to [Rdio Scanner](https://github.com/chuot/rdio-scanner) have been saved to an INI file which has the same arguments/values list.
|
||||
|
||||
|
|
@ -133,12 +133,12 @@ Then simply run [Rdio Scanner](https://github.com/chuot/rdio-scanner) without an
|
|||
|
||||
C:\Users\rdio\rdio-scanner>rdio-scanner
|
||||
|
||||
Rdio Scanner v6.3.3
|
||||
Rdio Scanner v6.3.4
|
||||
----------------------------------
|
||||
2022/05/24 11:11:28 server started
|
||||
2022/05/24 11:11:28 main interface at http://pc-windows
|
||||
2022/05/24 11:11:28 main interface at https://pc-windows
|
||||
2022/05/24 11:11:28 admin interface at https://pc-windows/admin
|
||||
2022/05/25 11:11:28 server started
|
||||
2022/05/25 11:11:28 main interface at http://pc-windows
|
||||
2022/05/25 11:11:28 main interface at https://pc-windows
|
||||
2022/05/25 11:11:28 admin interface at https://pc-windows/admin
|
||||
|
||||
\pagebreak{}
|
||||
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ func (admin *Admin) ChangePassword(currentPassword string, newPassword string) e
|
|||
return err
|
||||
}
|
||||
|
||||
admin.Controller.Logs.LogEvent(admin.Controller.Database, LogLevelWarn, "admin password changed.")
|
||||
admin.Controller.Logs.LogEvent(LogLevelWarn, "admin password changed.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -149,7 +149,7 @@ func (admin *Admin) ConfigHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}()
|
||||
|
||||
logError := func(err error) {
|
||||
admin.Controller.Logs.LogEvent(admin.Controller.Database, LogLevelError, fmt.Sprintf("admin.confighandler.put: %s", err.Error()))
|
||||
admin.Controller.Logs.LogEvent(LogLevelError, fmt.Sprintf("admin.confighandler.put: %s", err.Error()))
|
||||
}
|
||||
|
||||
t := admin.GetAuthorization(r)
|
||||
|
|
@ -284,7 +284,7 @@ func (admin *Admin) ConfigHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
admin.SendConfig(w)
|
||||
|
||||
admin.Controller.Logs.LogEvent(admin.Controller.Database, LogLevelWarn, "configuration changed")
|
||||
admin.Controller.Logs.LogEvent(LogLevelWarn, "configuration changed")
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
|
|
@ -394,11 +394,7 @@ func (admin *Admin) LoginHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
if attempt.Count > admin.AttemptsMax || time.Since(attempt.Date) < admin.AttemptsMaxDelay {
|
||||
if attempt.Count == admin.AttemptsMax+1 {
|
||||
admin.Controller.Logs.LogEvent(
|
||||
admin.Controller.Database,
|
||||
LogLevelWarn,
|
||||
fmt.Sprintf("too many login attempts for ip=\"%v\"", remoteAddr),
|
||||
)
|
||||
admin.Controller.Logs.LogEvent(LogLevelWarn, fmt.Sprintf("too many login attempts for ip=\"%v\"", remoteAddr))
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
|
|
@ -417,11 +413,7 @@ func (admin *Admin) LoginHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if !ok {
|
||||
admin.Controller.Logs.LogEvent(
|
||||
admin.Controller.Database,
|
||||
LogLevelWarn,
|
||||
fmt.Sprintf("invalid login attempt for ip=%v", remoteAddr),
|
||||
)
|
||||
admin.Controller.Logs.LogEvent(LogLevelWarn, fmt.Sprintf("invalid login attempt for ip=%v", remoteAddr))
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
|
@ -499,7 +491,7 @@ func (admin *Admin) PasswordHandler(w http.ResponseWriter, r *http.Request) {
|
|||
)
|
||||
|
||||
logError := func(err error) {
|
||||
admin.Controller.Logs.LogEvent(admin.Controller.Database, LogLevelError, fmt.Sprintf("admin.passwordhandler.post: %s", err.Error()))
|
||||
admin.Controller.Logs.LogEvent(LogLevelError, fmt.Sprintf("admin.passwordhandler.post: %s", err.Error()))
|
||||
}
|
||||
|
||||
t := admin.GetAuthorization(r)
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ type Config struct {
|
|||
SslCertFile string
|
||||
SslKeyFile string
|
||||
SslListen string
|
||||
daemon *Daemon
|
||||
newAdminPassword string
|
||||
}
|
||||
|
||||
|
|
@ -179,7 +180,7 @@ func NewConfig() *Config {
|
|||
}
|
||||
|
||||
if *serviceAction != "" {
|
||||
NewDaemon().Control(*serviceAction)
|
||||
config.daemon = NewDaemon().Control(*serviceAction)
|
||||
}
|
||||
|
||||
return config
|
||||
|
|
|
|||
|
|
@ -16,45 +16,40 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
Admin *Admin
|
||||
Api *Api
|
||||
Calls *Calls
|
||||
Config *Config
|
||||
Database *Database
|
||||
Accesses *Accesses
|
||||
Apikeys *Apikeys
|
||||
Dirwatches *Dirwatches
|
||||
Downstreams *Downstreams
|
||||
Groups *Groups
|
||||
Logs *Logs
|
||||
Options *Options
|
||||
Scheduler *Scheduler
|
||||
Systems *Systems
|
||||
Tags *Tags
|
||||
Clients *Clients
|
||||
Register chan *Client
|
||||
Unregister chan *Client
|
||||
Ingest chan *Call
|
||||
ffmpeg bool
|
||||
ffmpegWarned bool
|
||||
ingestMutex sync.Mutex
|
||||
running bool
|
||||
Admin *Admin
|
||||
Api *Api
|
||||
Calls *Calls
|
||||
Config *Config
|
||||
Database *Database
|
||||
Accesses *Accesses
|
||||
Apikeys *Apikeys
|
||||
Dirwatches *Dirwatches
|
||||
Downstreams *Downstreams
|
||||
FFMpeg *FFMpeg
|
||||
Groups *Groups
|
||||
Logs *Logs
|
||||
Options *Options
|
||||
Scheduler *Scheduler
|
||||
Systems *Systems
|
||||
Tags *Tags
|
||||
Clients *Clients
|
||||
Register chan *Client
|
||||
Unregister chan *Client
|
||||
Ingest chan *Call
|
||||
ingestMutex sync.Mutex
|
||||
running bool
|
||||
}
|
||||
|
||||
func NewController(config *Config) *Controller {
|
||||
|
|
@ -65,6 +60,7 @@ func NewController(config *Config) *Controller {
|
|||
Calls: NewCalls(),
|
||||
Dirwatches: NewDirwatches(),
|
||||
Downstreams: NewDownstreams(),
|
||||
FFMpeg: NewFFMpeg(),
|
||||
Groups: NewGroups(),
|
||||
Logs: NewLogs(),
|
||||
Options: NewOptions(),
|
||||
|
|
@ -82,63 +78,12 @@ func NewController(config *Config) *Controller {
|
|||
controller.Database = NewDatabase(config)
|
||||
controller.Scheduler = NewScheduler(controller)
|
||||
|
||||
controller.Logs.setDaemon(config.daemon)
|
||||
controller.Logs.setDatabase(controller.Database)
|
||||
|
||||
return controller
|
||||
}
|
||||
|
||||
func (controller *Controller) ConvertAudio(call *Call) {
|
||||
var (
|
||||
args = []string{"-i", "-"}
|
||||
err error
|
||||
)
|
||||
|
||||
if !controller.ffmpeg {
|
||||
if !controller.ffmpegWarned {
|
||||
controller.ffmpegWarned = true
|
||||
|
||||
controller.Logs.LogEvent(controller.Database, LogLevelWarn, "ffmpeg is not available, no audio conversion will be performed.")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if system, ok := controller.Systems.GetSystem(call.System); ok {
|
||||
if talkgroup, ok := system.Talkgroups.GetTalkgroup(call.Talkgroup); ok {
|
||||
if tag, ok := controller.Tags.GetTag(talkgroup.TagId); ok {
|
||||
args = append(args,
|
||||
"-metadata", fmt.Sprintf("album=%v", talkgroup.Label),
|
||||
"-metadata", fmt.Sprintf("artist=%v", system.Label),
|
||||
"-metadata", fmt.Sprintf("date=%v", call.DateTime),
|
||||
"-metadata", fmt.Sprintf("genre=%v", tag),
|
||||
"-metadata", fmt.Sprintf("title=%v", talkgroup.Name),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
args = append(args, "-af", "apad=whole_dur=3s,loudnorm=I=-16:TP=-1.5:LRA=11", "-c:a", "aac", "-b:a", "32k", "-movflags", "frag_keyframe+empty_moov", "-f", "ipod", "-")
|
||||
|
||||
cmd := exec.Command("ffmpeg", args...)
|
||||
cmd.Stdin = bytes.NewReader(call.Audio)
|
||||
|
||||
stdout := bytes.NewBuffer([]byte(nil))
|
||||
cmd.Stdout = stdout
|
||||
|
||||
stderr := bytes.NewBuffer([]byte(nil))
|
||||
cmd.Stderr = stderr
|
||||
|
||||
if err = cmd.Run(); err == nil {
|
||||
call.Audio = stdout.Bytes()
|
||||
call.AudioType = "audio/mp4"
|
||||
|
||||
switch v := call.AudioName.(type) {
|
||||
case string:
|
||||
call.AudioName = fmt.Sprintf("%v.m4a", strings.TrimSuffix(v, path.Ext((v))))
|
||||
}
|
||||
|
||||
} else {
|
||||
fmt.Println(stderr.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (controller *Controller) EmitCall(call *Call) {
|
||||
controller.Clients.EmitCall(call, controller.Accesses.IsRestricted())
|
||||
controller.Downstreams.Send(controller, call)
|
||||
|
|
@ -169,15 +114,11 @@ func (controller *Controller) IngestCall(call *Call) {
|
|||
defer controller.IngestUnlock()
|
||||
|
||||
logCall := func(call *Call, level string, message string) {
|
||||
controller.Logs.LogEvent(
|
||||
controller.Database,
|
||||
level,
|
||||
fmt.Sprintf("newcall: system=%v talkgroup=%v file=%v %v", call.System, call.Talkgroup, call.AudioName, message),
|
||||
)
|
||||
controller.Logs.LogEvent(level, fmt.Sprintf("newcall: system=%v talkgroup=%v file=%v %v", call.System, call.Talkgroup, call.AudioName, message))
|
||||
}
|
||||
|
||||
logError := func(err error) {
|
||||
controller.Logs.LogEvent(controller.Database, LogLevelError, fmt.Sprintf("controller.ingestcall: %v", err.Error()))
|
||||
controller.Logs.LogEvent(LogLevelError, fmt.Sprintf("controller.ingestcall: %v", err.Error()))
|
||||
}
|
||||
|
||||
if system, ok = controller.Systems.GetSystem(call.System); ok {
|
||||
|
|
@ -346,7 +287,9 @@ func (controller *Controller) IngestCall(call *Call) {
|
|||
}
|
||||
|
||||
if !controller.Options.DisableAudioConversion {
|
||||
controller.ConvertAudio(call)
|
||||
if err := controller.FFMpeg.Convert(call, controller.Systems, controller.Tags); err != nil {
|
||||
controller.Logs.LogEvent(LogLevelWarn, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if id, err = controller.Calls.WriteCall(call, controller.Database); err == nil {
|
||||
|
|
@ -385,11 +328,7 @@ func (controller *Controller) IngestUnlock() {
|
|||
}
|
||||
|
||||
func (controller *Controller) LogClientsCount() {
|
||||
controller.Logs.LogEvent(
|
||||
controller.Database,
|
||||
LogLevelInfo,
|
||||
fmt.Sprintf("listeners count is %v", controller.Clients.Count()),
|
||||
)
|
||||
controller.Logs.LogEvent(LogLevelInfo, fmt.Sprintf("listeners count is %v", controller.Clients.Count()))
|
||||
}
|
||||
|
||||
func (controller *Controller) ProcessMessage(client *Client, message *Message) error {
|
||||
|
|
@ -494,31 +433,19 @@ func (controller *Controller) ProcessMessageCommandPin(client *Client, message *
|
|||
if access, ok := controller.Accesses.GetAccess(code); ok {
|
||||
client.Access = access
|
||||
} else {
|
||||
controller.Logs.LogEvent(
|
||||
controller.Database,
|
||||
LogLevelWarn,
|
||||
fmt.Sprintf("invalid access code=\"%s\" address=\"%s\"", code, client.GetRemoteAddr()),
|
||||
)
|
||||
controller.Logs.LogEvent(LogLevelWarn, fmt.Sprintf("invalid access code=\"%s\" address=\"%s\"", code, client.GetRemoteAddr()))
|
||||
client.Send <- &Message{Command: MessageCommandPin}
|
||||
return nil
|
||||
}
|
||||
|
||||
if client.AuthCount == maxAuthCount {
|
||||
controller.Logs.LogEvent(
|
||||
controller.Database,
|
||||
LogLevelWarn,
|
||||
fmt.Sprintf("access ident=\"%s\" locked", client.Access.Ident),
|
||||
)
|
||||
controller.Logs.LogEvent(LogLevelWarn, fmt.Sprintf("access ident=\"%s\" locked", client.Access.Ident))
|
||||
client.Send <- &Message{Command: MessageCommandPin}
|
||||
return nil
|
||||
}
|
||||
|
||||
if client.Access.HasExpired() {
|
||||
controller.Logs.LogEvent(
|
||||
controller.Database,
|
||||
LogLevelWarn,
|
||||
fmt.Sprintf("access ident=\"%s\" expired", client.Access.Ident),
|
||||
)
|
||||
controller.Logs.LogEvent(LogLevelWarn, fmt.Sprintf("access ident=\"%s\" expired", client.Access.Ident))
|
||||
client.Send <- &Message{Command: MessageCommandExpired}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -526,11 +453,7 @@ func (controller *Controller) ProcessMessageCommandPin(client *Client, message *
|
|||
switch v := client.Access.Limit.(type) {
|
||||
case uint:
|
||||
if controller.Clients.AccessCount(client) > int(v) {
|
||||
controller.Logs.LogEvent(
|
||||
controller.Database,
|
||||
LogLevelWarn,
|
||||
fmt.Sprintf("access ident=\"%s\" too many concurrent connections, limit is %d", client.Access.Ident, client.Access.Limit),
|
||||
)
|
||||
controller.Logs.LogEvent(LogLevelWarn, fmt.Sprintf("access ident=\"%s\" too many concurrent connections, limit is %d", client.Access.Ident, client.Access.Limit))
|
||||
client.Send <- &Message{Command: MessageCommandMax}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -554,7 +477,7 @@ func (controller *Controller) Start() error {
|
|||
controller.running = true
|
||||
}
|
||||
|
||||
controller.Logs.LogEvent(controller.Database, LogLevelWarn, "server started")
|
||||
controller.Logs.LogEvent(LogLevelWarn, "server started")
|
||||
|
||||
if len(controller.Config.BaseDir) > 0 {
|
||||
log.Printf("base folder is %s\n", controller.Config.BaseDir)
|
||||
|
|
@ -592,14 +515,6 @@ func (controller *Controller) Start() error {
|
|||
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)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ type Daemon struct {
|
|||
Errors chan error
|
||||
Interface service.Interface
|
||||
Service service.Service
|
||||
Logger service.Logger
|
||||
}
|
||||
|
||||
func NewDaemon() *Daemon {
|
||||
|
|
@ -61,12 +62,20 @@ func NewDaemon() *Daemon {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if d.Logger, err = d.Service.Logger(nil); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return &d
|
||||
}
|
||||
|
||||
func (d *Daemon) Control(action string) *Daemon {
|
||||
if action == "run" {
|
||||
go d.Service.Run()
|
||||
go func() {
|
||||
if err := d.Service.Run(); err != nil {
|
||||
d.Logger.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
} else if err := service.Control(d.Service, action); err == nil {
|
||||
os.Exit(0)
|
||||
|
|
|
|||
|
|
@ -142,11 +142,7 @@ func (dirwatch *Dirwatch) Ingest(p string) {
|
|||
}
|
||||
|
||||
if err != nil {
|
||||
dirwatch.controller.Logs.LogEvent(
|
||||
dirwatch.controller.Database,
|
||||
LogLevelError,
|
||||
fmt.Sprintf("dirwatch.ingest: %v", err.Error()),
|
||||
)
|
||||
dirwatch.controller.Logs.LogEvent(LogLevelError, fmt.Sprintf("dirwatch.ingest: %v", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -524,9 +520,8 @@ func (dirwatch *Dirwatch) parseMask(call *Call) {
|
|||
|
||||
func (dirwatch *Dirwatch) Start(controller *Controller) error {
|
||||
var (
|
||||
delay time.Duration
|
||||
err error
|
||||
timers = map[string]*time.Timer{}
|
||||
delay time.Duration
|
||||
err error
|
||||
)
|
||||
|
||||
if dirwatch.Disabled {
|
||||
|
|
@ -552,8 +547,10 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
|
|||
}
|
||||
|
||||
go func() {
|
||||
var timers = map[string]*time.Timer{}
|
||||
|
||||
logError := func(err error) {
|
||||
controller.Logs.LogEvent(controller.Database, LogLevelError, fmt.Sprintf("dirwatch.watcher: %v", err.Error()))
|
||||
controller.Logs.LogEvent(LogLevelError, fmt.Sprintf("dirwatch.watcher: %v", err.Error()))
|
||||
}
|
||||
|
||||
newTimer := func(eventName string) *time.Timer {
|
||||
|
|
@ -571,6 +568,11 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
|
|||
timers[k].Stop()
|
||||
delete(timers, k)
|
||||
}
|
||||
|
||||
switch v := recover().(type) {
|
||||
case error:
|
||||
controller.Logs.LogEvent(LogLevelError, v.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
|
|
@ -625,6 +627,13 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
|
|||
}()
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
switch v := recover().(type) {
|
||||
case error:
|
||||
controller.Logs.LogEvent(LogLevelError, v.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(delay)
|
||||
|
||||
if err := fs.WalkDir(os.DirFS(dirwatch.Directory), ".", func(p string, d fs.DirEntry, err error) error {
|
||||
|
|
@ -640,11 +649,7 @@ func (dirwatch *Dirwatch) Start(controller *Controller) error {
|
|||
|
||||
return err
|
||||
}); err != nil {
|
||||
controller.Logs.LogEvent(
|
||||
controller.Database,
|
||||
LogLevelError,
|
||||
fmt.Sprintf("dirwatch.walkdir: %s", err.Error()),
|
||||
)
|
||||
controller.Logs.LogEvent(LogLevelError, fmt.Sprintf("dirwatch.walkdir: %s", err.Error()))
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
@ -776,11 +781,7 @@ func (dirwatches *Dirwatches) Read(db *Database) error {
|
|||
func (dirwatches *Dirwatches) Start(controller *Controller) {
|
||||
for i := range dirwatches.List {
|
||||
if err := dirwatches.List[i].Start(controller); err != nil {
|
||||
controller.Logs.LogEvent(
|
||||
controller.Database,
|
||||
LogLevelError,
|
||||
fmt.Sprintf("dirwatches.start: %s", err.Error()),
|
||||
)
|
||||
controller.Logs.LogEvent(LogLevelError, fmt.Sprintf("dirwatches.start: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -440,11 +440,7 @@ func (downstreams *Downstreams) Read(db *Database) error {
|
|||
func (downstreams *Downstreams) Send(controller *Controller, call *Call) {
|
||||
for _, downstream := range downstreams.List {
|
||||
logEvent := func(logLevel string, message string) {
|
||||
controller.Logs.LogEvent(
|
||||
controller.Database,
|
||||
logLevel,
|
||||
fmt.Sprintf("downstream: system=%v talkgroup=%v file=%v to %v %v", call.System, call.Talkgroup, call.AudioName, downstream.Url, message),
|
||||
)
|
||||
controller.Logs.LogEvent(logLevel, fmt.Sprintf("downstream: system=%v talkgroup=%v file=%v to %v %v", call.System, call.Talkgroup, call.AudioName, downstream.Url, message))
|
||||
}
|
||||
|
||||
if downstream.HasAccess(call) {
|
||||
|
|
|
|||
122
server/ffmpeg.go
Normal file
122
server/ffmpeg.go
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
// Copyright (C) 2019-2022 Chrystian Huot <chrystian.huot@saubeo.solutions>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type FFMpeg struct {
|
||||
available bool
|
||||
version43 bool
|
||||
warned bool
|
||||
}
|
||||
|
||||
func NewFFMpeg() *FFMpeg {
|
||||
ffmpeg := &FFMpeg{}
|
||||
|
||||
stdout := bytes.NewBuffer([]byte(nil))
|
||||
|
||||
cmd := exec.Command("ffmpeg", "-version")
|
||||
cmd.Stdout = stdout
|
||||
|
||||
if err := cmd.Run(); err == nil {
|
||||
ffmpeg.available = true
|
||||
|
||||
if l, err := stdout.ReadString('\n'); err == nil {
|
||||
s := regexp.MustCompile(`.*ffmpeg version .{0,1}([0-9])\.([0-9])\.[0-9].*`).ReplaceAllString(strings.TrimSuffix(l, "\n"), "$1.$2")
|
||||
v := strings.Split(s, ".")
|
||||
if len(v) > 1 {
|
||||
if major, err := strconv.Atoi(v[0]); err == nil {
|
||||
if minor, err := strconv.Atoi(v[1]); err == nil {
|
||||
if major >= 4 || (major == 4 && minor >= 3) {
|
||||
ffmpeg.version43 = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ffmpeg
|
||||
}
|
||||
|
||||
func (ffmpeg *FFMpeg) Convert(call *Call, systems *Systems, tags *Tags) error {
|
||||
var (
|
||||
args = []string{"-i", "-"}
|
||||
err error
|
||||
)
|
||||
|
||||
if !ffmpeg.available {
|
||||
if !ffmpeg.warned {
|
||||
ffmpeg.warned = true
|
||||
|
||||
return errors.New("ffmpeg is not available, no audio conversion will be performed.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if system, ok := systems.GetSystem(call.System); ok {
|
||||
if talkgroup, ok := system.Talkgroups.GetTalkgroup(call.Talkgroup); ok {
|
||||
if tag, ok := tags.GetTag(talkgroup.TagId); ok {
|
||||
args = append(args,
|
||||
"-metadata", fmt.Sprintf("album=%v", talkgroup.Label),
|
||||
"-metadata", fmt.Sprintf("artist=%v", system.Label),
|
||||
"-metadata", fmt.Sprintf("date=%v", call.DateTime),
|
||||
"-metadata", fmt.Sprintf("genre=%v", tag),
|
||||
"-metadata", fmt.Sprintf("title=%v", talkgroup.Name),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ffmpeg.version43 {
|
||||
args = append(args, "-af", "apad=whole_dur=3s,loudnorm=I=-16:TP=-1.5:LRA=11")
|
||||
}
|
||||
|
||||
args = append(args, "-c:a", "aac", "-b:a", "32k", "-movflags", "frag_keyframe+empty_moov", "-f", "ipod", "-")
|
||||
|
||||
cmd := exec.Command("ffmpeg", args...)
|
||||
cmd.Stdin = bytes.NewReader(call.Audio)
|
||||
|
||||
stdout := bytes.NewBuffer([]byte(nil))
|
||||
cmd.Stdout = stdout
|
||||
|
||||
stderr := bytes.NewBuffer([]byte(nil))
|
||||
cmd.Stderr = stderr
|
||||
|
||||
if err = cmd.Run(); err == nil {
|
||||
call.Audio = stdout.Bytes()
|
||||
call.AudioType = "audio/mp4"
|
||||
|
||||
switch v := call.AudioName.(type) {
|
||||
case string:
|
||||
call.AudioName = fmt.Sprintf("%v.m4a", strings.TrimSuffix(v, path.Ext((v))))
|
||||
}
|
||||
|
||||
} else {
|
||||
fmt.Println(stderr.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -38,7 +38,9 @@ type Log struct {
|
|||
}
|
||||
|
||||
type Logs struct {
|
||||
mutex sync.Mutex
|
||||
database *Database
|
||||
mutex sync.Mutex
|
||||
daemon *Daemon
|
||||
}
|
||||
|
||||
func NewLogs() *Logs {
|
||||
|
|
@ -47,20 +49,34 @@ func NewLogs() *Logs {
|
|||
}
|
||||
}
|
||||
|
||||
func (logs *Logs) LogEvent(db *Database, level string, message string) error {
|
||||
func (logs *Logs) LogEvent(level string, message string) error {
|
||||
logs.mutex.Lock()
|
||||
defer logs.mutex.Unlock()
|
||||
|
||||
log.Println(message)
|
||||
if logs.daemon != nil {
|
||||
switch level {
|
||||
case LogLevelError:
|
||||
logs.daemon.Logger.Error(message)
|
||||
case LogLevelWarn:
|
||||
logs.daemon.Logger.Warning(message)
|
||||
case LogLevelInfo:
|
||||
logs.daemon.Logger.Info(message)
|
||||
}
|
||||
|
||||
l := Log{
|
||||
DateTime: time.Now().UTC(),
|
||||
Level: level,
|
||||
Message: message,
|
||||
} else {
|
||||
log.Println(message)
|
||||
}
|
||||
|
||||
if _, err := db.Sql.Exec("insert into `rdioScannerLogs` (`dateTime`, `level`, `message`) values (?, ?, ?)", l.DateTime, l.Level, l.Message); err != nil {
|
||||
return fmt.Errorf("logs.logevent: %v", err)
|
||||
if logs.database != nil {
|
||||
l := Log{
|
||||
DateTime: time.Now().UTC(),
|
||||
Level: level,
|
||||
Message: message,
|
||||
}
|
||||
|
||||
if _, err := logs.database.Sql.Exec("insert into `rdioScannerLogs` (`dateTime`, `level`, `message`) values (?, ?, ?)", l.DateTime, l.Level, l.Message); err != nil {
|
||||
return fmt.Errorf("logs.logevent: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -219,6 +235,14 @@ func (logs *Logs) Search(searchOptions *LogsSearchOptions, db *Database) (*LogsS
|
|||
return logResults, nil
|
||||
}
|
||||
|
||||
func (logs *Logs) setDaemon(d *Daemon) {
|
||||
logs.daemon = d
|
||||
}
|
||||
|
||||
func (logs *Logs) setDatabase(d *Database) {
|
||||
logs.database = d
|
||||
}
|
||||
|
||||
type LogsSearchOptions struct {
|
||||
Date interface{} `json:"date,omitempty"`
|
||||
Level interface{} `json:"level,omitempty"`
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -60,7 +61,7 @@ func main() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
controller.Logs.LogEvent(controller.Database, LogLevelInfo, "admin password changed.")
|
||||
controller.Logs.LogEvent(LogLevelInfo, "admin password changed.")
|
||||
|
||||
os.Exit(0)
|
||||
|
||||
|
|
@ -247,14 +248,16 @@ func main() {
|
|||
}
|
||||
|
||||
func GetRemoteAddr(r *http.Request) string {
|
||||
re := regexp.MustCompile(`(\[[^\]]+\]|[^:]+):.*`)
|
||||
|
||||
for _, addr := range strings.Split(r.Header.Get("X-Forwarded-For"), ",") {
|
||||
if ip := strings.Split(addr, ":"); len(ip[0]) > 0 {
|
||||
return ip[0]
|
||||
if ip := re.ReplaceAllString(addr, "$1"); len(ip) > 0 {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
|
||||
if ip := strings.Split(r.RemoteAddr, ":"); len(ip[0]) > 0 {
|
||||
return ip[0]
|
||||
if ip := re.ReplaceAllString(r.RemoteAddr, "$1"); len(ip) > 0 {
|
||||
return ip
|
||||
}
|
||||
|
||||
return "unknown"
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func (scheduler *Scheduler) pruneDatabase() error {
|
|||
scheduler.Controller.IngestLock()
|
||||
defer scheduler.Controller.IngestUnlock()
|
||||
|
||||
scheduler.Controller.Logs.LogEvent(scheduler.Controller.Database, LogLevelInfo, "database pruning")
|
||||
scheduler.Controller.Logs.LogEvent(LogLevelInfo, "database pruning")
|
||||
|
||||
if err := scheduler.Controller.Calls.Prune(scheduler.Controller.Database, scheduler.Controller.Options.PruneDays); err != nil {
|
||||
return err
|
||||
|
|
@ -63,11 +63,7 @@ func (scheduler *Scheduler) run() {
|
|||
defer scheduler.mutex.Unlock()
|
||||
|
||||
logError := func(err error) {
|
||||
scheduler.Controller.Logs.LogEvent(
|
||||
scheduler.Controller.Database,
|
||||
LogLevelError,
|
||||
fmt.Sprintf("scheduler.run: %s", err.Error()),
|
||||
)
|
||||
scheduler.Controller.Logs.LogEvent(LogLevelError, fmt.Sprintf("scheduler.run: %s", err.Error()))
|
||||
}
|
||||
|
||||
if err := scheduler.pruneDatabase(); err != nil {
|
||||
|
|
|
|||
|
|
@ -15,4 +15,4 @@
|
|||
|
||||
package main
|
||||
|
||||
const Version = "6.3.3"
|
||||
const Version = "6.3.4"
|
||||
|
|
|
|||
Loading…
Reference in a new issue