Initial import: sources, staging packages, build scripts and CI workflow
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
name: Build and Publish Debian Packages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
paths:
|
||||
- 'sources/**'
|
||||
- 'buildpackages.sh'
|
||||
- '.gitea/workflows/**'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Packages
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dpkg-dev rsync
|
||||
|
||||
- name: Build all packages
|
||||
run: |
|
||||
chmod +x buildpackages.sh
|
||||
./buildpackages.sh all
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: debian-packages
|
||||
path: staging/*.deb
|
||||
|
||||
publish:
|
||||
name: Publish to eendoe repository
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download built artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: debian-packages
|
||||
path: staging
|
||||
|
||||
- name: Deploy packages to eendoe server
|
||||
env:
|
||||
SSH_KEY: ${{ secrets.EENDOE_SSH_KEY }}
|
||||
REMOTE_HOST: "eendoe.nl"
|
||||
REMOTE_USER: "bart"
|
||||
REMOTE_DIR: "/sites/repository/staging/"
|
||||
run: |
|
||||
if [ -n "$SSH_KEY" ]; then
|
||||
mkdir -p ~/.ssh
|
||||
echo "$SSH_KEY" > ~/.ssh/id_rsa
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
ssh-keyscan -H "$REMOTE_HOST" >> ~/.ssh/known_hosts 2>/dev/null
|
||||
rsync -avz staging/*.deb "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}"
|
||||
echo "Successfully deployed packages to $REMOTE_HOST:$REMOTE_DIR"
|
||||
else
|
||||
echo "Secret EENDOE_SSH_KEY not configured. Skipping automated remote sync."
|
||||
fi
|
||||
@@ -0,0 +1,5 @@
|
||||
.DS_Store
|
||||
*.swp
|
||||
*~
|
||||
# Exclude locally generated deb files in source folders
|
||||
sources/*/*.deb
|
||||
@@ -0,0 +1,26 @@
|
||||
# package-repo
|
||||
|
||||
Debian / APT package repository bronbestanden en build tools voor Eendoe.
|
||||
|
||||
## Inhoud
|
||||
|
||||
- **`sources/`**: Bronbestanden, control definities en packaging scripts voor de te bouwen packages:
|
||||
- `eendoe_repository`: Repository configuratie package (`/etc/apt/sources.list.d/`)
|
||||
- `eendoe_hyperdeck`: Hyperdeck Python service en systemd unit
|
||||
- `eendoe_srs`: SRS streaming server package en systemd unit
|
||||
- **`staging/`**: Bestaande en nieuw gebouwde `.deb` packages (exclusief Oracle database packages).
|
||||
- **`binaries/`**: Publiek geserveerde repository bestanden (`pool/`, `dists/`).
|
||||
- **`buildpackages.sh`**: Script om één of alle packages te bouwen (`./buildpackages.sh [all|<package_name>]`).
|
||||
- **`.gitea/workflows/build-and-publish.yaml`**: Gitea Actions CI/CD workflow voor het automatisch bouwen en publiceren van packages naar de eendoe server.
|
||||
|
||||
## Bouwen
|
||||
|
||||
Om alle packages lokaal te bouwen:
|
||||
```bash
|
||||
./buildpackages.sh all
|
||||
```
|
||||
|
||||
Om een specifiek package te bouwen:
|
||||
```bash
|
||||
./buildpackages.sh eendoe_hyperdeck
|
||||
```
|
||||
Binary file not shown.
@@ -0,0 +1,57 @@
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA256
|
||||
|
||||
Origin: . stable
|
||||
Label: . stable
|
||||
Suite: stable
|
||||
Codename: stable
|
||||
Date: Wed, 27 Dec 2023 15:09:09 UTC
|
||||
Architectures: amd64
|
||||
Components: main
|
||||
Description: Generated by aptly
|
||||
MD5Sum:
|
||||
cf5f351b126aaab2bf6ca97c13b378c5 3405449 Contents-amd64
|
||||
9f23a2449f3abc4c6fccecfd01fd86e3 152525 Contents-amd64.gz
|
||||
cf5f351b126aaab2bf6ca97c13b378c5 3405449 main/Contents-amd64
|
||||
9f23a2449f3abc4c6fccecfd01fd86e3 152525 main/Contents-amd64.gz
|
||||
f8778b24beb2988de929e2d59d1f4c58 2761 main/binary-amd64/Packages
|
||||
a9ae692d17b8e39db0555ab28ca1207d 1357 main/binary-amd64/Packages.bz2
|
||||
9a64097df252b063fd82b0127ffc57ae 1268 main/binary-amd64/Packages.gz
|
||||
1a863a05e2aa38a0db0bf785722d7fae 99 main/binary-amd64/Release
|
||||
SHA1:
|
||||
5fe9dd2d9aa59f1f24de2ca35728e05db5f74655 3405449 Contents-amd64
|
||||
3913d6a2268e0599ee051d4a267b70a4740c03fa 152525 Contents-amd64.gz
|
||||
5fe9dd2d9aa59f1f24de2ca35728e05db5f74655 3405449 main/Contents-amd64
|
||||
3913d6a2268e0599ee051d4a267b70a4740c03fa 152525 main/Contents-amd64.gz
|
||||
7236da7bf2d82d5b66a9305d78f7ec7876792c71 2761 main/binary-amd64/Packages
|
||||
a2ed1b4117915d2349051ada9ddc089113aa7803 1357 main/binary-amd64/Packages.bz2
|
||||
a628182db07c468ccf6236597693c3624a0eb9f8 1268 main/binary-amd64/Packages.gz
|
||||
279ffa18634b8d6c311595f6e6db2c1b2324b3ef 99 main/binary-amd64/Release
|
||||
SHA256:
|
||||
0ec939a6371353e8ba850811ecba0bd6749ecf5ad4e6ffe4d3f4d48ed68dc8ee 3405449 Contents-amd64
|
||||
915bdf7b19675b1eb09b22f0aada96d0d1557fa9ed70a3e71db197c71376db02 152525 Contents-amd64.gz
|
||||
0ec939a6371353e8ba850811ecba0bd6749ecf5ad4e6ffe4d3f4d48ed68dc8ee 3405449 main/Contents-amd64
|
||||
915bdf7b19675b1eb09b22f0aada96d0d1557fa9ed70a3e71db197c71376db02 152525 main/Contents-amd64.gz
|
||||
f2197b98eadd153d364ff7948bca222763f70e18b76a963b504c1ebbbeb0f2b7 2761 main/binary-amd64/Packages
|
||||
7dc41b5f359d26cb8c1bcc4b512c94c5db1b402efb15e76d5d0d23625cdca933 1357 main/binary-amd64/Packages.bz2
|
||||
64f1a3fd43c28053b3c2301a123b4cf6dc5a1370a94463d861fab3856554366e 1268 main/binary-amd64/Packages.gz
|
||||
101f8d561bb536f600918ab98ffb742828149f0d90ed0b007b7b761e9ad4f8a2 99 main/binary-amd64/Release
|
||||
SHA512:
|
||||
ec29ceab750bab737fbbcc9f00b74c334d0e1ea2f62c0081ed9b695d76bac1296f7560b2ee80361c9f8ed2b538a6adad21d8d3b55200fa71096ac09cd6c7eebd 3405449 Contents-amd64
|
||||
69bbc10540b2bb90941dd2b6804544c91fd335e91f7674f50b2d32312257b997f34ec5d0147d69a16164e41e2bdbdc008a74c863b6de0c42c15eba062c540efa 152525 Contents-amd64.gz
|
||||
ec29ceab750bab737fbbcc9f00b74c334d0e1ea2f62c0081ed9b695d76bac1296f7560b2ee80361c9f8ed2b538a6adad21d8d3b55200fa71096ac09cd6c7eebd 3405449 main/Contents-amd64
|
||||
69bbc10540b2bb90941dd2b6804544c91fd335e91f7674f50b2d32312257b997f34ec5d0147d69a16164e41e2bdbdc008a74c863b6de0c42c15eba062c540efa 152525 main/Contents-amd64.gz
|
||||
19efa178a219564af52c8eff763c820d973a9f00f43a6fd5d1599179661b025a043da630874ba56814ca43608d68aa2e555bb5f303a3d84deb1412c4c8923ff6 2761 main/binary-amd64/Packages
|
||||
87099e5b28836adde21419be72bc16bbb6b02bd901f8372df2de3543d03f8de9ff58fd058ef7bc90a7bafc9b8d4c38fdd25c507b788b8054d79d448c97ef32d7 1357 main/binary-amd64/Packages.bz2
|
||||
dc706a07d45f70d0bf645c4d65e1b6b235a66dfa294f8a77825b1395cf40aa3a2ed097f7092e4635c99ff231fc9f44852d0b91ed09fbbd1cc8a78bb758afc2e7 1268 main/binary-amd64/Packages.gz
|
||||
fd75f52e3d8ab190e919a646d1ccc84e447b32e45691f0f9f65c3ac5ac806f024f7edef6e0c3c9e2d19a5f2621dcd3744f684d9b87a7cdc493b0eb55e7a0b393 99 main/binary-amd64/Release
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
wsBcBAEBCAAQBQJljD4VCRBWSZ+SBjn1cQAANDUIAKjuNqznCTWjUgwAdRkmCLyb
|
||||
HzmxTbtSfc4Eguri0QsUCQg7FA5nlupNketz2NNJ3TIkPJ7KVHAvcYdifnDlL0k2
|
||||
WQMRC4L94jxYSvRnMj4f+/pWt1UxHs5wCHGaojvf8s9ZzSTme52g3MLtikQCEpgv
|
||||
zqIgSH4bzAJu0fc5Je1qRpxiwATyf7B7vC276XSWkDIxDZBUSupLoZNua0NdZ7mh
|
||||
cTXyFIexrWOJk3xJjKfcTQZEB+RxVZk/vSADAnaVKo/YuzBoSx2A1HEwVDOPc4TD
|
||||
vwPIfM9LDoS6YrN4mKDvkjlUtgds/5rXcOVxEQAAaG/md5RvRTOAFbpBKs/pbZc=
|
||||
=2HIC
|
||||
-----END PGP SIGNATURE-----
|
||||
@@ -0,0 +1,44 @@
|
||||
Origin: . stable
|
||||
Label: . stable
|
||||
Suite: stable
|
||||
Codename: stable
|
||||
Date: Wed, 27 Dec 2023 15:09:09 UTC
|
||||
Architectures: amd64
|
||||
Components: main
|
||||
Description: Generated by aptly
|
||||
MD5Sum:
|
||||
cf5f351b126aaab2bf6ca97c13b378c5 3405449 Contents-amd64
|
||||
9f23a2449f3abc4c6fccecfd01fd86e3 152525 Contents-amd64.gz
|
||||
cf5f351b126aaab2bf6ca97c13b378c5 3405449 main/Contents-amd64
|
||||
9f23a2449f3abc4c6fccecfd01fd86e3 152525 main/Contents-amd64.gz
|
||||
f8778b24beb2988de929e2d59d1f4c58 2761 main/binary-amd64/Packages
|
||||
a9ae692d17b8e39db0555ab28ca1207d 1357 main/binary-amd64/Packages.bz2
|
||||
9a64097df252b063fd82b0127ffc57ae 1268 main/binary-amd64/Packages.gz
|
||||
1a863a05e2aa38a0db0bf785722d7fae 99 main/binary-amd64/Release
|
||||
SHA1:
|
||||
5fe9dd2d9aa59f1f24de2ca35728e05db5f74655 3405449 Contents-amd64
|
||||
3913d6a2268e0599ee051d4a267b70a4740c03fa 152525 Contents-amd64.gz
|
||||
5fe9dd2d9aa59f1f24de2ca35728e05db5f74655 3405449 main/Contents-amd64
|
||||
3913d6a2268e0599ee051d4a267b70a4740c03fa 152525 main/Contents-amd64.gz
|
||||
7236da7bf2d82d5b66a9305d78f7ec7876792c71 2761 main/binary-amd64/Packages
|
||||
a2ed1b4117915d2349051ada9ddc089113aa7803 1357 main/binary-amd64/Packages.bz2
|
||||
a628182db07c468ccf6236597693c3624a0eb9f8 1268 main/binary-amd64/Packages.gz
|
||||
279ffa18634b8d6c311595f6e6db2c1b2324b3ef 99 main/binary-amd64/Release
|
||||
SHA256:
|
||||
0ec939a6371353e8ba850811ecba0bd6749ecf5ad4e6ffe4d3f4d48ed68dc8ee 3405449 Contents-amd64
|
||||
915bdf7b19675b1eb09b22f0aada96d0d1557fa9ed70a3e71db197c71376db02 152525 Contents-amd64.gz
|
||||
0ec939a6371353e8ba850811ecba0bd6749ecf5ad4e6ffe4d3f4d48ed68dc8ee 3405449 main/Contents-amd64
|
||||
915bdf7b19675b1eb09b22f0aada96d0d1557fa9ed70a3e71db197c71376db02 152525 main/Contents-amd64.gz
|
||||
f2197b98eadd153d364ff7948bca222763f70e18b76a963b504c1ebbbeb0f2b7 2761 main/binary-amd64/Packages
|
||||
7dc41b5f359d26cb8c1bcc4b512c94c5db1b402efb15e76d5d0d23625cdca933 1357 main/binary-amd64/Packages.bz2
|
||||
64f1a3fd43c28053b3c2301a123b4cf6dc5a1370a94463d861fab3856554366e 1268 main/binary-amd64/Packages.gz
|
||||
101f8d561bb536f600918ab98ffb742828149f0d90ed0b007b7b761e9ad4f8a2 99 main/binary-amd64/Release
|
||||
SHA512:
|
||||
ec29ceab750bab737fbbcc9f00b74c334d0e1ea2f62c0081ed9b695d76bac1296f7560b2ee80361c9f8ed2b538a6adad21d8d3b55200fa71096ac09cd6c7eebd 3405449 Contents-amd64
|
||||
69bbc10540b2bb90941dd2b6804544c91fd335e91f7674f50b2d32312257b997f34ec5d0147d69a16164e41e2bdbdc008a74c863b6de0c42c15eba062c540efa 152525 Contents-amd64.gz
|
||||
ec29ceab750bab737fbbcc9f00b74c334d0e1ea2f62c0081ed9b695d76bac1296f7560b2ee80361c9f8ed2b538a6adad21d8d3b55200fa71096ac09cd6c7eebd 3405449 main/Contents-amd64
|
||||
69bbc10540b2bb90941dd2b6804544c91fd335e91f7674f50b2d32312257b997f34ec5d0147d69a16164e41e2bdbdc008a74c863b6de0c42c15eba062c540efa 152525 main/Contents-amd64.gz
|
||||
19efa178a219564af52c8eff763c820d973a9f00f43a6fd5d1599179661b025a043da630874ba56814ca43608d68aa2e555bb5f303a3d84deb1412c4c8923ff6 2761 main/binary-amd64/Packages
|
||||
87099e5b28836adde21419be72bc16bbb6b02bd901f8372df2de3543d03f8de9ff58fd058ef7bc90a7bafc9b8d4c38fdd25c507b788b8054d79d448c97ef32d7 1357 main/binary-amd64/Packages.bz2
|
||||
dc706a07d45f70d0bf645c4d65e1b6b235a66dfa294f8a77825b1395cf40aa3a2ed097f7092e4635c99ff231fc9f44852d0b91ed09fbbd1cc8a78bb758afc2e7 1268 main/binary-amd64/Packages.gz
|
||||
fd75f52e3d8ab190e919a646d1ccc84e447b32e45691f0f9f65c3ac5ac806f024f7edef6e0c3c9e2d19a5f2621dcd3744f684d9b87a7cdc493b0eb55e7a0b393 99 main/binary-amd64/Release
|
||||
@@ -0,0 +1,10 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
wsBcBAABCAAQBQJljD4VCRBWSZ+SBjn1cQAA8egIADfaxAQ/X/sjUAVIXODYu2cu
|
||||
CWzVmXJ9PcNAQhTo+3rCgLKfs2o8Vw4lKGbNlAUAUZuLTooAUAmIqqnkgaiJsXLO
|
||||
wGWqbHRi/opVVvYRATpuQJfpZ6Du9z8bhmnm7klVlZhluxuFia1analsT3lOopI1
|
||||
GrXrN4TMXwUp3tOk/7hWaP/ME3v0MsMIqtrkDwseA8lQ7pMhA8TnmabDZTSmf94R
|
||||
KxYzw8TaI/Ddrs9HBsP9d0hUqOvyhicwqC5XF4Rtn9GgjN2H0+/vUPkNta7I8zOV
|
||||
UlnOj0r8owjVKg4opRqrGfk14MmyliQZ2KlFHhsgdxUoTB0QPuFKnQLflwx7Ztw=
|
||||
=tgYK
|
||||
-----END PGP SIGNATURE-----
|
||||
Binary file not shown.
@@ -0,0 +1,65 @@
|
||||
Package: eendoe-repository
|
||||
Section: utils
|
||||
Maintainer: Bart Meijer
|
||||
Architecture: all
|
||||
Version: 1.0
|
||||
Filename: pool/main/e/eendoe-repository/eendoe-repository-1.0_all.deb
|
||||
Size: 7224
|
||||
MD5sum: 1dec21c569795d9a7c408b49bc462351
|
||||
SHA1: f81b15730a43fc3e8bb002046a09755c83b00794
|
||||
SHA256: 87fe58023583065366484a08af3789e276a0ab87516eae549e550dd6e74ca78f
|
||||
SHA512: 6c5ccca79318822ea39cfe04bbf8c11e9be06e8f53aafdef9a47b8bbcf3b2370b1ad1d15e23fb36378aa8cf3304fce8563f5e963299eda348636f0e1eead4809
|
||||
Description: Eendoe package repository
|
||||
|
||||
Package: oracle-database-preinstall-21c
|
||||
Priority: extra
|
||||
Section: alien
|
||||
Installed-Size: 101
|
||||
Maintainer: Bart Meijer <bart@vmserver>
|
||||
Architecture: amd64
|
||||
Version: 1.0-2
|
||||
Filename: pool/main/o/oracle-database-preinstall-21c/oracle-database-preinstall-21c_1.0-2_amd64.deb
|
||||
Size: 19796
|
||||
MD5sum: 9a95793469fb85f535a26266b60738bf
|
||||
SHA1: 6869aad4114275c479e40865bba021c6ca02e6cb
|
||||
SHA256: a6ad8c2b5b204f631f88199ed799784ad6fd73bd65d899277b8e786ecf5bb578
|
||||
SHA512: 617ce833e5771b2d55f8d1c39c77adf1f775590565ff056610625f8c24450150d4a563a7a4cac25966b41c6becd90e73c38c379bc198096acd0b673eaa0b48e2
|
||||
Description: Oracle Database Preinstallation RPM
|
||||
The Oracle Database Preinstallation RPM automatically installs any additional
|
||||
packages needed for installing Oracle Grid Infrastructure and Oracle Database
|
||||
and configures your server operating system automatically, including setting
|
||||
kernel parameters and other basic operating system requirements for install.
|
||||
.
|
||||
(Converted from a rpm package by alien version 8.95.)
|
||||
|
||||
Package: oracle-database-xe-21c
|
||||
Section: utils
|
||||
Maintainer: Bart Meijer
|
||||
Architecture: all
|
||||
Version: 1.0.3
|
||||
Filename: pool/main/o/oracle-database-xe-21c/oracle-database-xe-21c-1.0.3_all.deb
|
||||
Size: 2188370848
|
||||
MD5sum: b89910db6b9ba0ab149db6ee78a711f8
|
||||
SHA1: 39e2f816b81ccc4dbbc4bc89d20361272dd59161
|
||||
SHA256: ec96ebd544d9492e3696fc609d18a539357c653b3fb4264d144fbd0a27b23eec
|
||||
SHA512: 1922b4fec8b7e6b03a298a2f8d4f400ec9aa9b2d7ad6fe4ceaa83d136c6d5910316f46c6025b352b05ce18ad8fc9e3b7286bf3948e000a1857b6fce2d2c19c03
|
||||
Description: Eendoe package repository
|
||||
|
||||
Package: oracle-database-xe-21c
|
||||
Priority: extra
|
||||
Section: alien
|
||||
Installed-Size: 6067591
|
||||
Maintainer: Bart Meijer <bart@vmserver>
|
||||
Architecture: amd64
|
||||
Version: 1.0-2
|
||||
Filename: pool/main/o/oracle-database-xe-21c/oracle-database-xe-21c_1.0-2_amd64.deb
|
||||
Size: 2188841976
|
||||
MD5sum: 0b39df8f78e8c909557d1f028c9e7e5f
|
||||
SHA1: 882d1f5f63d842c6051a9aa0ec57a2d34cdb1a3f
|
||||
SHA256: 5b1401ac3e34ac5c0c636b8b41a6b3762f9b8297fb6f42c3bc2d1d05677ae01d
|
||||
SHA512: 9ff82c1b2d09582f1c456ba99ecb3f3be5079bd00a5101c7f927fb3752bc869fe4e982b51d170feca64e9ab6ab58c41566a30e57b1ff9fe066041bc0465e3d2a
|
||||
Description: Oracle 21c Express Edition Database
|
||||
Oracle 21c Express Edition Database
|
||||
.
|
||||
(Converted from a rpm package by alien version 8.95.)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
Origin: . stable
|
||||
Label: . stable
|
||||
Archive: stable
|
||||
Suite: stable
|
||||
Architecture: amd64
|
||||
Component: main
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
<a href="/pool/main/e/eendoe-repository/eendoe-repository-1.0_all.deb">Download</a>
|
||||
|
||||
|
||||
wget https://repository.eendoe.nl/pool/main/e/eendoe-repository/eendoe-repository-1.0_all.deb && sudo dpkg -i eendoe-repository-1.0_all.deb
|
||||
Binary file not shown.
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
OUTPUT_DIR="${OUTPUT_DIR:-$SCRIPT_DIR/staging}"
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
build_package() {
|
||||
local pkg_dir="$1"
|
||||
local pkg_name
|
||||
pkg_name="$(basename "$pkg_dir")"
|
||||
|
||||
if [ -f "$pkg_dir/bin/build.sh" ]; then
|
||||
echo "=== Building $pkg_name ==="
|
||||
(
|
||||
cd "$pkg_dir"
|
||||
./bin/build.sh
|
||||
# Move built debian packages to output dir
|
||||
mv -v *.deb "$OUTPUT_DIR/" 2>/dev/null || true
|
||||
)
|
||||
else
|
||||
echo "Skipping $pkg_name: no bin/build.sh found"
|
||||
fi
|
||||
}
|
||||
|
||||
TARGET="${1:-all}"
|
||||
|
||||
if [ "$TARGET" = "all" ]; then
|
||||
echo "Building all packages in sources/..."
|
||||
for dir in "$SCRIPT_DIR"/sources/*; do
|
||||
if [ -d "$dir" ]; then
|
||||
build_package "$dir"
|
||||
fi
|
||||
done
|
||||
else
|
||||
if [ -d "$SCRIPT_DIR/sources/$TARGET" ]; then
|
||||
build_package "$SCRIPT_DIR/sources/$TARGET"
|
||||
else
|
||||
echo "Error: Source directory sources/$TARGET not found."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Build complete. Packages available in: $OUTPUT_DIR"
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
cd /data/repository/binary/
|
||||
|
||||
aptly repo add -force-replace rrw *.deb
|
||||
|
||||
aptly publish update stable
|
||||
rsync -avh /home/bartlocal/.aptly/public/ bart@eendoe.nl:/sites/repository/
|
||||
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
CONTROL_FILE=$(ls -1 *.control)
|
||||
|
||||
echo "Using control file $CONTROL_FILE"
|
||||
PACKAGE_NAME=$(awk '/^Package: /{print $NF}' $CONTROL_FILE)
|
||||
VERSION=$(awk '/^Version: /{print $NF}' $CONTROL_FILE)
|
||||
|
||||
|
||||
|
||||
|
||||
# tag git repo
|
||||
# git tag -f release-${VERSION}
|
||||
|
||||
echo "Building package $PACKAGE_NAME ($VERSION): $PACKAGE_NAME-${VERSION}_all.deb"
|
||||
# VERSION
|
||||
sed "s/{VERSION}/${VERSION}/" $PACKAGE_NAME.control > packaging/$PACKAGE_NAME/DEBIAN/control
|
||||
|
||||
#create debian package
|
||||
dpkg --build packaging/$PACKAGE_NAME $PACKAGE_NAME-${VERSION}_all.deb
|
||||
|
||||
# dpkg-sig -k 14AF7F05 --sign builder icosi-${VERSION}_all.deb
|
||||
|
||||
echo "clean up"
|
||||
rm packaging/$PACKAGE_NAME/DEBIAN/control
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
Package: eendoe-hyperdeck
|
||||
Version: {VERSION}
|
||||
Section: utils
|
||||
Maintainer: nl.eendoe.bart
|
||||
Architecture: all
|
||||
Depends: python3, python3-pip, ffmpeg, eendoe-srs
|
||||
Description: Eendoe hyperdeck client module
|
||||
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
|
||||
#set -e
|
||||
case "$1" in
|
||||
configure|abort-upgrade)
|
||||
|
||||
id -u vid &>/dev/null || useradd --system -m vid
|
||||
|
||||
runuser -l vid -c "pip3 install -r /usr/share/hyperdeck/requirements.txt"
|
||||
|
||||
|
||||
|
||||
FILE="/etc/hyperdeck/config.json"
|
||||
SOURCEFILE="/usr/share/hyperdeck/config.json.orig"
|
||||
if [ -f "$FILE" ]; then
|
||||
echo "$FILE exists."
|
||||
else
|
||||
mkdir -p /etc/hyperdeck/
|
||||
echo "Copy default config"
|
||||
cp "$SOURCEFILE" "$FILE"
|
||||
chmod 744 "$FILE"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
systemd-notify --booted
|
||||
if [ "$?" -eq 0 ]
|
||||
then
|
||||
echo "Systemd running, registering service"
|
||||
systemctl daemon-reload && systemctl enable hyperdeck
|
||||
else
|
||||
echo "Systemd not running, skipping"
|
||||
fi
|
||||
|
||||
mkdir -p /var/log/hyperdeck/
|
||||
chown vid:vid "/var/log/hyperdeck/"
|
||||
|
||||
echo "Installation done"
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
case "$1" in
|
||||
install|upgrade)
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
#
|
||||
# Systemd unit file for Hyperdeck recorder
|
||||
#
|
||||
|
||||
[Unit]
|
||||
Description=Eendoe Hyperdeck recorder manager
|
||||
After=network.target
|
||||
RequiresMountsFor=/mnt/video
|
||||
|
||||
[Service]
|
||||
|
||||
|
||||
# Lifecycle
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/python3 /usr/share/hyperdeck/main.py -c /etc/hyperdeck/config.json
|
||||
SuccessExitStatus=0
|
||||
Restart=on-abort
|
||||
WorkingDirectory=/usr/share/hyperdeck/
|
||||
|
||||
# Logging
|
||||
SyslogIdentifier=hyperdeck
|
||||
|
||||
# Security
|
||||
User=vid
|
||||
Group=vid
|
||||
PrivateTmp=yes
|
||||
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"videopath": "/mnt/video/",
|
||||
"hyperdeck": {"ip": "10.31.35.221", "port": 9993},
|
||||
"rtsp_url" : "rtsp://admin:admin@10.31.35.205:554/1",
|
||||
"log_path": "/var/log/hyperdeck/"
|
||||
}
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
import os
|
||||
from typing import BinaryIO
|
||||
|
||||
import logging
|
||||
from fastapi import Request,FastAPI, HTTPException, status
|
||||
from fastapi.responses import JSONResponse, FileResponse,StreamingResponse
|
||||
from hyperdeck.application import HyperDeck
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
class ScheduleItemRequest(BaseModel):
|
||||
start: datetime| None
|
||||
end: datetime| None
|
||||
id: str| None
|
||||
title: str| None
|
||||
|
||||
|
||||
def send_bytes_range_requests(
|
||||
file_obj: BinaryIO, start: int, end: int, chunk_size: int = 10_000
|
||||
):
|
||||
"""Send a file in chunks using Range Requests specification RFC7233
|
||||
|
||||
`start` and `end` parameters are inclusive due to specification
|
||||
"""
|
||||
with file_obj as f:
|
||||
f.seek(start)
|
||||
while (pos := f.tell()) <= end:
|
||||
read_size = min(chunk_size, end + 1 - pos)
|
||||
yield f.read(read_size)
|
||||
|
||||
|
||||
def _get_range_header(range_header: str, file_size: int) -> tuple[int, int]:
|
||||
def _invalid_range():
|
||||
return HTTPException(
|
||||
status.HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE,
|
||||
detail=f"Invalid request range (Range:{range_header!r})",
|
||||
)
|
||||
|
||||
try:
|
||||
h = range_header.replace("bytes=", "").split("-")
|
||||
start = int(h[0]) if h[0] != "" else 0
|
||||
end = int(h[1]) if h[1] != "" else file_size - 1
|
||||
except ValueError:
|
||||
raise _invalid_range()
|
||||
|
||||
if start > end or start < 0 or end > file_size - 1:
|
||||
raise _invalid_range()
|
||||
return start, end
|
||||
|
||||
|
||||
def range_requests_response(
|
||||
request: Request, file_path: str, content_type: str
|
||||
):
|
||||
"""Returns StreamingResponse using Range Requests of a given file"""
|
||||
|
||||
file_size = os.stat(file_path).st_size
|
||||
range_header = request.headers.get("range")
|
||||
|
||||
headers = {
|
||||
"content-type": content_type,
|
||||
"accept-ranges": "bytes",
|
||||
"content-encoding": "identity",
|
||||
"content-length": str(file_size),
|
||||
"access-control-expose-headers": (
|
||||
"content-type, accept-ranges, content-length, "
|
||||
"content-range, content-encoding"
|
||||
),
|
||||
}
|
||||
start = 0
|
||||
end = file_size - 1
|
||||
status_code = status.HTTP_200_OK
|
||||
|
||||
if range_header is not None:
|
||||
start, end = _get_range_header(range_header, file_size)
|
||||
size = end - start + 1
|
||||
headers["content-length"] = str(size)
|
||||
headers["content-range"] = f"bytes {start}-{end}/{file_size}"
|
||||
status_code = status.HTTP_206_PARTIAL_CONTENT
|
||||
|
||||
return StreamingResponse(
|
||||
send_bytes_range_requests(open(file_path, mode="rb"), start, end),
|
||||
headers=headers,
|
||||
status_code=status_code,
|
||||
)
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
CHUNK_SIZE = 1024*1024
|
||||
|
||||
|
||||
|
||||
@app.exception_handler(RuntimeError)
|
||||
async def validation_exception_handler(request: Request, exc: RuntimeError) -> JSONResponse:
|
||||
return JSONResponse({
|
||||
"status":"error",
|
||||
"message": str(exc)
|
||||
},500)
|
||||
|
||||
|
||||
@app.get("/videos/", response_class=JSONResponse)
|
||||
def read_fonts():
|
||||
app = HyperDeck()
|
||||
|
||||
|
||||
files = []
|
||||
for f in app.getVideos():
|
||||
files.append({
|
||||
"name": f["name"],
|
||||
"filesize": f["filesize"],
|
||||
"start": f["start"],
|
||||
"end": f["end"],
|
||||
"title": f["title"],
|
||||
|
||||
})
|
||||
|
||||
|
||||
return files
|
||||
|
||||
|
||||
@app.get("/status/", response_class=JSONResponse)
|
||||
def getStatus():
|
||||
app = HyperDeck()
|
||||
|
||||
date = datetime.now()
|
||||
next = app.getNextScheduled()
|
||||
|
||||
if next:
|
||||
nexttime = next.start.isoformat()
|
||||
else:
|
||||
nexttime = "none"
|
||||
|
||||
|
||||
return JSONResponse({
|
||||
"status": app.getStatus().value,
|
||||
"errormessage" : app.getLastError(),
|
||||
"date": date.isoformat(), #strftime("%Y%m%d%H%M%S"),
|
||||
"nextrecording": nexttime
|
||||
})
|
||||
|
||||
@app.post("/status/record", response_class=JSONResponse)
|
||||
async def startManualRecording(request: Request):
|
||||
logging.info("Manually set status to recording" )
|
||||
app = HyperDeck()
|
||||
|
||||
if app.startRecording():
|
||||
return {"status":"ok"}
|
||||
else:
|
||||
return {"status":"error"}
|
||||
|
||||
|
||||
@app.post("/status/stop", response_class=JSONResponse)
|
||||
async def stopRecording(request: Request):
|
||||
logging.info("Manually set status to idle" )
|
||||
app = HyperDeck()
|
||||
if app.stopRecording():
|
||||
return {"status":"ok"}
|
||||
else:
|
||||
return {"status":"error"}
|
||||
|
||||
|
||||
@app.get("/schedule/active", response_class=JSONResponse)
|
||||
async def getScheduleActive(request: Request):
|
||||
app = HyperDeck()
|
||||
return app.schedule.getActiveItems()
|
||||
|
||||
@app.get("/schedule", response_class=JSONResponse)
|
||||
async def getSchedule(request: Request):
|
||||
app = HyperDeck()
|
||||
return app.getSchedule()
|
||||
|
||||
|
||||
|
||||
|
||||
@app.get("/schedule/lastday", response_class=JSONResponse)
|
||||
async def getItemsLastDay(request: Request):
|
||||
app = HyperDeck()
|
||||
return app.schedule.getItemsLastDay()
|
||||
|
||||
|
||||
@app.post("/schedule", response_class=JSONResponse)
|
||||
async def createScheduledItem(item: ScheduleItemRequest ):
|
||||
app = HyperDeck()
|
||||
|
||||
item.start = item.start.replace(tzinfo=None)
|
||||
item.end = item.end.replace(tzinfo=None)
|
||||
|
||||
if item.end<= item.start:
|
||||
raise HTTPException(status_code=422, detail="End time must be later then start time")
|
||||
|
||||
if not app.isFree(item.start, item.end,""):
|
||||
raise HTTPException(status_code=422, detail="Time slot already allocated")
|
||||
|
||||
app.addScheduledRecording(item.start, item.end, item.title)
|
||||
return app.getSchedule()
|
||||
|
||||
@app.put("/schedule", response_class=JSONResponse)
|
||||
async def editScheduledItem(item: ScheduleItemRequest ):
|
||||
app = HyperDeck()
|
||||
|
||||
item.start = item.start.replace(tzinfo=None)
|
||||
item.end = item.end.replace(tzinfo=None)
|
||||
|
||||
if item.end<= item.start:
|
||||
raise HTTPException(status_code=422, detail="End time must be later then start time")
|
||||
|
||||
|
||||
if not app.isFree(item.start, item.end,item.id):
|
||||
raise HTTPException(status_code=422, detail="Time slot already allocated")
|
||||
|
||||
|
||||
i = app.schedule.getScheduleItem(item.id)
|
||||
if i.status != "waiting":
|
||||
raise HTTPException(status_code=422, detail="Unable to update recorded item")
|
||||
|
||||
i.start = item.start
|
||||
i.end = item.end
|
||||
i.title = item.title
|
||||
app.saveSchedule()
|
||||
return app.getSchedule()
|
||||
|
||||
@app.delete("/schedule", response_class=JSONResponse)
|
||||
async def deleteScheduledItem(item: ScheduleItemRequest ):
|
||||
app = HyperDeck()
|
||||
i = app.schedule.getScheduleItem(item.id)
|
||||
if i.status != "waiting":
|
||||
raise HTTPException(status_code=422, detail="Unable to delete recorded item")
|
||||
|
||||
app.removeScheduledItem(i)
|
||||
|
||||
return app.getSchedule()
|
||||
|
||||
|
||||
@app.get("/videos/get/{video}", response_class=FileResponse)
|
||||
async def read_item(video: str):
|
||||
app = HyperDeck()
|
||||
|
||||
f = app.getVideo(video)
|
||||
|
||||
logging.info("Downloading video " + f["path"])
|
||||
|
||||
return FileResponse(f["path"],headers={'Content-Disposition': 'attachement; filename="'+f["filename"]+'"'})
|
||||
|
||||
|
||||
@app.get("/videos/view/{video}", response_class=StreamingResponse)
|
||||
async def stream_item(video: str, request: Request):
|
||||
app = HyperDeck()
|
||||
f = app.getVideo(video)
|
||||
return range_requests_response(
|
||||
request, file_path=f["path"], content_type="video/mp4"
|
||||
)
|
||||
|
||||
|
||||
@app.get("/videos/{video}", response_class=JSONResponse)
|
||||
async def stream_item_alt(video: str, request: Request):
|
||||
app = HyperDeck()
|
||||
f = app.getVideo(video)
|
||||
return f
|
||||
|
||||
@app.delete("/videos/{video}", response_class=JSONResponse)
|
||||
async def deleteVideo(video: str ):
|
||||
app = HyperDeck()
|
||||
app.removeVideo(video)
|
||||
return app.getSchedule()
|
||||
+436
@@ -0,0 +1,436 @@
|
||||
from enum import Enum
|
||||
import json
|
||||
import socket
|
||||
from hyperdeck.db import Schedule, ScheduleItem, ScheduleItemStatus
|
||||
from hyperdeck.utils import Singleton
|
||||
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
import datetime
|
||||
|
||||
|
||||
class Config(metaclass=Singleton):
|
||||
def open(self, filename):
|
||||
f = open(filename, "r")
|
||||
j = json.load(f)
|
||||
self.fromJson(j)
|
||||
|
||||
def save(self, filename):
|
||||
with open(filename, "w") as f:
|
||||
f.write(json.dumps(self.toJson()))
|
||||
|
||||
def toJson(self):
|
||||
return {}
|
||||
|
||||
def fromJson(self, j):
|
||||
self.videopath = j["videopath"]
|
||||
self.hyperdeck_ip = j["hyperdeck"]["ip"]
|
||||
self.hyperdeck_port = j["hyperdeck"]["port"]
|
||||
|
||||
if "max_age" in j:
|
||||
self.max_age =int( j["max_age"])
|
||||
else:
|
||||
self.max_age = 90
|
||||
|
||||
|
||||
if "max_originals_age" in j:
|
||||
self.maxmax_originals_age__originals_age =int( j["max_originals_age"])
|
||||
else:
|
||||
self.max_originals_age = 5
|
||||
|
||||
if "rtsp_url" in j:
|
||||
self.rtsp_url = j["rtsp_url"]
|
||||
|
||||
self.videopath_originals = os.path.join(self.videopath, "originals")
|
||||
self.videopath_convert = os.path.join(self.videopath, "convert")
|
||||
|
||||
if "log_path" in j:
|
||||
self.log_path = j["log_path"]
|
||||
else:
|
||||
self.log_path = "/var/log/hyperdeck/"
|
||||
|
||||
pass
|
||||
|
||||
def __init__(self, configfile) -> None:
|
||||
self.open(configfile)
|
||||
pass
|
||||
|
||||
|
||||
class Release(metaclass=Singleton):
|
||||
def __init__(self) -> None:
|
||||
f = open("release.json")
|
||||
release = json.load(f)
|
||||
self.version = release["version"]
|
||||
|
||||
class HyperDeckStatus(Enum):
|
||||
IDLE = "idle"
|
||||
RECORDING = "recording"
|
||||
UNKNOWN = "unknown"
|
||||
ERROR ="error"
|
||||
|
||||
class HyperDeck(metaclass=Singleton):
|
||||
|
||||
def load_config(self, configfile:str ):
|
||||
|
||||
|
||||
self.config = Config(configfile)
|
||||
if not os.path.isdir(self.config.videopath):
|
||||
raise Exception("Directory {self.config.videopath} does not exist")
|
||||
|
||||
if not os.path.isdir(self.config.videopath_originals):
|
||||
logging.info(
|
||||
"Creating directory {}".format(self.config.videopath_originals)
|
||||
)
|
||||
os.mkdir(self.config.videopath_originals)
|
||||
|
||||
if not os.path.isdir(self.config.videopath_originals):
|
||||
raise Exception(
|
||||
"Directory {} does not exist".format(
|
||||
self.config.videopath_originals
|
||||
)
|
||||
)
|
||||
|
||||
if not os.path.isdir(self.config.videopath_convert):
|
||||
logging.info("Creating directory {}".format(self.config.videopath_convert))
|
||||
os.mkdir(self.config.videopath_convert)
|
||||
|
||||
if not os.path.isdir(self.config.videopath_convert):
|
||||
raise Exception(
|
||||
"Directory {} does not exist".format(self.config.videopath_convert)
|
||||
)
|
||||
|
||||
self.status = HyperDeckStatus.UNKNOWN
|
||||
|
||||
self.videofiles = []
|
||||
self.current = None
|
||||
self.lasterror = ""
|
||||
self.schedule = Schedule()
|
||||
|
||||
self.processSchedule()
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.config = None
|
||||
os.environ["TZ"] = "Europe/Amsterdam"
|
||||
time.tzset()
|
||||
self.release = Release()
|
||||
|
||||
def getConvertedFilename(self, filename:str):
|
||||
return os.path.join(self.config.videopath_convert, 'c_' + filename)
|
||||
|
||||
def getTargetFilename(self, filename:str):
|
||||
return os.path.join(self.config.videopath, 'c_' + filename)
|
||||
|
||||
|
||||
def getStatus(self):
|
||||
return self.status
|
||||
|
||||
def cleanup(self):
|
||||
|
||||
|
||||
logging.info(f"Ckeaning up originals older than {self.config.max_originals_age} days from {self.config.videopath_originals}")
|
||||
maxdate = datetime.datetime.now() - datetime.timedelta(days=self.config.max_originals_age)
|
||||
for f in os.listdir(self.config.videopath_originals):
|
||||
filename = os.path.join(self.config.videopath_originals,f)
|
||||
if os.path.getmtime(filename) < maxdate.timestamp():
|
||||
logging.info(f"Deleting file {filename}")
|
||||
os.remove(filename)
|
||||
|
||||
|
||||
logging.info(f"Doing cleanup for items older than {self.config.max_age} days")
|
||||
items = self.schedule.getItemsOlderThan(self.config.max_age)
|
||||
for item in items:
|
||||
logging.info(f"Cleaning {item.id}")
|
||||
f = self.getConvertedFilename(item.file)
|
||||
if os.path.exists(f):
|
||||
logging.info(f"Removing file {f}")
|
||||
os.remove(f)
|
||||
|
||||
f = self.getTargetFilename(item.file)
|
||||
if os.path.exists(f):
|
||||
logging.info(f"Removing file {f}")
|
||||
os.remove(f)
|
||||
|
||||
self.schedule.removeItem(item)
|
||||
|
||||
|
||||
logging.info("Cleanup done")
|
||||
|
||||
|
||||
def sendData(self, req):
|
||||
try:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.settimeout(5)
|
||||
s.connect((self.config.hyperdeck_ip, self.config.hyperdeck_port))
|
||||
logging.info(f"Connected to {self.config.hyperdeck_ip}")
|
||||
|
||||
data = s.recv(1024).decode()
|
||||
|
||||
logging.info("Got data from Hyperdeck: " + data)
|
||||
|
||||
s.sendall(req.encode())
|
||||
|
||||
data = s.recv(1024).decode()
|
||||
|
||||
logging.info("Got data from Hyperdeck: " + data)
|
||||
|
||||
s.shutdown(socket.SHUT_RDWR)
|
||||
s.close()
|
||||
|
||||
if data[:3] == "200":
|
||||
self.lasterror = ""
|
||||
return True
|
||||
else:
|
||||
self.status = HyperDeckStatus.ERROR
|
||||
self.lasterror = data[3:]
|
||||
return False
|
||||
except TimeoutError:
|
||||
logging.error("Kon geen verbinding maken met hyperdeck")
|
||||
return False
|
||||
except Exception as e:
|
||||
logging.error("Got data from Hyperdeck: " + str(e))
|
||||
return False
|
||||
|
||||
def getLastError(self):
|
||||
return self.lasterror
|
||||
|
||||
def startRecording(self):
|
||||
if self.status == ScheduleItemStatus.RECORDING:
|
||||
logging.error("Already recording")
|
||||
return False
|
||||
|
||||
n: ScheduleItem = ScheduleItem.create(datetime.datetime.now().replace(microsecond=0), None, "recording")
|
||||
|
||||
n.status = ScheduleItemStatus.RECORDING
|
||||
filename = "vrm_" + n.start.strftime("%Y%m%d%H%M%S")
|
||||
n.file = filename
|
||||
|
||||
|
||||
item = n.to_schema()
|
||||
self.schedule.saveItem(n)
|
||||
|
||||
if self.startRecordingFile(item.file):
|
||||
|
||||
self.current = item
|
||||
return True
|
||||
else:
|
||||
n = self.schedule.getScheduleItem(item.id)
|
||||
n.status = ScheduleItemStatus.FAILED
|
||||
|
||||
self.schedule.saveItem(n)
|
||||
return False
|
||||
|
||||
|
||||
def startRecordingFile(self, filename: str):
|
||||
if self.status == HyperDeckStatus.RECORDING:
|
||||
logging.error("Already recording")
|
||||
return False
|
||||
|
||||
logging.info("Start recording")
|
||||
|
||||
if self.sendData("record: name: " + filename + "\n"):
|
||||
self.status = HyperDeckStatus.RECORDING
|
||||
return True
|
||||
else:
|
||||
self.status = HyperDeckStatus.ERROR
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def stopRecording(self):
|
||||
if not self.current:
|
||||
logging.error("No current recording")
|
||||
return False
|
||||
|
||||
if self.status == ScheduleItemStatus.IDLE:
|
||||
logging.warning("Unable to stop recording: nothing recording")
|
||||
self.current.status = ScheduleItemStatus.FAILED
|
||||
return False
|
||||
|
||||
logging.info("Stop recording")
|
||||
|
||||
if self.sendData("stop\n"):
|
||||
item = self.schedule.getScheduleItem(self.current.id)
|
||||
|
||||
item.status = ScheduleItemStatus.RECORDED
|
||||
self.schedule.saveItem(item)
|
||||
self.current = None
|
||||
self.status = HyperDeckStatus.IDLE
|
||||
return True
|
||||
else:
|
||||
self.status = HyperDeckStatus.ERROR
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def getMachines(self):
|
||||
return self.config.machines
|
||||
|
||||
def loadVideos(self):
|
||||
# if len(self.videofiles)==0 :
|
||||
newlist = []
|
||||
|
||||
for file in os.listdir(self.config.videopath):
|
||||
if file.startswith("c_") and file.endswith(".mp4"):
|
||||
filename = os.path.join(self.config.videopath, file)
|
||||
file_stats = os.stat(filename)
|
||||
|
||||
item = {
|
||||
"name": file,
|
||||
"path": filename,
|
||||
"type": "mp4",
|
||||
"filename": file,
|
||||
"filesize": file_stats.st_size,
|
||||
"start": None,
|
||||
"end": None,
|
||||
"title": None,
|
||||
}
|
||||
|
||||
rec = self.schedule.getScheduleItemByFile(item["name"])
|
||||
|
||||
if rec:
|
||||
item["start"] = rec.start
|
||||
item["end"] = rec.end
|
||||
item["title"] = rec.title
|
||||
|
||||
newlist.append(item)
|
||||
|
||||
self.videofiles = sorted(newlist, key=lambda q: q["name"].lower())
|
||||
|
||||
def getVideo(self, name):
|
||||
self.loadVideos()
|
||||
|
||||
limited_list = [
|
||||
element for element in self.videofiles if element["name"] == name
|
||||
]
|
||||
return limited_list[0]
|
||||
|
||||
def getVideos(self):
|
||||
self.loadVideos()
|
||||
|
||||
return self.videofiles
|
||||
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
def removeScheduledItem(self, itm: ScheduleItem):
|
||||
self.schedule.remove(itm)
|
||||
self.saveSchedule()
|
||||
|
||||
def removeVideo(self, file: str):
|
||||
filename = os.path.join(self.config.videopath, file)
|
||||
logging.info("Removing file {}".format(filename))
|
||||
os.remove(filename)
|
||||
|
||||
itm = self.schedule.getScheduleItemByFile(file)
|
||||
if itm:
|
||||
self.removeScheduledItem(itm)
|
||||
|
||||
def isFree(self, start: datetime, end: datetime, exclude_id: str) -> bool:
|
||||
return self.schedule.getFree(start, end, exclude_id)
|
||||
|
||||
|
||||
def addScheduledRecording(self, start: datetime, end: datetime, title: str):
|
||||
n = ScheduleItem.create(start, end, title)
|
||||
s = self.schedule.getScheduleItem(n.id)
|
||||
if s:
|
||||
raise Exception("Schedule item already exists")
|
||||
|
||||
self.schedule.saveItem(n)
|
||||
|
||||
|
||||
|
||||
|
||||
def getSchedule(self):
|
||||
retval = []
|
||||
for s in self.schedule.getAll():
|
||||
j = {
|
||||
"id": s.id,
|
||||
"start": s.start,
|
||||
"status": s.status,
|
||||
"end": s.end,
|
||||
"file": None,
|
||||
"start_recording": s.start_recording,
|
||||
"end_recording": s.end_recording,
|
||||
"start_copying": s.start_copying,
|
||||
"end_copying": s.end_copying,
|
||||
"start_processing": s.start_processing,
|
||||
"end_processing": s.end_processing,
|
||||
"title": s.title,
|
||||
}
|
||||
|
||||
if not j["end"]:
|
||||
j["end"] = j["start"]
|
||||
|
||||
if j["title"]:
|
||||
j["display"] = (
|
||||
j["title"]
|
||||
+ ": "
|
||||
+ s.start.strftime("%H:%M")
|
||||
+ " - "
|
||||
+ j["end"].strftime("%H:%M")
|
||||
)
|
||||
else:
|
||||
j["display"] = (
|
||||
s.start.strftime("%H:%M") + " - " + j["end"].strftime("%H:%M")
|
||||
)
|
||||
|
||||
if s.status == ScheduleItemStatus.DONE:
|
||||
j["file"] = "c_" + s.file + ".mp4"
|
||||
retval.append(j)
|
||||
return retval
|
||||
|
||||
def getNextScheduled(self):
|
||||
|
||||
return self.schedule.getNextScheduled()
|
||||
|
||||
def hasProcessing(self) -> bool:
|
||||
return self.getNextProcessing is None
|
||||
|
||||
"""
|
||||
def markCopied(self, filename):
|
||||
s = self.getByFile(filename)
|
||||
if s and s.status == ScheduleItemStatus.COPYING:
|
||||
s.status = ScheduleItemStatus.COPIED
|
||||
self.saveSchedule()
|
||||
"""
|
||||
|
||||
def processSchedule(self):
|
||||
for s in self.schedule.getItemsByStatus([ScheduleItemStatus.WAITING, ScheduleItemStatus.RECORDING]):
|
||||
if s.status == ScheduleItemStatus.WAITING:
|
||||
if s.end < datetime.datetime.now():
|
||||
id = s.id
|
||||
s.status = ScheduleItemStatus.FAILED
|
||||
self.schedule.saveItem(s)
|
||||
s = self.schedule.getScheduleItem(id)
|
||||
|
||||
if s.start < datetime.datetime.now():
|
||||
if self.startRecordingFile(s.file):
|
||||
s.status = ScheduleItemStatus.RECORDING
|
||||
|
||||
logging.info("Start recording " + s.file)
|
||||
s.start_recording = datetime.datetime.now().replace(
|
||||
microsecond=0
|
||||
)
|
||||
else:
|
||||
s.status = ScheduleItemStatus.FAILED
|
||||
logging.error("Unable to start recording")
|
||||
self.schedule.saveItem(s)
|
||||
|
||||
elif s.status ==ScheduleItemStatus.RECORDING:
|
||||
self.current = s
|
||||
logging.info(s.end)
|
||||
if s.end and (s.end < datetime.datetime.now()):
|
||||
logging.info("End of schedule for " + s.file)
|
||||
if not self.stopRecording():
|
||||
logging.error("Unable to stop recording")
|
||||
else:
|
||||
s.end_recording = datetime.datetime.now().replace(microsecond=0)
|
||||
self.schedule.saveItem(s)
|
||||
|
||||
elif s.end is None:
|
||||
self.status = HyperDeckStatus.RECORDING
|
||||
|
||||
def getWorksheet(self):
|
||||
return self.worksheet
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
from datetime import datetime, timedelta
|
||||
import enum
|
||||
import logging
|
||||
import os
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
from sqlmodel import Enum,Field, Column, SQLModel, Session, create_engine, not_, or_, select
|
||||
|
||||
|
||||
def enum_values(enum_class: type[enum.Enum]) -> list:
|
||||
"""Get values for enum."""
|
||||
return [status.value for status in enum_class]
|
||||
|
||||
|
||||
class ScheduleItemStatus(str, enum.Enum):
|
||||
NEW="new"
|
||||
WAITING="waiting"
|
||||
IDLE="idle"
|
||||
RECORDING="recording"
|
||||
RECORDED="recorded"
|
||||
COPYING="copying"
|
||||
COPIED="copied"
|
||||
PROCESSING="processing"
|
||||
FAILED="failed"
|
||||
DONE="done"
|
||||
|
||||
class ScheduleItemSchema(BaseModel):
|
||||
id: Optional[str] = None
|
||||
status: ScheduleItemStatus
|
||||
file: str = ""
|
||||
title: str = ""
|
||||
|
||||
# Tijden
|
||||
start: Optional[datetime] = None
|
||||
end: Optional[datetime] = None
|
||||
|
||||
# Proces tijden
|
||||
start_recording: Optional[datetime] = None
|
||||
end_recording: Optional[datetime] = None
|
||||
start_copying: Optional[datetime] = None
|
||||
end_copying: Optional[datetime] = None
|
||||
start_processing: Optional[datetime] = None
|
||||
end_processing: Optional[datetime] = None
|
||||
|
||||
last_update: Optional[datetime] = None
|
||||
|
||||
class Config:
|
||||
# In v1 moet dit 'orm_mode' zijn in plaats van 'from_attributes'
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class ScheduleItem(SQLModel, table=True):
|
||||
status:ScheduleItemStatus = Field(
|
||||
sa_column=Column(
|
||||
name="status",
|
||||
nullable=False,
|
||||
type_= Enum(ScheduleItemStatus, values_callable=enum_values),
|
||||
)
|
||||
)
|
||||
file:str = ""
|
||||
start:Optional[datetime] = None
|
||||
end:Optional[datetime] = None
|
||||
id: str | None = Field(default=None, primary_key=True)
|
||||
start_recording:datetime = None
|
||||
end_recording:datetime = None
|
||||
start_copying:datetime = None
|
||||
end_copying:datetime = None
|
||||
start_processing:datetime = None
|
||||
end_processing:datetime = None
|
||||
title:str = ""
|
||||
last_update:datetime = None
|
||||
|
||||
|
||||
def to_schema(self) -> ScheduleItemSchema:
|
||||
"""
|
||||
Converteert het database-object naar een sessie-loos Pydantic model.
|
||||
Dit voorkomt DetachedInstanceErrors buiten de database scope.
|
||||
"""
|
||||
return ScheduleItemSchema.from_orm(self)
|
||||
|
||||
# Je kunt dit ook integreren in je assign of create logica
|
||||
@classmethod
|
||||
def from_schema(cls, schema: ScheduleItemSchema):
|
||||
"""Maakt een database-object aan op basis van een schema."""
|
||||
return cls(**schema.dict())
|
||||
|
||||
def assign(self, item):
|
||||
self.status = item.status
|
||||
self.title = item.title
|
||||
self.end_processing = item.end_processing
|
||||
self.end_copying = item.end_copying
|
||||
self.end_recording = item.end_recording
|
||||
self.last_update = datetime.now()
|
||||
|
||||
def create(start: datetime, end: datetime, title: str) :
|
||||
retval = ScheduleItem()
|
||||
retval.start = start.replace(tzinfo=None)
|
||||
if end:
|
||||
retval.end = end.replace(tzinfo=None)
|
||||
if retval.end <= retval.start:
|
||||
raise Exception("End time must be later then start time")
|
||||
retval.last_update = datetime.now()
|
||||
|
||||
|
||||
retval.id = start.strftime("R%Y%m%d%H%M%S")
|
||||
retval.title = title
|
||||
|
||||
retval.status = ScheduleItemStatus.WAITING
|
||||
retval.file = "vr_" + start.strftime("%Y%m%d%H%M%S")
|
||||
return retval
|
||||
|
||||
def setCopying(self):
|
||||
self.status = ScheduleItemStatus.COPYING
|
||||
self.start_copying = datetime.now().replace(microsecond=0)
|
||||
|
||||
def setProcessing(self):
|
||||
self.status = ScheduleItemStatus.PROCESSING
|
||||
self.start_processing = datetime.now().replace(microsecond=0)
|
||||
|
||||
def fromJson(j):
|
||||
retval = ScheduleItem()
|
||||
retval.start = datetime.fromisoformat(j["start"])
|
||||
|
||||
if "title" in j:
|
||||
retval.title = j["title"]
|
||||
else:
|
||||
retval.title = None
|
||||
|
||||
if "end" in j:
|
||||
retval.end = datetime.fromisoformat(j["end"])
|
||||
else:
|
||||
retval.end = None
|
||||
|
||||
if "start_recording" in j and j["start_recording"]:
|
||||
retval.start_recording = datetime.fromisoformat(
|
||||
j["start_recording"]
|
||||
)
|
||||
if "end_recording" in j and j["end_recording"]:
|
||||
retval.end_recording = datetime.fromisoformat(j["end_recording"])
|
||||
|
||||
if "start_copying" in j and j["start_copying"]:
|
||||
retval.start_copying = datetime.fromisoformat(j["start_copying"])
|
||||
if "end_copying" in j and j["end_copying"]:
|
||||
retval.end_copying = datetime.fromisoformat(j["end_copying"])
|
||||
|
||||
if "start_processing" in j and j["start_processing"]:
|
||||
retval.start_processing = datetime.fromisoformat(
|
||||
j["start_processing"]
|
||||
)
|
||||
if "end_processing" in j and j["end_processing"]:
|
||||
retval.end_processing = datetime.fromisoformat(j["end_processing"])
|
||||
|
||||
retval.id = retval.start.strftime("R%Y%m%d%H%M%S")
|
||||
retval.status = j["status"]
|
||||
retval.file = j["file"]
|
||||
return retval
|
||||
|
||||
class Schedule:
|
||||
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def saveItems(self, items: List[ScheduleItem]):
|
||||
with Session(engine) as session:
|
||||
for item in items:
|
||||
item.last_update = datetime.now()
|
||||
session.add(item)
|
||||
session.commit()
|
||||
|
||||
|
||||
|
||||
def saveItem(self, item: ScheduleItem):
|
||||
with Session(engine) as session:
|
||||
item.last_update = datetime.now()
|
||||
session.add(item)
|
||||
session.commit()
|
||||
|
||||
def removeItem(self, item: ScheduleItem):
|
||||
with Session(engine) as session:
|
||||
session.delete(item)
|
||||
session.commit()
|
||||
|
||||
def getItemsOlderThan(self, age:int) -> List[ScheduleItem]:
|
||||
periode = timedelta(days=age)
|
||||
with Session(engine) as session:
|
||||
statement = select(ScheduleItem).where(ScheduleItem.start < datetime.now()-periode)
|
||||
return session.exec(statement).all()
|
||||
|
||||
def getFree(self, start: datetime, end: datetime, exclude_id: str) -> bool:
|
||||
with Session(engine) as session:
|
||||
statement = select(ScheduleItem).where(
|
||||
not_(or_(
|
||||
ScheduleItem.id == exclude_id,
|
||||
ScheduleItem.status != ScheduleItemStatus.WAITING,
|
||||
ScheduleItem.start >= end,
|
||||
not ScheduleItem.end,
|
||||
ScheduleItem.end <= start
|
||||
|
||||
)))
|
||||
return session.exec(statement).first() is None
|
||||
|
||||
def getNextScheduled(self):
|
||||
with Session(engine) as session:
|
||||
statement = select(ScheduleItem).where(ScheduleItem.start > datetime.now()).order_by(ScheduleItem.start)
|
||||
|
||||
session.exec(statement).first()
|
||||
|
||||
|
||||
def getItemsLastDay(self):
|
||||
time = datetime.now()-timedelta(days=1)
|
||||
with Session(engine) as session:
|
||||
statement = select(ScheduleItem).where(
|
||||
or_(
|
||||
ScheduleItem.start_copying > time,
|
||||
ScheduleItem.start_processing > time,
|
||||
ScheduleItem.start_recording > time,
|
||||
ScheduleItem.last_update > time
|
||||
)).order_by(ScheduleItem.start
|
||||
)
|
||||
return session.exec(statement).all()
|
||||
|
||||
|
||||
def getAll(self):
|
||||
with Session(engine) as session:
|
||||
statement = select(ScheduleItem).order_by(ScheduleItem.start)
|
||||
return session.exec(statement).all()
|
||||
|
||||
def getActiveItems(self):
|
||||
with Session(engine) as session:
|
||||
statement = select(ScheduleItem).where(
|
||||
ScheduleItem.status.in_([ScheduleItemStatus.PROCESSING,ScheduleItemStatus.COPYING])
|
||||
)
|
||||
return session.exec(statement).all()
|
||||
|
||||
def getItemsByStatus(self, statusses: List[ScheduleItem]):
|
||||
with Session(engine) as session:
|
||||
statement = select(ScheduleItem).where(ScheduleItem.status.in_( statusses))
|
||||
return session.exec(statement).all()
|
||||
|
||||
def returnFirstByStatus(self, status: ScheduleItemStatus) -> ScheduleItem|None:
|
||||
with Session(engine) as session:
|
||||
statement = select(ScheduleItem).where(ScheduleItem.status == status).order_by(ScheduleItem.start)
|
||||
return session.exec(statement).first()
|
||||
|
||||
|
||||
def getNextCopying(self) -> ScheduleItem:
|
||||
|
||||
item = self.returnFirstByStatus(ScheduleItemStatus.RECORDED)
|
||||
if item:
|
||||
id = item.id
|
||||
item.setCopying()
|
||||
self.saveItem(item)
|
||||
item = self.getScheduleItem(id)
|
||||
return item
|
||||
|
||||
|
||||
def getNextProcessing(self) -> ScheduleItem:
|
||||
item = self.returnFirstByStatus(ScheduleItemStatus.COPIED)
|
||||
if item:
|
||||
id = item.id
|
||||
item.setProcessing()
|
||||
self.saveItem(item)
|
||||
item = self.getScheduleItem(id)
|
||||
return item
|
||||
|
||||
def getByFile(self, filename):
|
||||
with Session(engine) as session:
|
||||
statement = select(ScheduleItem).where(ScheduleItem.file == filename)
|
||||
return session.exec(statement).first()
|
||||
|
||||
|
||||
def getScheduleItemByFile(self, filename) -> ScheduleItem:
|
||||
with Session(engine) as session:
|
||||
statement = select(ScheduleItem).where("c_" +ScheduleItem.file+ ".mp4" == filename)
|
||||
return session.exec(statement).first()
|
||||
|
||||
def getScheduleItem(self, id:str) -> ScheduleItem:
|
||||
with Session(engine) as session:
|
||||
statement = select(ScheduleItem).where(ScheduleItem.id == id)
|
||||
return session.exec(statement).first()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
sqlite_file_name =os.path.join(os.getcwd(), "hyperdeck.db")
|
||||
sqlite_url = f"sqlite:///{sqlite_file_name}"
|
||||
|
||||
engine = create_engine(sqlite_url, echo=True)
|
||||
|
||||
def create_db_and_tables():
|
||||
SQLModel.metadata.create_all(engine)
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
from ftplib import FTP
|
||||
from hyperdeck.application import HyperDeck
|
||||
import logging,os ,re
|
||||
|
||||
|
||||
logger = logging.getLogger('tasklogger')
|
||||
|
||||
def downloadFiles(name) -> bool:
|
||||
|
||||
app = HyperDeck()
|
||||
|
||||
logger.info("Download file " + name)
|
||||
|
||||
with FTP(host=app.config.hyperdeck_ip, user='anonymous', passwd='secret') as ftp:
|
||||
logger.info("Connected to FTP")
|
||||
|
||||
logger.debug(ftp.getwelcome())
|
||||
files = ftp.nlst('/1')
|
||||
logger.info("{} files found".format(len(files)))
|
||||
|
||||
cnt = 0
|
||||
found = False
|
||||
for f in files:
|
||||
if f.startswith( name) and f.endswith('.mp4'):
|
||||
if re.search("_[0-9]{4}\.mp4$",f):
|
||||
nr = f[-8:-4]
|
||||
else:
|
||||
nr = str(cnt).zfill(4)
|
||||
|
||||
targetfile = os.path.join(app.config.videopath_originals, nr + '_' + name + ".mp4")
|
||||
logger.info("Retrieving file " + f + ' to '+targetfile)
|
||||
|
||||
if os.path.isfile(targetfile):
|
||||
logger.error("Target file already exists: " + targetfile)
|
||||
return False
|
||||
|
||||
with open(targetfile, 'wb') as fp:
|
||||
ftp.retrbinary('RETR /1/' + f, fp.write)
|
||||
ftp.delete('/1/' + f)
|
||||
found = True
|
||||
|
||||
cnt += 1
|
||||
|
||||
files = ftp.nlst('/2')
|
||||
for f in files:
|
||||
if f.startswith( name) and f.endswith('.mp4'):
|
||||
if re.search("_[0-9]{4}\.mp4$",f):
|
||||
nr = f[-8:-4]
|
||||
else:
|
||||
nr = str(cnt).zfill(4)
|
||||
|
||||
targetfile = os.path.join(app.config.videopath_originals, nr + '_' + name + ".mp4")
|
||||
logger.info("Retrieving file " + f + ' to '+targetfile)
|
||||
|
||||
if os.path.isfile(targetfile):
|
||||
logger.error("Target file already exists: " + targetfile)
|
||||
return False
|
||||
|
||||
with open(targetfile, 'wb') as fp:
|
||||
ftp.retrbinary('RETR /2/' + f, fp.write)
|
||||
ftp.delete('/2/' + f)
|
||||
found = True
|
||||
|
||||
cnt += 1
|
||||
|
||||
|
||||
if found:
|
||||
return True
|
||||
|
||||
logger.error("File not found: " + name)
|
||||
return False
|
||||
|
||||
logger.error("Unable to connect to FTP")
|
||||
return False
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
|
||||
|
||||
from json import JSONEncoder
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
||||
from hyperdeck.db import ScheduleItem
|
||||
|
||||
|
||||
|
||||
|
||||
class ScheduleItemEncoder(JSONEncoder):
|
||||
def default(self, o):
|
||||
if isinstance(o, ScheduleItem):
|
||||
return o.toJson()
|
||||
|
||||
return json.JSONEncoder.default(self, o)
|
||||
|
||||
|
||||
def ScheduleItemDecoder(j):
|
||||
print(j)
|
||||
return ScheduleItem.fromJson(j)
|
||||
|
||||
class jsonconverter:
|
||||
def loadschedule(self):
|
||||
try:
|
||||
with open(os.path.join(os.getcwd(), "schedule.json"), "r") as f:
|
||||
self.schedule = json.load(f, object_hook=ScheduleItemDecoder)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logging.error("Unable to load schedule" + repr(e))
|
||||
self.schedule = []
|
||||
raise e
|
||||
|
||||
|
||||
def saveSchedule(self):
|
||||
with open(os.path.join(os.getcwd(), "schedule.json"), "w") as f:
|
||||
f.write(json.dumps(self.schedule, cls=ScheduleItemEncoder))
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
import logging
|
||||
|
||||
from rocketry import Rocketry
|
||||
from rocketry.conds import every
|
||||
|
||||
from hyperdeck.application import HyperDeck
|
||||
import datetime
|
||||
from hyperdeck.db import ScheduleItemStatus
|
||||
from hyperdeck.download import downloadFiles
|
||||
from hyperdeck.transcode import transcodeFile
|
||||
|
||||
import os
|
||||
|
||||
import multiprocessing
|
||||
|
||||
|
||||
from rocketry.conds import (
|
||||
minutely, daily
|
||||
)
|
||||
|
||||
manager = multiprocessing.Manager()
|
||||
|
||||
copyqueue = manager.Queue()
|
||||
copyqueue_done = manager.Queue()
|
||||
processqueue= manager.Queue()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
application = HyperDeck()
|
||||
# Creating the Rocketry app
|
||||
app = Rocketry(config={"task_execution": "async"})
|
||||
|
||||
formatter = logging.Formatter('[%(asctime)s] %(message)s')
|
||||
|
||||
handler =logging.FileHandler(os.path.join(application.config.log_path, "scheduler.log"))
|
||||
handler.setFormatter(formatter)
|
||||
tasklogger = logging.getLogger("rocketry.task")
|
||||
tasklogger.setLevel(logging.INFO)
|
||||
tasklogger.addHandler(handler)
|
||||
|
||||
|
||||
logger = logging.getLogger('tasklogger')
|
||||
|
||||
handler =logging.FileHandler(os.path.join(application.config.log_path, "task.log") )
|
||||
|
||||
handler.setFormatter(formatter)
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.addHandler(handler)
|
||||
|
||||
# Creating some tasks
|
||||
|
||||
logger.info("Startup, checking for in progress items")
|
||||
for s in application.schedule.getActiveItems():
|
||||
if s.status == ScheduleItemStatus.COPYING:
|
||||
s.start_copying = datetime.datetime.now().replace(microsecond=0)
|
||||
logger.info("File " + s.file + " marked for copying, queuing")
|
||||
copyqueue.put(s.to_schema())
|
||||
|
||||
if s.status == ScheduleItemStatus.PROCESSING:
|
||||
s.start_processing = datetime.datetime.now().replace(microsecond=0)
|
||||
logger.info("File " + s.file + " marked for processing, queuing")
|
||||
processqueue.put(s.to_schema())
|
||||
|
||||
|
||||
@app.task(minutely)
|
||||
def do_checkschedule():
|
||||
application.processSchedule()
|
||||
|
||||
|
||||
@app.task(every('10 seconds'))
|
||||
def get_NewFileName():
|
||||
|
||||
|
||||
if not copyqueue_done.empty():
|
||||
item = copyqueue_done.get()
|
||||
|
||||
logger.info("Process queue output for " + item.file)
|
||||
itemdb=application.schedule.getScheduleItem(item.id)
|
||||
itemdb.assign(item)
|
||||
application.schedule.saveItem(itemdb)
|
||||
|
||||
if copyqueue.empty():
|
||||
currentcopy = application.schedule.getNextCopying()
|
||||
if currentcopy:
|
||||
logger.info("Add to copy queue " + currentcopy.file)
|
||||
copyqueue.put(currentcopy.to_schema())
|
||||
|
||||
if processqueue.empty():
|
||||
currentcopy = application.schedule.getNextProcessing()
|
||||
if currentcopy:
|
||||
logger.info("Add to process queue " + currentcopy.file)
|
||||
processqueue.put(currentcopy.to_schema())
|
||||
|
||||
@app.task(minutely,execution="process",timeout=60*60*24)
|
||||
def do_copyfile(q=copyqueue,qd=copyqueue_done ):
|
||||
|
||||
if q.empty():
|
||||
return
|
||||
|
||||
item = copyqueue.get()
|
||||
logger.info("Start copying " + item.file)
|
||||
|
||||
if not downloadFiles(item.file):
|
||||
item.status = ScheduleItemStatus.FAILED
|
||||
logger.info("Copying failed for " + item.file)
|
||||
|
||||
else:
|
||||
item.status = ScheduleItemStatus.COPIED
|
||||
item.end_copying = datetime.datetime.now().replace(microsecond=0)
|
||||
logger.info("Copying success for " + item.file)
|
||||
|
||||
qd.put(item)
|
||||
|
||||
return
|
||||
|
||||
@app.task(daily,execution="process",timeout=60*60*24)
|
||||
def do_cleanup():
|
||||
application.cleanup()
|
||||
|
||||
@app.task(minutely,execution="process",timeout=60*60*24)
|
||||
def do_processfile(q=processqueue,qd=copyqueue_done ):
|
||||
|
||||
if q.empty():
|
||||
return
|
||||
|
||||
item = processqueue.get()
|
||||
logger.info("Start processing " + item.file)
|
||||
|
||||
|
||||
|
||||
if not transcodeFile(item.file):
|
||||
item.status = ScheduleItemStatus.FAILED
|
||||
else:
|
||||
item.status = ScheduleItemStatus.DONE
|
||||
item.end_processing = datetime.datetime.now().replace(microsecond=0)
|
||||
|
||||
qd.put(item)
|
||||
|
||||
return
|
||||
|
||||
#time.sleep(5)
|
||||
#raise TaskTerminationException()
|
||||
|
||||
if __name__ == "__main__":
|
||||
# If this script is run, only Rocketry is run
|
||||
app.run()
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import os
|
||||
from hyperdeck.application import HyperDeck
|
||||
import logging
|
||||
|
||||
|
||||
|
||||
logger = logging.getLogger('tasklogger')
|
||||
|
||||
def transcodeFile(name) -> bool:
|
||||
|
||||
app = HyperDeck()
|
||||
|
||||
filename= name + ".mp4"
|
||||
|
||||
tofind = os.path.join(app.config.videopath_originals, '*' + name + ".mp4")
|
||||
|
||||
convertedfile = os.path.join(app.config.videopath_convert, 'c_' + filename)
|
||||
targetfile = os.path.join(app.config.videopath, 'c_' + filename)
|
||||
|
||||
r= os.system('for f in ' + tofind + '; do echo "file \'$f\'" ; done > /tmp/processlist.txt')
|
||||
|
||||
logger.info("Transcode file " + filename)
|
||||
|
||||
logfile = app.config.log_path + name +'.log'
|
||||
r= os.system('/usr/bin/ffmpeg -hide_banner -loglevel warning -nostats -f concat -safe 0 -i /tmp/processlist.txt -y -c:v libx264 -preset medium -b:v 3000k -maxrate 3000k -bufsize 6000k -vf "scale=1280:-1,format=yuv420p" -g 50 -c:a aac -b:a 128k -ac 2 -ar 44100 -threads 2 ' + convertedfile + ' > ' + logfile)
|
||||
logger.info("Converting done for file " + filename)
|
||||
|
||||
|
||||
if not os.path.isfile(convertedfile):
|
||||
logger.error("Converted file {} not found, transcode failed".format(convertedfile))
|
||||
return False
|
||||
os.rename(convertedfile , targetfile)
|
||||
|
||||
return r==0
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
|
||||
class Singleton(type):
|
||||
_instances = {}
|
||||
def __call__(cls, *args, **kwargs):
|
||||
if cls not in cls._instances:
|
||||
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
|
||||
return cls._instances[cls]
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import getopt
|
||||
from hyperdeck.application import HyperDeck
|
||||
import asyncio
|
||||
import uvicorn
|
||||
|
||||
|
||||
from hyperdeck.db import create_db_and_tables
|
||||
from hyperdeck.jsondb import jsonconverter
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
opts, args = getopt.getopt(sys.argv[1:], "c:o:")
|
||||
configfile = "config.json"
|
||||
for opt, arg in opts:
|
||||
if opt == "-h":
|
||||
print("test.py -c <config>")
|
||||
sys.exit()
|
||||
elif opt in ("-c"):
|
||||
configfile = arg
|
||||
|
||||
create_db_and_tables()
|
||||
|
||||
hApp = HyperDeck()
|
||||
hApp.load_config(configfile)
|
||||
|
||||
if os.path.exists("schedule.json"):
|
||||
logging.info("Found schedule.json, loading")
|
||||
conv = jsonconverter()
|
||||
conv.loadschedule()
|
||||
logging.info("Loading done, creating database")
|
||||
|
||||
hApp.schedule.saveItems(conv.schedule)
|
||||
|
||||
logging.info("Database ready, moving file")
|
||||
os.rename("schedule.json","schedule.json.bak")
|
||||
|
||||
|
||||
from hyperdeck.api import app as app_fastapi
|
||||
from hyperdeck.scheduler import app as app_rocketry
|
||||
|
||||
|
||||
|
||||
session = app_rocketry.session
|
||||
|
||||
|
||||
class Server(uvicorn.Server):
|
||||
def handle_exit(self, sig: int, frame) -> None:
|
||||
app_rocketry.session.shut_down()
|
||||
return super().handle_exit(sig, frame)
|
||||
|
||||
|
||||
async def app():
|
||||
server = Server(
|
||||
config=uvicorn.Config(
|
||||
app_fastapi, workers=1, host=["0.0.0.0", "::"], port=8180, loop="asyncio"
|
||||
)
|
||||
)
|
||||
|
||||
api = asyncio.create_task(server.serve())
|
||||
sched = asyncio.create_task(app_rocketry.serve())
|
||||
|
||||
await asyncio.wait([sched, api])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
asyncio.run(app())
|
||||
@@ -0,0 +1 @@
|
||||
{ "version" : "0.15.2" }
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
anyio==4.10.0
|
||||
click==8.1.3
|
||||
fastapi==0.116.1
|
||||
h11==0.16.0
|
||||
idna==3.10
|
||||
sniffio==1.3.0
|
||||
starlette==0.47.3
|
||||
typing_extensions==4.15.0
|
||||
pydantic==1.10.10
|
||||
uvicorn
|
||||
jinja2
|
||||
python-multipart
|
||||
requests
|
||||
rocketry==2.4.1
|
||||
opencv-python
|
||||
ffmpeg-python
|
||||
sqlmodel
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
import sys, getopt,os
|
||||
from hyperdeck.application import Config, HyperDeck
|
||||
import logging
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
||||
opts, args = getopt.getopt(sys.argv[1:],"c:o:")
|
||||
configfile="config.json"
|
||||
for opt, arg in opts:
|
||||
if opt == '-h':
|
||||
print ('test.py -c <config>')
|
||||
sys.exit()
|
||||
elif opt in ("-c"):
|
||||
configfile = arg
|
||||
|
||||
config = Config(configfile)
|
||||
|
||||
logging.info("Setting up connection to: " +config.rtsp_url )
|
||||
|
||||
|
||||
os.system('/usr/bin/ffmpeg -hide_banner -loglevel warning -nostats -re -rtsp_transport tcp -i '+ config.rtsp_url +' -c copy -s hd720 -f flv -y rtmp://localhost/live/livestream > /tmp/ffmpeglive.log')
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
from rocketry import Rocketry
|
||||
|
||||
app = Rocketry()
|
||||
|
||||
@app.task('daily')
|
||||
def do_things():
|
||||
print("dd")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
CONTROL_FILE=$(ls -1 *.control)
|
||||
|
||||
echo "Using control file $CONTROL_FILE"
|
||||
PACKAGE_NAME=$(awk '/^Package: /{print $NF}' $CONTROL_FILE)
|
||||
VERSION=$(awk '/^Version: /{print $NF}' $CONTROL_FILE)
|
||||
|
||||
|
||||
|
||||
|
||||
# tag git repo
|
||||
# git tag -f release-${VERSION}
|
||||
|
||||
echo "Building package $PACKAGE_NAME ($VERSION): $PACKAGE_NAME-${VERSION}_all.deb"
|
||||
# VERSION
|
||||
sed "s/{VERSION}/${VERSION}/" $PACKAGE_NAME.control > packaging/$PACKAGE_NAME/DEBIAN/control
|
||||
|
||||
#create debian package
|
||||
dpkg --build packaging/$PACKAGE_NAME $PACKAGE_NAME-${VERSION}_all.deb
|
||||
|
||||
# dpkg-sig -k 14AF7F05 --sign builder icosi-${VERSION}_all.deb
|
||||
|
||||
echo "clean up"
|
||||
rm packaging/$PACKAGE_NAME/DEBIAN/control
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
Package: eendoe-repository
|
||||
Version: 1.0
|
||||
Section: utils
|
||||
Maintainer: Bart Meijer
|
||||
Architecture: all
|
||||
Depends:
|
||||
Description: Eendoe package repository
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
case "$1" in
|
||||
configure|abort-upgrade)
|
||||
|
||||
esac
|
||||
exit 0
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
case "$1" in
|
||||
install|upgrade)
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
#Eendoe APT package list
|
||||
|
||||
deb [arch=amd64] https://repository.eendoe.nl/ stable main
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
#Eendoe APT package list
|
||||
|
||||
deb [arch=amd64] https://repository.eendoe.nl/ stable main
|
||||
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
CONTROL_FILE=$(ls -1 *.control)
|
||||
|
||||
echo "Using control file $CONTROL_FILE"
|
||||
PACKAGE_NAME=$(awk '/^Package: /{print $NF}' $CONTROL_FILE)
|
||||
VERSION=$(awk '/^Version: /{print $NF}' $CONTROL_FILE)
|
||||
|
||||
|
||||
|
||||
|
||||
# tag git repo
|
||||
# git tag -f release-${VERSION}
|
||||
|
||||
echo "Building package $PACKAGE_NAME ($VERSION): $PACKAGE_NAME-${VERSION}_all.deb"
|
||||
# VERSION
|
||||
sed "s/{VERSION}/${VERSION}/" $PACKAGE_NAME.control > packaging/$PACKAGE_NAME/DEBIAN/control
|
||||
|
||||
#create debian package
|
||||
dpkg --build packaging/$PACKAGE_NAME $PACKAGE_NAME-${VERSION}_all.deb
|
||||
|
||||
# dpkg-sig -k 14AF7F05 --sign builder icosi-${VERSION}_all.deb
|
||||
|
||||
echo "clean up"
|
||||
rm packaging/$PACKAGE_NAME/DEBIAN/control
|
||||
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
Package: eendoe-srs
|
||||
Version: {VERSION}
|
||||
Section: utils
|
||||
Maintainer: nl.eendoe.bart
|
||||
Architecture: all
|
||||
Depends: python3, python3-pip, ffmpeg
|
||||
Description: Eendoe Hyperdeck SRS module
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
case "$1" in
|
||||
configure|abort-upgrade)
|
||||
|
||||
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
case "$1" in
|
||||
install|upgrade)
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
#!/bin/bash
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: ossrs(srs)
|
||||
# Required-Start: $all
|
||||
# Required-Stop: $all
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: ossrs(srs)
|
||||
# Description: https://github.com/ossrs/srs
|
||||
### END INIT INFO
|
||||
|
||||
# the config of ROOT, user must modify it when start srs from other directory,
|
||||
# it's ok to use the script by command ./etc/init.d/ossrs
|
||||
ROOT="/usr/share/srs"
|
||||
APP="/usr/share/srs/objs/srs"
|
||||
CONFIG="/etc/srs/srs.conf"
|
||||
DEFAULT_PID_FILE='/tmp/srs.pid'
|
||||
DEFAULT_LOG_FILE='/tmp/srs.log'
|
||||
|
||||
########################################################################
|
||||
# utility functions
|
||||
########################################################################
|
||||
RED="\\033[31m"
|
||||
GREEN="\\033[32m"
|
||||
YELLOW="\\033[33m"
|
||||
BLACK="\\033[0m"
|
||||
POS="\\033[60G"
|
||||
|
||||
ok_msg() {
|
||||
echo -e "${1}${POS}${BLACK}[${GREEN} OK ${BLACK}]"
|
||||
}
|
||||
|
||||
failed_msg() {
|
||||
echo -e "${1}${POS}${BLACK}[${RED}FAILED${BLACK}]"
|
||||
}
|
||||
|
||||
# load process info of srs
|
||||
# @set variable $srs_pid to the process id in srs.pid file.
|
||||
# @return 0, if process exists; otherwise:
|
||||
# 1, for pid file not exists.
|
||||
# 2, for get proecess info by pid failed.
|
||||
# @set variable $error_msg if error.
|
||||
# @set variable $pid_file to pid file.
|
||||
load_process_info() {
|
||||
# get pid file
|
||||
pid_file=`cd ${ROOT} && cat ${CONFIG} |grep ^pid|awk '{print $2}'|awk -F ';' '{print $1}'`
|
||||
if [[ -z $pid_file ]]; then pid_file=${DEFAULT_PID_FILE}; fi
|
||||
# get abs path
|
||||
pid_dir=`dirname $pid_file`
|
||||
pid_file=`(cd ${ROOT}; cd $pid_dir; pwd)`/`basename $pid_file`
|
||||
|
||||
srs_pid=`cat $pid_file 2>/dev/null`
|
||||
ret=$?; if [[ 0 -ne $ret ]]; then error_msg="No pid file $pid_file"; return 1; fi
|
||||
|
||||
ps -p ${srs_pid} >/dev/null 2>/dev/null
|
||||
ret=$?; if [[ 0 -ne $ret ]]; then error_msg="Process $srs_pid does not exists"; return 2; fi
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
start() {
|
||||
# if exists, exit.
|
||||
load_process_info
|
||||
if [[ 0 -eq $? ]]; then failed_msg "SRS started(pid ${srs_pid}), should not start it again."; return 1; fi
|
||||
|
||||
# not exists, start server
|
||||
ok_msg "Starting SRS..."
|
||||
|
||||
# get log file
|
||||
log_file=`cd ${ROOT} && cat ${CONFIG} |grep '^srs_log_file'| awk '{print $2}'| awk -F ';' '{print $1}'`
|
||||
if [[ -z $log_file ]]; then log_file=${DEFAULT_LOG_FILE}; fi
|
||||
# get abs path
|
||||
log_dir=`dirname $log_file`
|
||||
log_file=`(cd ${ROOT} && cd $log_dir && pwd)`/`basename $log_file`
|
||||
|
||||
# TODO: FIXME: set limit by, for instance, "ulimit -HSn 10000"
|
||||
if [[ -z $log_file ]]; then
|
||||
(ulimit -c unlimited && cd ${ROOT}; ${APP} -c ${CONFIG} >/dev/null 2>&1)
|
||||
else
|
||||
(ulimit -c unlimited && cd ${ROOT}; ${APP} -c ${CONFIG} >> $log_file.sys 2>&1)
|
||||
fi
|
||||
|
||||
# check again after start server
|
||||
for ((i = 0; i < 5; i++)); do
|
||||
# sleep a little while, for srs may start then crash.
|
||||
sleep 0.1
|
||||
load_process_info
|
||||
ret=$?; if [[ 0 -ne $ret ]]; then
|
||||
failed_msg "SRS start failed";
|
||||
failed_msg "see $log_file";
|
||||
return $ret;
|
||||
fi
|
||||
done
|
||||
|
||||
# check whether started.
|
||||
load_process_info
|
||||
ret=$?; if [[ 0 -eq $ret ]]; then ok_msg "SRS started(pid ${srs_pid})"; return 0; fi
|
||||
|
||||
failed_msg "SRS not started"
|
||||
return $ret
|
||||
}
|
||||
|
||||
stop() {
|
||||
# not start, exit
|
||||
load_process_info
|
||||
if [[ 0 -ne $? ]]; then failed_msg "SRS not start."; return 0; fi
|
||||
|
||||
ok_msg "Stopping SRS(pid ${srs_pid})..."
|
||||
|
||||
# process exists, try to kill to stop normally
|
||||
for((i=0;i<100;i++)); do
|
||||
load_process_info
|
||||
if [[ 0 -eq $? ]]; then
|
||||
kill -s SIGTERM ${srs_pid} 2>/dev/null
|
||||
ret=$?; if [[ 0 -ne $ret ]]; then failed_msg "send signal SIGTERM failed ret=$ret"; return $ret; fi
|
||||
sleep 0.3
|
||||
else
|
||||
ok_msg "SRS stopped by SIGTERM"
|
||||
# delete the pid file when stop success.
|
||||
rm -f ${pid_file}
|
||||
break;
|
||||
fi
|
||||
done
|
||||
|
||||
# process exists, use kill -9 to force to exit
|
||||
load_process_info
|
||||
if [[ 0 -eq $? ]]; then
|
||||
kill -s SIGKILL ${srs_pid} 2>/dev/null
|
||||
ret=$?; if [[ 0 -ne $ret ]]; then failed_msg "send signal SIGKILL failed ret=$ret"; return $ret; fi
|
||||
ok_msg "SRS stopped by SIGKILL"
|
||||
else
|
||||
# delete the pid file when stop success.
|
||||
rm -f ${pid_file}
|
||||
fi
|
||||
|
||||
sleep 0.1
|
||||
return 0
|
||||
}
|
||||
|
||||
# get the status of srs process
|
||||
# @return 0 if srs is running; otherwise, 1 for stopped.
|
||||
status() {
|
||||
load_process_info
|
||||
ret=$?; if [[ 0 -eq $ret ]]; then ok_msg "SRS(pid ${srs_pid}) is running."; return 0; fi
|
||||
|
||||
failed_msg "Error: $error_msg"
|
||||
return 1
|
||||
}
|
||||
|
||||
reload() {
|
||||
# not start, exit
|
||||
load_process_info
|
||||
if [[ 0 -ne $? ]]; then failed_msg "SRS not start."; return 0; fi
|
||||
|
||||
ok_msg "Reload SRS(pid ${srs_pid})..."
|
||||
|
||||
# process exists, reload it
|
||||
kill -s SIGHUP ${srs_pid} 2>/dev/null
|
||||
ret=$?; if [[ 0 -ne $ret ]]; then failed_msg "Reload SRS failed ret=$ret"; return $ret; fi
|
||||
|
||||
load_process_info
|
||||
if [[ 0 -ne $? ]]; then failed_msg "SRS reload failed."; return $ret; fi
|
||||
|
||||
ok_msg "SRS reloaded"
|
||||
return 0
|
||||
}
|
||||
|
||||
logrotate() {
|
||||
# not start, exit
|
||||
load_process_info
|
||||
if [[ 0 -ne $? ]]; then failed_msg "SRS not start."; return 0; fi
|
||||
|
||||
ok_msg "Reopen log file of SRS(pid ${srs_pid})..."
|
||||
kill -s SIGUSR1 ${srs_pid}
|
||||
|
||||
ok_msg "Log rotated"
|
||||
return 0
|
||||
}
|
||||
|
||||
grace() {
|
||||
# not start, exit
|
||||
load_process_info
|
||||
if [[ 0 -ne $? ]]; then failed_msg "SRS not start."; return 0; fi
|
||||
|
||||
ok_msg "Gracefully quit for SRS(pid ${srs_pid})..."
|
||||
kill -s SIGQUIT ${srs_pid}
|
||||
|
||||
ok_msg "Gracefully quit"
|
||||
return 0
|
||||
}
|
||||
|
||||
menu() {
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart)
|
||||
stop
|
||||
start
|
||||
;;
|
||||
status)
|
||||
status
|
||||
;;
|
||||
reload)
|
||||
reload
|
||||
;;
|
||||
rotate)
|
||||
logrotate
|
||||
;;
|
||||
grace)
|
||||
grace
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|status|restart|reload|rotate|grace}"
|
||||
echo " reload Apply log file by not restarting SRS"
|
||||
echo " rotate For log rotate, to send SIGUSR1 to SRS to reopen the log file."
|
||||
echo " grace For gracefully quit, to send SIGQUIT to SRS."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
menu $1
|
||||
|
||||
code=$?
|
||||
exit ${code}
|
||||
@@ -0,0 +1,47 @@
|
||||
# main config for srs.
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
|
||||
pid /tmp/srs.pid;
|
||||
#srs_log_tank file;
|
||||
srs_log_file /usr/share/srs/objs/srs.log;
|
||||
daemon on;
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 1985;
|
||||
}
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir /usr/share/srs/objs/nginx/html;
|
||||
}
|
||||
rtc_server {
|
||||
enabled on;
|
||||
listen 8000; # UDP port
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate
|
||||
candidate $CANDIDATE;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
hls {
|
||||
enabled on;#
|
||||
hls_m3u8_file recorder1/[app]/[stream].m3u8;
|
||||
}
|
||||
http_remux {
|
||||
enabled on;
|
||||
fast_cache 10;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
}
|
||||
rtc {
|
||||
enabled on;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtmp-to-rtc
|
||||
rtmp_to_rtc off;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtc-to-rtmp
|
||||
rtc_to_rtmp off;
|
||||
}
|
||||
|
||||
play{
|
||||
gop_cache_max_frames 2500;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#
|
||||
# Systemd unit file for Hyperdeck recorder
|
||||
#
|
||||
|
||||
[Unit]
|
||||
Description=Eendoe Hyperdeck live feed
|
||||
After=network.target
|
||||
RequiresMountsFor=/mnt/video
|
||||
|
||||
[Service]
|
||||
|
||||
|
||||
# Lifecycle
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/python3 /usr/share/hyperdeck/srsfeed.py -c /etc/hyperdeck/config.json
|
||||
SuccessExitStatus=0
|
||||
Restart=on-abort
|
||||
WorkingDirectory=/usr/share/hyperdeck/
|
||||
|
||||
# Logging
|
||||
SyslogIdentifier=hyperdeck_srs
|
||||
|
||||
# Security
|
||||
User=vid
|
||||
Group=vid
|
||||
PrivateTmp=yes
|
||||
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
}
|
||||
stats {
|
||||
network 0;
|
||||
}
|
||||
rtc_server {
|
||||
enabled on;
|
||||
# Listen at udp://8000
|
||||
listen 8000;
|
||||
#
|
||||
# The $CANDIDATE means fetch from env, if not configed, use * as default.
|
||||
#
|
||||
# The * means retrieving server IP automatically, from all network interfaces,
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate
|
||||
candidate $CANDIDATE;
|
||||
}
|
||||
|
||||
vhost __defaultVhost__ {
|
||||
rtc {
|
||||
enabled on;
|
||||
}
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
}
|
||||
ingest livestream {
|
||||
enabled on;
|
||||
input {
|
||||
type file;
|
||||
url ./doc/source.200kbps.768x320.flv;
|
||||
}
|
||||
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
|
||||
engine {
|
||||
enabled off;
|
||||
output rtmp://127.0.0.1:[port]/live/livestream;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# SRT config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
}
|
||||
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
|
||||
srt_server {
|
||||
enabled on;
|
||||
listen 10080;
|
||||
maxbw 1000000000;
|
||||
connect_timeout 4000;
|
||||
peerlatency 0;
|
||||
recvlatency 0;
|
||||
latency 0;
|
||||
tsbpdmode off;
|
||||
tlpktdrop off;
|
||||
sendbuf 2000000;
|
||||
recvbuf 2000000;
|
||||
}
|
||||
|
||||
# @doc https://github.com/ossrs/srs/issues/1147#issuecomment-577607026
|
||||
vhost __defaultVhost__ {
|
||||
srt {
|
||||
enabled on;
|
||||
srt_to_rtmp on;
|
||||
}
|
||||
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
}
|
||||
}
|
||||
|
||||
# For SRT to use vhost.
|
||||
vhost srs.srt.com.cn {
|
||||
}
|
||||
|
||||
stats {
|
||||
network 0;
|
||||
disk sda sdb xvda xvdb;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
}
|
||||
stats {
|
||||
network 0;
|
||||
}
|
||||
rtc_server {
|
||||
enabled on;
|
||||
# Listen at udp://8000
|
||||
listen 8000;
|
||||
#
|
||||
# The $CANDIDATE means fetch from env, if not configed, use * as default.
|
||||
#
|
||||
# The * means retrieving server IP automatically, from all network interfaces,
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate
|
||||
candidate $CANDIDATE;
|
||||
}
|
||||
|
||||
srt_server {
|
||||
enabled on;
|
||||
listen 10080;
|
||||
maxbw 1000000000;
|
||||
connect_timeout 4000;
|
||||
peerlatency 0;
|
||||
recvlatency 0;
|
||||
latency 0;
|
||||
tsbpdmode off;
|
||||
tlpktdrop off;
|
||||
sendbuf 2000000;
|
||||
recvbuf 2000000;
|
||||
}
|
||||
|
||||
vhost __defaultVhost__ {
|
||||
rtc {
|
||||
enabled on;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtmp-to-rtc
|
||||
rtmp_to_rtc on;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtc-to-rtmp
|
||||
rtc_to_rtmp on;
|
||||
}
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
}
|
||||
hls {
|
||||
enabled on;
|
||||
}
|
||||
srt {
|
||||
enabled on;
|
||||
srt_to_rtmp on;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
listen 1935;
|
||||
pid ./objs/srs.pid;
|
||||
srs_log_tank console;
|
||||
srs_log_level trace;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 1985;
|
||||
crossdomain on;
|
||||
raw_api {
|
||||
enabled on;
|
||||
allow_reload on;
|
||||
allow_query on;
|
||||
allow_update on;
|
||||
}
|
||||
}
|
||||
# for SRS1.
|
||||
http_stream {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
# for SRS1.
|
||||
http {
|
||||
enabled on;
|
||||
mount [vhost]/hls;
|
||||
dir ./objs/nginx/html/hls;
|
||||
}
|
||||
|
||||
# for SRS1.
|
||||
refer github.com github.io;
|
||||
refer_publish github.com github.io;
|
||||
refer_play github.com github.io;
|
||||
|
||||
# for SRS2
|
||||
publish_1stpkt_timeout 20000;
|
||||
publish_normal_timeout 7000;
|
||||
|
||||
# for SRS2
|
||||
mr {
|
||||
enabled off;
|
||||
latency 350;
|
||||
}
|
||||
|
||||
# for SRS1
|
||||
mode remote;
|
||||
origin 127.0.0.1:1935 localhost:1935;
|
||||
|
||||
token_traverse off;
|
||||
vhost same.edge.srs.com;
|
||||
|
||||
debug_srs_upnode off;
|
||||
|
||||
# for SRS1
|
||||
forward 127.0.0.1:1936 127.0.0.1:1937;
|
||||
|
||||
# for SRS1
|
||||
time_jitter full;
|
||||
|
||||
# for SRS2
|
||||
mix_correct off;
|
||||
|
||||
#for SRS1
|
||||
atc on;
|
||||
atc_auto on;
|
||||
|
||||
# for SRS2
|
||||
mw_latency 100;
|
||||
|
||||
# for SRS1
|
||||
gop_cache off;
|
||||
queue_length 10;
|
||||
|
||||
# for SRS2
|
||||
send_min_interval 10.0;
|
||||
reduce_sequence_header on;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
# no-daemon and write log to console config for srs.
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 1985;
|
||||
}
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
}
|
||||
rtc_server {
|
||||
enabled on;
|
||||
listen 8000; # UDP port
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate
|
||||
candidate $CANDIDATE;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
hls {
|
||||
enabled on;
|
||||
}
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
}
|
||||
rtc {
|
||||
enabled on;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtmp-to-rtc
|
||||
rtmp_to_rtc on;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtc-to-rtmp
|
||||
rtc_to_rtmp on;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
# no-daemon and write log to console config for srs.
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935 [::]:1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 1985;
|
||||
}
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# the config for srs to delivery dash
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/sample-dash
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
dash {
|
||||
enabled on;
|
||||
dash_fragment 10;
|
||||
dash_update_period 10;
|
||||
dash_window_size 3;
|
||||
dash_path ./objs/nginx/html;
|
||||
dash_mpd_file [app]/[stream].mpd;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
# the config for srs demo
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 19350;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
pid ./objs/srs.demo.19350.pid;
|
||||
|
||||
vhost __defaultVhost__ {
|
||||
enabled on;
|
||||
play {
|
||||
gop_cache on;
|
||||
}
|
||||
hls {
|
||||
enabled on;
|
||||
hls_path ./objs/nginx/html/forward;
|
||||
hls_fragment 5;
|
||||
hls_window 30;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
# the config for srs demo
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
pid ./objs/srs.demo.pid;
|
||||
|
||||
chunk_size 60000;
|
||||
max_connections 2000;
|
||||
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 1985;
|
||||
}
|
||||
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
|
||||
vhost __defaultVhost__ {
|
||||
enabled on;
|
||||
gop_cache on;
|
||||
}
|
||||
|
||||
vhost demo.srs.com {
|
||||
chunk_size 60000;
|
||||
enabled on;
|
||||
gop_cache on;
|
||||
queue_length 30;
|
||||
forward {
|
||||
enabled on;
|
||||
destination 127.0.0.1:19350;
|
||||
}
|
||||
hls {
|
||||
enabled on;
|
||||
hls_path ./objs/nginx/html;
|
||||
hls_fragment 5;
|
||||
hls_window 30;
|
||||
}
|
||||
http_hooks {
|
||||
enabled on;
|
||||
on_publish http://127.0.0.1:8085/api/v1/streams;
|
||||
on_unpublish http://127.0.0.1:8085/api/v1/streams;
|
||||
on_play http://127.0.0.1:8085/api/v1/sessions;
|
||||
on_stop http://127.0.0.1:8085/api/v1/sessions;
|
||||
}
|
||||
transcode {
|
||||
enabled on;
|
||||
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
|
||||
engine ld {
|
||||
enabled on;
|
||||
vfilter {
|
||||
i ./doc/srs-logo.png;
|
||||
filter_complex 'overlay=10:10';
|
||||
}
|
||||
vcodec libx264;
|
||||
vbitrate 300;
|
||||
vfps 20;
|
||||
vwidth 768;
|
||||
vheight 320;
|
||||
vthreads 1;
|
||||
vprofile baseline;
|
||||
vpreset superfast;
|
||||
vparams {
|
||||
}
|
||||
acodec libfdk_aac;
|
||||
abitrate 45;
|
||||
asample_rate 44100;
|
||||
achannels 2;
|
||||
aparams {
|
||||
}
|
||||
output rtmp://127.0.0.1:[port]/[app]/[stream]_[engine]?vhost=[vhost];
|
||||
}
|
||||
engine sd {
|
||||
enabled on;
|
||||
vfilter {
|
||||
vf 'split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2';
|
||||
}
|
||||
vcodec libx264;
|
||||
vbitrate 500;
|
||||
vfps 20;
|
||||
vwidth 768;
|
||||
vheight 320;
|
||||
vthreads 1;
|
||||
vprofile main;
|
||||
vpreset fast;
|
||||
vparams {
|
||||
}
|
||||
acodec libfdk_aac;
|
||||
abitrate 40;
|
||||
asample_rate 44100;
|
||||
achannels 2;
|
||||
aparams {
|
||||
}
|
||||
output rtmp://127.0.0.1:[port]/[app]/[stream]_[engine]?vhost=[vhost];
|
||||
}
|
||||
}
|
||||
ingest {
|
||||
enabled on;
|
||||
input {
|
||||
type file;
|
||||
url ./doc/source.200kbps.768x320.flv;
|
||||
}
|
||||
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
|
||||
engine {
|
||||
enabled off;
|
||||
output rtmp://127.0.0.1:[port]/live/livestream?vhost=[vhost];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vhost players {
|
||||
enabled on;
|
||||
gop_cache on;
|
||||
transcode {
|
||||
enabled on;
|
||||
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
|
||||
engine hls {
|
||||
enabled on;
|
||||
vfilter {
|
||||
i ./doc/srs-logo.png;
|
||||
filter_complex 'overlay=10:10';
|
||||
}
|
||||
vcodec libx264;
|
||||
vbitrate 300;
|
||||
vfps 20;
|
||||
vwidth 768;
|
||||
vheight 320;
|
||||
vthreads 1;
|
||||
vprofile baseline;
|
||||
vpreset superfast;
|
||||
vparams {
|
||||
g 100;
|
||||
}
|
||||
acodec libfdk_aac;
|
||||
abitrate 30;
|
||||
asample_rate 44100;
|
||||
achannels 2;
|
||||
aparams {
|
||||
}
|
||||
output rtmp://127.0.0.1:[port]/[app]?vhost=players_pub/[stream];
|
||||
}
|
||||
}
|
||||
ingest {
|
||||
enabled on;
|
||||
input {
|
||||
type file;
|
||||
url ./doc/source.200kbps.768x320.flv;
|
||||
}
|
||||
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
|
||||
engine {
|
||||
enabled off;
|
||||
output rtmp://127.0.0.1:[port]/live/demo?vhost=[vhost];
|
||||
}
|
||||
}
|
||||
}
|
||||
vhost players_pub {
|
||||
hls {
|
||||
enabled on;
|
||||
hls_path ./objs/nginx/html;
|
||||
hls_fragment 5;
|
||||
hls_window 30;
|
||||
}
|
||||
}
|
||||
|
||||
vhost players_chat {
|
||||
gop_cache off;
|
||||
hls {
|
||||
enabled off;
|
||||
}
|
||||
transcode {
|
||||
enabled on;
|
||||
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
|
||||
engine chat_hls {
|
||||
enabled on;
|
||||
vfilter {
|
||||
}
|
||||
vcodec libx264;
|
||||
vbitrate 150;
|
||||
vfps 15;
|
||||
vwidth 576;
|
||||
vheight 240;
|
||||
vthreads 1;
|
||||
vprofile baseline;
|
||||
vpreset ultrafast;
|
||||
vparams {
|
||||
g 30;
|
||||
}
|
||||
acodec an;
|
||||
output rtmp://127.0.0.1:[port]/[app]?vhost=chat_hls/[stream];
|
||||
}
|
||||
}
|
||||
}
|
||||
vhost chat_hls {
|
||||
hls {
|
||||
enabled on;
|
||||
hls_path ./objs/nginx/html;
|
||||
hls_fragment 2;
|
||||
hls_window 10;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# docker config for srs.
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
# For docker, please use docker logs to manage the logs of SRS.
|
||||
# See https://docs.docker.com/config/containers/logging/
|
||||
srs_log_tank console;
|
||||
daemon off;
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 1985;
|
||||
}
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
rtc_server {
|
||||
enabled on;
|
||||
listen 8000;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate
|
||||
candidate $CANDIDATE;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
hls {
|
||||
enabled on;
|
||||
}
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
}
|
||||
rtc {
|
||||
enabled on;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtmp-to-rtc
|
||||
rtmp_to_rtc on;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtc-to-rtmp
|
||||
rtc_to_rtmp on;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# the config for srs to dvr in session mode
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/dvr
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
vhost __defaultVhost__ {
|
||||
dvr {
|
||||
enabled on;
|
||||
dvr_path ./objs/nginx/html/[app]/[stream].[timestamp].mp4;
|
||||
dvr_plan session;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
# the config for srs to dvr in custom path.
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/dvr#custom-path
|
||||
# @see https://ossrs.io/lts/en-us/docs/v4/doc/dvr#custom-path
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
vhost __defaultVhost__ {
|
||||
dvr {
|
||||
enabled on;
|
||||
dvr_path ./objs/nginx/html/[app]/[stream]/[2006]/[01]/[02]/[15].[04].[05].[999].flv;
|
||||
dvr_plan segment;
|
||||
dvr_duration 30;
|
||||
dvr_wait_keyframe on;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
# the config for srs to dvr in segment mode
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/dvr
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
vhost __defaultVhost__ {
|
||||
dvr {
|
||||
enabled on;
|
||||
dvr_path ./objs/nginx/html/[app]/[stream].[timestamp].flv;
|
||||
dvr_plan segment;
|
||||
dvr_duration 30;
|
||||
dvr_wait_keyframe on;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# the config for srs to dvr in session mode
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/dvr
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
vhost __defaultVhost__ {
|
||||
dvr {
|
||||
enabled on;
|
||||
dvr_path ./objs/nginx/html/[app]/[stream].[timestamp].flv;
|
||||
dvr_plan session;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
# the config for srs origin-edge cluster
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/edge
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
pid objs/edge.pid;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
cluster {
|
||||
mode remote;
|
||||
origin 127.0.0.1:19350;
|
||||
}
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# the config for srs for token traverse authentication
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/drm
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
vhost __defaultVhost__ {
|
||||
cluster {
|
||||
mode remote;
|
||||
origin 127.0.0.1:19350;
|
||||
token_traverse on;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# the config for srs origin-edge cluster
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/edge
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
pid objs/edge2.pid;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
vhost __defaultVhost__ {
|
||||
cluster {
|
||||
mode remote;
|
||||
origin 127.0.0.1:19350;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# the config for srs to support nginx-rtmp exec.
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/nginx-exec
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
vhost __defaultVhost__ {
|
||||
exec {
|
||||
enabled on;
|
||||
publish ./objs/ffmpeg/bin/ffmpeg -f flv -i [url] -c copy -y ./[stream].flv;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
# the config for srs use ffmpeg to transcode
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/sample-ffmpeg
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
vhost __defaultVhost__ {
|
||||
transcode {
|
||||
enabled on;
|
||||
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
|
||||
engine ff {
|
||||
enabled on;
|
||||
vfilter {
|
||||
}
|
||||
vcodec libx264;
|
||||
vthreads 4;
|
||||
vprofile main;
|
||||
vpreset medium;
|
||||
vparams {
|
||||
}
|
||||
acodec libfdk_aac;
|
||||
aparams {
|
||||
}
|
||||
output rtmp://127.0.0.1:[port]/[app]/[stream]_[engine]?vhost=[vhost];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon on; disable_daemon_for_docker off; srs_log_tank file;
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 1985;
|
||||
}
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
rtc_server {
|
||||
enabled on;
|
||||
listen 8000; # UDP port
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate
|
||||
candidate $CANDIDATE;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
hls {
|
||||
enabled on;
|
||||
}
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
}
|
||||
rtc {
|
||||
enabled on;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtmp-to-rtc
|
||||
rtmp_to_rtc off;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtc-to-rtmp
|
||||
rtc_to_rtmp off;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# the config for srs to forward to slave service
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/sample-forward
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
pid ./objs/srs.backend.pid;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
vhost __defaultVhost__ {
|
||||
forward {
|
||||
enabled on;
|
||||
backend http://127.0.0.1:8085/api/v1/forward;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# the config for srs to forward
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/sample-forward
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
pid ./objs/srs.master.pid;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
vhost __defaultVhost__ {
|
||||
forward {
|
||||
enabled on;
|
||||
destination 127.0.0.1:19350;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# the config for srs to forward
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/sample-forward
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 19350;
|
||||
max_connections 1000;
|
||||
pid ./objs/srs.slave.pid;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
vhost __defaultVhost__ {
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,56 @@
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
|
||||
stream_caster {
|
||||
enabled on;
|
||||
caster gb28181;
|
||||
output rtmp://127.0.0.1/live/[stream];
|
||||
listen 9000;
|
||||
sip {
|
||||
enabled on;
|
||||
listen 5060;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v5/doc/gb28181#config-candidate
|
||||
candidate $CANDIDATE;
|
||||
}
|
||||
}
|
||||
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 1985;
|
||||
}
|
||||
stats {
|
||||
network 0;
|
||||
}
|
||||
rtc_server {
|
||||
enabled on;
|
||||
listen 8000; # UDP port
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate
|
||||
candidate $CANDIDATE;
|
||||
}
|
||||
|
||||
vhost __defaultVhost__ {
|
||||
rtc {
|
||||
enabled on;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtmp-to-rtc
|
||||
rtmp_to_rtc on;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtc-to-rtmp
|
||||
rtc_to_rtmp on;
|
||||
}
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
}
|
||||
hls {
|
||||
enabled on;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# the config for srs origin-edge cluster
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/edge
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 19351;
|
||||
max_connections 1000;
|
||||
pid objs/edge1.pid;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
vhost __defaultVhost__ {
|
||||
cluster {
|
||||
mode remote;
|
||||
origin 127.0.0.1:19350;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# the config for srs origin-edge cluster
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/edge
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 19352;
|
||||
max_connections 1000;
|
||||
pid objs/edge2.pid;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
vhost __defaultVhost__ {
|
||||
cluster {
|
||||
mode remote;
|
||||
origin 127.0.0.1:19350;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
# the config for srs to delivery hds
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
|
||||
vhost __defaultVhost__ {
|
||||
hds {
|
||||
enabled on;
|
||||
hds_fragment 10;
|
||||
hds_window 60;
|
||||
hds_path ./objs/nginx/html;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
srt_server {
|
||||
enabled on;
|
||||
listen 10080;
|
||||
}
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 1985;
|
||||
}
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
srt {
|
||||
enabled on;
|
||||
srt_to_rtmp on;
|
||||
}
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
srt_server {
|
||||
enabled on;
|
||||
listen 10080;
|
||||
}
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 1985;
|
||||
}
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
srt {
|
||||
enabled on;
|
||||
srt_to_rtmp on;
|
||||
}
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].ts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
# the config for srs to delivery hls
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/sample-hls
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
hls {
|
||||
enabled on;
|
||||
hls_path ./objs/nginx/html;
|
||||
hls_fragment 10;
|
||||
hls_window 60;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
|
||||
# Note that this is for NGINX server, not SRS.
|
||||
|
||||
worker_processes 3;
|
||||
events {
|
||||
worker_connections 10240;
|
||||
}
|
||||
|
||||
http {
|
||||
# For Proxy Cache.
|
||||
proxy_cache_path /tmp/nginx-cache levels=1:2 keys_zone=srs_cache:8m max_size=1000m inactive=600m;
|
||||
proxy_temp_path /tmp/nginx-cache/tmp;
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
# For Proxy Cache.
|
||||
proxy_cache_valid 404 10s;
|
||||
proxy_cache_lock on;
|
||||
proxy_cache_lock_age 300s;
|
||||
proxy_cache_lock_timeout 300s;
|
||||
proxy_cache_min_uses 1;
|
||||
|
||||
location ~ /.+/.*\.(m3u8)$ {
|
||||
proxy_pass http://127.0.0.1:8080$request_uri;
|
||||
# For Proxy Cache.
|
||||
proxy_cache srs_cache;
|
||||
proxy_cache_key $scheme$proxy_host$uri$args;
|
||||
proxy_cache_valid 200 302 10s;
|
||||
}
|
||||
location ~ /.+/.*\.(ts)$ {
|
||||
proxy_pass http://127.0.0.1:8080$request_uri;
|
||||
# For Proxy Cache.
|
||||
proxy_cache srs_cache;
|
||||
proxy_cache_key $scheme$proxy_host$uri;
|
||||
proxy_cache_valid 200 302 60m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
hls {
|
||||
enabled on;
|
||||
# Note that it will make NGINX edge cache always missed, so never enable HLS streaming if use NGINX edges.
|
||||
hls_ctx off;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# the config for srs to delivery realtime RTMP stream
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/sample-realtime
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
hls {
|
||||
enabled on;
|
||||
hls_fragment 0.2;
|
||||
hls_window 2;
|
||||
hls_wait_keyframe off;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
# the config for srs to remux rtmp to aac live stream.
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/delivery-http-flv
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
http_remux {
|
||||
enabled on;
|
||||
fast_cache 30;
|
||||
mount [vhost]/[app]/[stream].aac;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 1985;
|
||||
auth {
|
||||
enabled on;
|
||||
username admin;
|
||||
password admin;
|
||||
}
|
||||
}
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
# main config for srs.
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 1985;
|
||||
}
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
rtc_server {
|
||||
enabled on;
|
||||
listen 8000; # UDP port
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate
|
||||
candidate $CANDIDATE;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
hls {
|
||||
enabled on;
|
||||
}
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
}
|
||||
rtc {
|
||||
enabled on;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtmp-to-rtc
|
||||
rtmp_to_rtc off;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtc-to-rtmp
|
||||
rtc_to_rtmp off;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
# main config for srs.
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
}
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
rtc_server {
|
||||
enabled on;
|
||||
listen 8000; # UDP port
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate
|
||||
candidate $CANDIDATE;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
hls {
|
||||
enabled on;
|
||||
}
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
}
|
||||
rtc {
|
||||
enabled on;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtmp-to-rtc
|
||||
rtmp_to_rtc off;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtc-to-rtmp
|
||||
rtc_to_rtmp off;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
# main config for srs.
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
https {
|
||||
enabled on;
|
||||
listen 8088;
|
||||
key ./conf/server.key;
|
||||
cert ./conf/server.crt;
|
||||
}
|
||||
}
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
https {
|
||||
enabled on;
|
||||
listen 8088;
|
||||
key ./conf/server.key;
|
||||
cert ./conf/server.crt;
|
||||
}
|
||||
}
|
||||
rtc_server {
|
||||
enabled on;
|
||||
listen 8000; # UDP port
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#config-candidate
|
||||
candidate $CANDIDATE;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
hls {
|
||||
enabled on;
|
||||
}
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
}
|
||||
rtc {
|
||||
enabled on;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtmp-to-rtc
|
||||
rtmp_to_rtc off;
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/webrtc#rtc-to-rtmp
|
||||
rtc_to_rtmp off;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
# main config for srs.
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_api {
|
||||
enabled on;
|
||||
listen 1985;
|
||||
raw_api {
|
||||
enabled on;
|
||||
allow_reload on;
|
||||
}
|
||||
}
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
stats {
|
||||
network 0;
|
||||
disk sda sdb xvda xvdb;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
# the config for srs to remux rtmp to flv live stream.
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/delivery-http-flv
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
# the config for srs to remux rtmp to flv live stream.
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/delivery-http-flv
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 19351;
|
||||
max_connections 1000;
|
||||
pid objs/srs.flv.19351.pid;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8081;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
cluster {
|
||||
mode remote;
|
||||
origin 127.0.0.1;
|
||||
}
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
# the config for srs to remux rtmp to flv live stream.
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/delivery-http-flv
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 19352;
|
||||
max_connections 1000;
|
||||
pid objs/srs.flv.19352.pid;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8082;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
cluster {
|
||||
mode remote;
|
||||
origin 127.0.0.1;
|
||||
}
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].flv;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
# the config for srs http heartbeat, report its info to api-server
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
heartbeat {
|
||||
enabled on;
|
||||
interval 9.3;
|
||||
# for python api-server
|
||||
url http://127.0.0.1:8085/api/v1/servers;
|
||||
device_id "my-srs-device";
|
||||
# for ossrs.net monitor, device_id is the key genereated by bsm.
|
||||
#url http://www.ossrs.net:1977/api/v1/robots/servers;
|
||||
#device_id "35c9b402c12a7246868752e2878f7e0e";
|
||||
# with detail summaries
|
||||
summaries on;
|
||||
}
|
||||
stats {
|
||||
network 0;
|
||||
disk sda sdb xvda xvdb;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# the config for srs to delivery hls
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/sample-hls
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
hls {
|
||||
enabled on;
|
||||
hls_fragment 10;
|
||||
hls_window 60;
|
||||
hls_path ./objs/nginx/html;
|
||||
hls_m3u8_file [app]/[stream].m3u8;
|
||||
hls_ts_file [app]/[stream]-[seq].ts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
# http-hooks or http-callbacks config for srs.
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
vhost __defaultVhost__ {
|
||||
http_hooks {
|
||||
enabled on;
|
||||
on_publish http://127.0.0.1:8085/api/v1/streams http://localhost:8085/api/v1/streams;
|
||||
on_unpublish http://127.0.0.1:8085/api/v1/streams http://localhost:8085/api/v1/streams;
|
||||
on_play http://127.0.0.1:8085/api/v1/sessions http://localhost:8085/api/v1/sessions;
|
||||
on_stop http://127.0.0.1:8085/api/v1/sessions http://localhost:8085/api/v1/sessions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
# the config for srs to remux rtmp to mp3 live stream.
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/delivery-http-flv
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
http_remux {
|
||||
enabled on;
|
||||
fast_cache 30;
|
||||
mount [vhost]/[app]/[stream].mp3;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
# the config for srs to serve as http server
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
vhost ossrs.net {
|
||||
http_static {
|
||||
enabled on;
|
||||
mount [vhost]/;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
# the config for srs to remux rtmp to ts live stream.
|
||||
# @see https://ossrs.net/lts/zh-cn/docs/v4/doc/delivery-http-flv
|
||||
# @see full.conf for detail config.
|
||||
|
||||
listen 1935;
|
||||
max_connections 1000;
|
||||
daemon off;
|
||||
srs_log_tank console;
|
||||
http_server {
|
||||
enabled on;
|
||||
listen 8080;
|
||||
dir ./objs/nginx/html;
|
||||
}
|
||||
vhost __defaultVhost__ {
|
||||
http_remux {
|
||||
enabled on;
|
||||
mount [vhost]/[app]/[stream].ts;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user