Compare commits
10 Commits
ad3e9704c0
...
7b37ea2dfa
Author | SHA1 | Date |
---|---|---|
|
7b37ea2dfa | |
|
1bd68fc0ba | |
|
9b8393f27c | |
|
43151cd1a8 | |
|
9b3cf7c5eb | |
|
5177abd11f | |
|
2396d6fb62 | |
|
7572f594e7 | |
|
10cc0f3604 | |
|
be84376c04 |
|
@ -0,0 +1,10 @@
|
||||||
|
module git.icedream.tech/icedream/auto-restart-voicemeeter
|
||||||
|
|
||||||
|
go 1.20
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/JamesDunne/go-asio v0.0.0-20150322061733-6c4b099ca927
|
||||||
|
github.com/davecgh/go-spew v1.1.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require github.com/cenkalti/backoff/v4 v4.2.0 // indirect
|
|
@ -0,0 +1,6 @@
|
||||||
|
github.com/JamesDunne/go-asio v0.0.0-20150322061733-6c4b099ca927 h1:qcTT3RNLm21eamlM+G/AJrh1KG9VcyW8AwOsbzVRW04=
|
||||||
|
github.com/JamesDunne/go-asio v0.0.0-20150322061733-6c4b099ca927/go.mod h1:dRYieepeZSO8hlYXKnpnn6WqR7Myv+kb+Y+w14NxfFw=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
@ -0,0 +1,243 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
asio "github.com/JamesDunne/go-asio"
|
||||||
|
"github.com/cenkalti/backoff/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
func reverse(s string) string {
|
||||||
|
r := []rune(s)
|
||||||
|
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
|
||||||
|
r[i], r[j] = r[j], r[i]
|
||||||
|
}
|
||||||
|
return string(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// TODO - handle interrupt signal here rather than in sample loop
|
||||||
|
|
||||||
|
bo := backoff.NewConstantBackOff(time.Second)
|
||||||
|
err := backoff.Retry(run, bo)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func run() error {
|
||||||
|
fmt.Printf("CoInitialize(0)\n")
|
||||||
|
asio.CoInitialize(0)
|
||||||
|
defer fmt.Printf("CoUninitialize()\n")
|
||||||
|
defer asio.CoUninitialize()
|
||||||
|
|
||||||
|
drivers, err := asio.ListDrivers()
|
||||||
|
if err != nil {
|
||||||
|
return backoff.Permanent(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var mainOutDriver *asio.ASIODriver
|
||||||
|
for _, driver := range drivers {
|
||||||
|
if driver.Name != "Voicemeeter Virtual ASIO" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mainOutDriver = driver
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if mainOutDriver == nil {
|
||||||
|
return backoff.Permanent(errors.New("could not find main Voicemeeter ASIO output"))
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("ASIO driver:", mainOutDriver.GUID, mainOutDriver.CLSID, mainOutDriver.Name)
|
||||||
|
|
||||||
|
if err := mainOutDriver.Open(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer mainOutDriver.Close()
|
||||||
|
|
||||||
|
drv := mainOutDriver.ASIO
|
||||||
|
fmt.Printf("getDriverName(): '%s'\n", drv.GetDriverName())
|
||||||
|
fmt.Printf("getDriverVersion(): %d\n", drv.GetDriverVersion())
|
||||||
|
|
||||||
|
// mainOutUnknown := drv.AsIUnknown()
|
||||||
|
// mainOutUnknown.AddRef()
|
||||||
|
// defer mainOutUnknown.Release()
|
||||||
|
|
||||||
|
// getChannels
|
||||||
|
in, out, err := drv.GetChannels()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("getChannels(): %d, %d\n", in, out)
|
||||||
|
|
||||||
|
// getBufferSize
|
||||||
|
minSize, maxSize, preferredSize, granularity, err := drv.GetBufferSize()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("getBufferSize(): %d, %d, %d, %d\n", minSize, maxSize, preferredSize, granularity)
|
||||||
|
|
||||||
|
// getSampleRate
|
||||||
|
srate, err := drv.GetSampleRate()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("getSampleRate(): %v\n", srate)
|
||||||
|
|
||||||
|
// canSampleRate
|
||||||
|
var sampleRate float64
|
||||||
|
// for _, canSampleRate := range []int{
|
||||||
|
// 48000,
|
||||||
|
// 44100,
|
||||||
|
// } {
|
||||||
|
// sampleRateF64 := float64(canSampleRate)
|
||||||
|
// err = drv.CanSampleRate(sampleRateF64)
|
||||||
|
// fmt.Printf("canSampleRate(%q): %v\n", sampleRateF64, err)
|
||||||
|
// if err != nil {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// sampleRate = canSampleRate
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// if sampleRate == 0 {
|
||||||
|
// // log.Fatal("Could not negotiate a compatible samplerate")
|
||||||
|
// // return
|
||||||
|
// fmt.Println("WARNING: Defaulting to 48000 Hz")
|
||||||
|
// sampleRate = 48000
|
||||||
|
// }
|
||||||
|
sampleRate = srate
|
||||||
|
// sampleRate = 48000
|
||||||
|
|
||||||
|
// SetSampleRate
|
||||||
|
err = drv.SetSampleRate(sampleRate)
|
||||||
|
fmt.Printf("setSampleRate(%v): %v\n", float64(sampleRate), err)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("WARNING: setSampleRate failed, ignoring:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// outputReady
|
||||||
|
fmt.Printf("outputReady(): %v\n", drv.OutputReady())
|
||||||
|
|
||||||
|
// open control panel:
|
||||||
|
// drv.ControlPanel()
|
||||||
|
|
||||||
|
bufferDescriptors := make([]asio.BufferInfo, 0, in+out)
|
||||||
|
for i := 0; i < in; i++ {
|
||||||
|
bufferDescriptors = append(bufferDescriptors, asio.BufferInfo{
|
||||||
|
Channel: i,
|
||||||
|
IsInput: true,
|
||||||
|
})
|
||||||
|
cinfo, err := drv.GetChannelInfo(i, true)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Printf(" IN%-2d: active=%v, group=%d, type=%d, name=%s\n", i+1, cinfo.IsActive, cinfo.ChannelGroup, cinfo.SampleType, cinfo.Name)
|
||||||
|
}
|
||||||
|
for i := 0; i < out; i++ {
|
||||||
|
bufferDescriptors = append(bufferDescriptors, asio.BufferInfo{
|
||||||
|
Channel: i,
|
||||||
|
IsInput: false,
|
||||||
|
})
|
||||||
|
cinfo, err := drv.GetChannelInfo(i, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Printf("OUT%-2d: active=%v, group=%d, type=%d, name=%s\n", i+1, cinfo.IsActive, cinfo.ChannelGroup, cinfo.SampleType, cinfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = drv.CreateBuffers(bufferDescriptors, 512, asio.Callbacks{
|
||||||
|
Message: func(selector, value int32, message uintptr, opt *float64) int32 {
|
||||||
|
log.Println("Message:", selector, value, message, opt)
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
BufferSwitch: func(doubleBufferIndex int, directProcess bool) {
|
||||||
|
log.Println("Buffer switch:", doubleBufferIndex, directProcess)
|
||||||
|
},
|
||||||
|
BufferSwitchTimeInfo: func(params *asio.ASIOTime, doubleBufferIndex int32, directProcess bool) *asio.ASIOTime {
|
||||||
|
log.Println("Buffer switch time info:", params, doubleBufferIndex, directProcess)
|
||||||
|
return params
|
||||||
|
},
|
||||||
|
SampleRateDidChange: func(rate float64) {
|
||||||
|
log.Println("Sample rate did change:", rate)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fmt.Printf("disposeBuffers()\n")
|
||||||
|
defer drv.DisposeBuffers()
|
||||||
|
fmt.Printf("createBuffers()\n")
|
||||||
|
|
||||||
|
// getLatencies
|
||||||
|
latin, latout, err := drv.GetLatencies()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("getLatencies(): %d, %d\n", latin, latout)
|
||||||
|
|
||||||
|
err = drv.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer drv.Stop()
|
||||||
|
|
||||||
|
c := make(chan os.Signal, 1)
|
||||||
|
go signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
||||||
|
output := new(strings.Builder)
|
||||||
|
ladder := " ▁▂▃▄▅▆▇█"
|
||||||
|
chars := []rune(reverse(ladder) + ladder)
|
||||||
|
grace := 5 * time.Second
|
||||||
|
lastSignalTime := time.Now()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-time.After(33 * time.Millisecond):
|
||||||
|
output.Reset()
|
||||||
|
for _, desc := range bufferDescriptors {
|
||||||
|
for _, buf := range desc.Buffers {
|
||||||
|
output.WriteString("|")
|
||||||
|
if buf == nil {
|
||||||
|
output.WriteString("?")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if *buf != 0 {
|
||||||
|
lastSignalTime = time.Now()
|
||||||
|
}
|
||||||
|
output.WriteString(fmt.Sprintf("%c",
|
||||||
|
chars[len(chars)/2+
|
||||||
|
int(float64(len(chars))/2*
|
||||||
|
float64(*buf)/float64(math.MaxInt32))]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.Stdout.WriteString(output.String() + "\r")
|
||||||
|
if time.Now().Sub(lastSignalTime) > time.Second {
|
||||||
|
os.Stdout.WriteString("Silence!\r")
|
||||||
|
}
|
||||||
|
if time.Now().Sub(lastSignalTime) > grace {
|
||||||
|
os.Stdout.WriteString("\n")
|
||||||
|
log.Println("Restarting audio engine...")
|
||||||
|
cmd := exec.Command(`C:\Program Files (x86)\VB\Voicemeeter\voicemeeterpro.exe`, "-r")
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
log.Printf("Restart audio engine result: %v", cmd.Run())
|
||||||
|
// time.Sleep(3 * time.Second)
|
||||||
|
// lastSignalTime = time.Now()
|
||||||
|
return errors.New("audio engine restarted, retry")
|
||||||
|
}
|
||||||
|
case <-c:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,7 +26,7 @@ then
|
||||||
rm winfsp.zip
|
rm winfsp.zip
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for bin in foobar2000 tunadish tunaposter prime4
|
for bin in auto-restart-voicemeeter foobar2000 tunadish tunaposter prime4
|
||||||
do
|
do
|
||||||
cd "$SCRIPT_DIR/$bin"
|
cd "$SCRIPT_DIR/$bin"
|
||||||
go build -ldflags "-s -w" -o "$GOBIN/$bin$ext" -v
|
go build -ldflags "-s -w" -o "$GOBIN/$bin$ext" -v
|
||||||
|
|
|
@ -11,8 +11,8 @@ require (
|
||||||
github.com/billziss-gh/cgofuse v1.5.0
|
github.com/billziss-gh/cgofuse v1.5.0
|
||||||
github.com/dhowden/tag v0.0.0-20220618230019-adf36e896086
|
github.com/dhowden/tag v0.0.0-20220618230019-adf36e896086
|
||||||
github.com/gin-gonic/gin v1.9.0
|
github.com/gin-gonic/gin v1.9.0
|
||||||
github.com/icedream/livestream-tools/icedreammusic/metacollector v0.0.0-20230302092605-950fba55448e
|
github.com/icedream/livestream-tools/icedreammusic/metacollector v0.0.0-20230302143311-ad3e9704c029
|
||||||
github.com/icedream/livestream-tools/icedreammusic/tuna v0.0.0-20230302092605-950fba55448e
|
github.com/icedream/livestream-tools/icedreammusic/tuna v0.0.0-20230302143311-ad3e9704c029
|
||||||
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20191105091915-95d230a53780
|
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20191105091915-95d230a53780
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# ARG IMAGE=savonet/liquidsoap:master
|
# ARG IMAGE=savonet/liquidsoap:master
|
||||||
ARG IMAGE=savonet/liquidsoap-ci-build:v2.1.4_alpine_amd64
|
ARG IMAGE=savonet/liquidsoap-ci-build:rolling-release-v2.2.x@sha256:d38c0580b4b1490bfd679fdacadd20352558c8ac214beb2a8aed3d7017275e15
|
||||||
|
|
||||||
# FROM $IMAGE
|
# FROM $IMAGE
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# Our Denon StagelinQ receiver will send the metadata to this interface.
|
# Our Denon StagelinQ receiver will send the metadata to this interface.
|
||||||
# For fun, I tested this code with the port set to 1608 to emulate the OBS Tuna plugin's HTTP interface, and that works well!
|
# For fun, I tested this code with the port set to 1608 to emulate the OBS Tuna plugin's HTTP interface, and that works well!
|
||||||
|
|
||||||
metadata_api_hostname = getenv(default="icedream-bitwave", "METADATA_API_HOSTNAME")
|
metadata_api_hostname = environment.get(default="icedream-bitwave", "METADATA_API_HOSTNAME")
|
||||||
|
|
||||||
def http_export_meta(%argsof(json.stringify), m) =
|
def http_export_meta(%argsof(json.stringify), m) =
|
||||||
j = json()
|
j = json()
|
||||||
|
@ -16,7 +16,10 @@ def setup_harbor_metadata_api(~metadata_api_port=21338, s) =
|
||||||
s = insert_metadata(s)
|
s = insert_metadata(s)
|
||||||
|
|
||||||
# Handler for fetching metadata
|
# Handler for fetching metadata
|
||||||
def on_http_get_metadata(~protocol, ~data, ~headers, uri) =
|
def on_http_get_metadata(request) =
|
||||||
|
http_version = request.http_version
|
||||||
|
headers = request.headers
|
||||||
|
|
||||||
m = s.last_metadata() ?? []
|
m = s.last_metadata() ?? []
|
||||||
|
|
||||||
# remove cover info and link to it instead if existing
|
# remove cover info and link to it instead if existing
|
||||||
|
@ -35,7 +38,7 @@ def setup_harbor_metadata_api(~metadata_api_port=21338, s) =
|
||||||
m = metadata.cover.remove(m)
|
m = metadata.cover.remove(m)
|
||||||
data = http_export_meta(compact=true, m)
|
data = http_export_meta(compact=true, m)
|
||||||
|
|
||||||
http.response(protocol=protocol, code=200, headers=[
|
http.response(http_version=http_version, status_code=200, headers=[
|
||||||
("access-control-allow-origin","*"),
|
("access-control-allow-origin","*"),
|
||||||
("access-control-allow-credentials","true"),
|
("access-control-allow-credentials","true"),
|
||||||
("access-control-allow-methods","GET,POST"),
|
("access-control-allow-methods","GET,POST"),
|
||||||
|
@ -47,11 +50,14 @@ def setup_harbor_metadata_api(~metadata_api_port=21338, s) =
|
||||||
end
|
end
|
||||||
|
|
||||||
# Handler for fetching current cover art
|
# Handler for fetching current cover art
|
||||||
def on_http_get_cover(~protocol, ~data, ~headers, uri) =
|
def on_http_get_cover(request) =
|
||||||
|
http_version = request.http_version
|
||||||
|
headers = request.headers
|
||||||
|
|
||||||
m = s.last_metadata() ?? []
|
m = s.last_metadata() ?? []
|
||||||
cover = metadata.cover(m) ?? "".{mime="text/plain"}
|
cover = metadata.cover(m) ?? "".{mime="text/plain"}
|
||||||
if string.length(cover) > 0 then
|
if string.length(cover) > 0 then
|
||||||
http.response(protocol=protocol, code=200, headers=[
|
http.response(http_version=http_version, status_code=200, headers=[
|
||||||
("access-control-allow-origin","*"),
|
("access-control-allow-origin","*"),
|
||||||
("access-control-allow-credentials","true"),
|
("access-control-allow-credentials","true"),
|
||||||
("access-control-allow-methods","GET,POST"),
|
("access-control-allow-methods","GET,POST"),
|
||||||
|
@ -61,7 +67,7 @@ def setup_harbor_metadata_api(~metadata_api_port=21338, s) =
|
||||||
("content-type", cover.mime),
|
("content-type", cover.mime),
|
||||||
], data=string_of(cover))
|
], data=string_of(cover))
|
||||||
else
|
else
|
||||||
http.response(protocol=protocol, code=404, headers=[
|
http.response(http_version=http_version, status_code=404, headers=[
|
||||||
("access-control-allow-origin","*"),
|
("access-control-allow-origin","*"),
|
||||||
("access-control-allow-credentials","true"),
|
("access-control-allow-credentials","true"),
|
||||||
("access-control-allow-methods","GET,POST"),
|
("access-control-allow-methods","GET,POST"),
|
||||||
|
@ -73,7 +79,11 @@ def setup_harbor_metadata_api(~metadata_api_port=21338, s) =
|
||||||
end
|
end
|
||||||
|
|
||||||
# Handler for receiving metadata
|
# Handler for receiving metadata
|
||||||
def on_http_metadata(~protocol, ~data, ~headers, uri) =
|
def on_http_metadata(request) =
|
||||||
|
http_version = request.http_version
|
||||||
|
data = request.data()
|
||||||
|
headers = request.headers
|
||||||
|
|
||||||
let json.parse (data : {
|
let json.parse (data : {
|
||||||
data: [(string * string)] as json.object
|
data: [(string * string)] as json.object
|
||||||
}) = data
|
}) = data
|
||||||
|
@ -108,7 +118,7 @@ def setup_harbor_metadata_api(~metadata_api_port=21338, s) =
|
||||||
# set metadata on stream
|
# set metadata on stream
|
||||||
s.insert_metadata(new_track=new_track, m)
|
s.insert_metadata(new_track=new_track, m)
|
||||||
|
|
||||||
http.response(protocol=protocol, code=200, headers=[
|
http.response(http_version=http_version, status_code=200, headers=[
|
||||||
("allow","POST"),
|
("allow","POST"),
|
||||||
("access-control-allow-origin","*"),
|
("access-control-allow-origin","*"),
|
||||||
("access-control-allow-credentials","true"),
|
("access-control-allow-credentials","true"),
|
||||||
|
@ -119,8 +129,11 @@ def setup_harbor_metadata_api(~metadata_api_port=21338, s) =
|
||||||
end
|
end
|
||||||
|
|
||||||
# Just in case we use a browser to send data to this (for example while emulating Tuna)
|
# Just in case we use a browser to send data to this (for example while emulating Tuna)
|
||||||
def on_http_metadata_cors(~protocol, ~data, ~headers, uri) =
|
def on_http_metadata_cors(request) =
|
||||||
http.response(protocol=protocol, code=200, headers=[
|
http_version = request.http_version
|
||||||
|
headers = request.headers
|
||||||
|
|
||||||
|
http.response(http_version=http_version, status_code=200, headers=[
|
||||||
("allow","POST"),
|
("allow","POST"),
|
||||||
("access-control-allow-origin","*"),
|
("access-control-allow-origin","*"),
|
||||||
("access-control-allow-credentials","true"),
|
("access-control-allow-credentials","true"),
|
||||||
|
@ -130,17 +143,17 @@ def setup_harbor_metadata_api(~metadata_api_port=21338, s) =
|
||||||
], data="POST")
|
], data="POST")
|
||||||
end
|
end
|
||||||
|
|
||||||
harbor.http.register(port=metadata_api_port, method="GET", "/#{id}/meta", on_http_get_metadata)
|
harbor.http.register.simple(port=metadata_api_port, method="GET", "/#{id}/meta", on_http_get_metadata)
|
||||||
harbor.http.register(port=metadata_api_port, method="OPTIONS", "/#{id}/meta", on_http_metadata_cors)
|
harbor.http.register.simple(port=metadata_api_port, method="OPTIONS", "/#{id}/meta", on_http_metadata_cors)
|
||||||
|
|
||||||
harbor.http.register(port=metadata_api_port, method="GET", "/#{id}/cover", on_http_get_cover)
|
harbor.http.register.simple(port=metadata_api_port, method="GET", "/#{id}/cover", on_http_get_cover)
|
||||||
harbor.http.register(port=metadata_api_port, method="OPTIONS", "/#{id}/cover", on_http_metadata_cors)
|
harbor.http.register.simple(port=metadata_api_port, method="OPTIONS", "/#{id}/cover", on_http_metadata_cors)
|
||||||
|
|
||||||
harbor.http.register(port=metadata_api_port, method="POST", "/#{id}", on_http_metadata)
|
harbor.http.register.simple(port=metadata_api_port, method="POST", "/#{id}", on_http_metadata)
|
||||||
harbor.http.register(port=metadata_api_port, method="OPTIONS", "/#{id}", on_http_metadata_cors)
|
harbor.http.register.simple(port=metadata_api_port, method="OPTIONS", "/#{id}", on_http_metadata_cors)
|
||||||
|
|
||||||
harbor.http.register(port=metadata_api_port, method="POST", "/", on_http_metadata)
|
harbor.http.register.simple(port=metadata_api_port, method="POST", "/", on_http_metadata)
|
||||||
harbor.http.register(port=metadata_api_port, method="OPTIONS", "/", on_http_metadata_cors)
|
harbor.http.register.simple(port=metadata_api_port, method="OPTIONS", "/", on_http_metadata_cors)
|
||||||
|
|
||||||
s
|
s
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
stream_name=getenv(default="Spontaneously live in the mix", "STREAM_NAME")
|
stream_name=environment.get(default="Spontaneously live in the mix", "STREAM_NAME")
|
||||||
stream_description=getenv(default="", "STREAM_DESCRIPTION")
|
stream_description=environment.get(default="", "STREAM_DESCRIPTION")
|
||||||
|
|
||||||
internal_icecast_username=getenv(default="source", "INTERNAL_ICECAST_USERNAME")
|
internal_icecast_username=environment.get(default="source", "INTERNAL_ICECAST_USERNAME")
|
||||||
internal_icecast_password=getenv(default="source", "INTERNAL_ICECAST_PASSWORD")
|
internal_icecast_password=environment.get(default="source", "INTERNAL_ICECAST_PASSWORD")
|
||||||
|
|
||||||
rektfm_username=getenv("REKTNETWORK_USERNAME")
|
rektfm_username=environment.get("REKTNETWORK_USERNAME")
|
||||||
rektfm_password=getenv("REKTNETWORK_PASSWORD")
|
rektfm_password=environment.get("REKTNETWORK_PASSWORD")
|
||||||
|
|
|
@ -22,18 +22,32 @@ set("sandbox", "disabled")
|
||||||
s = input.http(id="input_ice_main", max_buffer=4., "http://127.0.0.1:61120/main")
|
s = input.http(id="input_ice_main", max_buffer=4., "http://127.0.0.1:61120/main")
|
||||||
|
|
||||||
# Split audio off to be handled specially
|
# Split audio off to be handled specially
|
||||||
a = drop_video(s)
|
# NOTE - drop_video causes a weird error during script validation, we assume audio-only here
|
||||||
|
# a = drop_video(s)
|
||||||
|
a = s
|
||||||
a = mksafe_soft(a)
|
a = mksafe_soft(a)
|
||||||
output.dummy(a)
|
output.dummy(a)
|
||||||
|
|
||||||
def append_encoder_meta(m) =
|
def append_encoder_meta(_) =
|
||||||
[
|
new_meta = [
|
||||||
("encoder", "Liquidsoap #{liquidsoap.version}"),
|
("encoder", "Liquidsoap #{liquidsoap.version}"),
|
||||||
("stream_name", stream_name),
|
|
||||||
("stream_description", stream_description),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
new_meta = if null.defined(stream_name) then
|
||||||
|
[...new_meta, ("stream_name", null.get(stream_name))]
|
||||||
|
else
|
||||||
|
new_meta
|
||||||
|
end
|
||||||
|
|
||||||
|
new_meta = if null.defined(stream_description) then
|
||||||
|
[...new_meta, ("stream_description", null.get(stream_description))]
|
||||||
|
else
|
||||||
|
new_meta
|
||||||
|
end
|
||||||
|
|
||||||
|
new_meta
|
||||||
end
|
end
|
||||||
a = map_metadata(id="main", append_encoder_meta, a)
|
a = metadata.map(id="main", append_encoder_meta, a)
|
||||||
|
|
||||||
a = setup_harbor_metadata_api(a)
|
a = setup_harbor_metadata_api(a)
|
||||||
|
|
||||||
|
@ -55,10 +69,10 @@ def internal_icecast(
|
||||||
headers=[],
|
headers=[],
|
||||||
port=61120,
|
port=61120,
|
||||||
host="127.0.0.1",
|
host="127.0.0.1",
|
||||||
user=internal_icecast_username,
|
user=null.get(internal_icecast_username),
|
||||||
password=internal_icecast_password,
|
password=null.get(internal_icecast_password),
|
||||||
name=stream_name,
|
name=null.get(stream_name),
|
||||||
description=stream_description,
|
description=null.get(stream_description),
|
||||||
e, s)
|
e, s)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -123,7 +137,7 @@ setup_harbor_stream_api(internal_icecast(
|
||||||
))
|
))
|
||||||
|
|
||||||
# REKT.fm
|
# REKT.fm
|
||||||
if string.length(rektfm_username) > 0 and string.length(rektfm_password) > 0 then
|
if null.defined(rektfm_username) and null.defined(rektfm_password) then
|
||||||
setup_harbor_stream_api(output.icecast(
|
setup_harbor_stream_api(output.icecast(
|
||||||
id="out_a_rekt",
|
id="out_a_rekt",
|
||||||
# %ogg(%flac),
|
# %ogg(%flac),
|
||||||
|
@ -133,10 +147,10 @@ if string.length(rektfm_username) > 0 and string.length(rektfm_password) > 0 the
|
||||||
port=60000,
|
port=60000,
|
||||||
host="stream.rekt.network",
|
host="stream.rekt.network",
|
||||||
# host="stream.rekt.fm",
|
# host="stream.rekt.fm",
|
||||||
user=rektfm_username,
|
user=null.get(rektfm_username),
|
||||||
name=stream_name,
|
name=null.get(stream_name),
|
||||||
description=stream_description,
|
description=null.get(stream_description),
|
||||||
password=rektfm_password,
|
password=null.get(rektfm_password),
|
||||||
start=false,
|
start=false,
|
||||||
%ffmpeg(
|
%ffmpeg(
|
||||||
format="ogg",
|
format="ogg",
|
||||||
|
|
|
@ -3,35 +3,34 @@ stream_api_port=21336
|
||||||
interactive.harbor(port=stream_api_port, uri="/interactive") # expose through stream API port
|
interactive.harbor(port=stream_api_port, uri="/interactive") # expose through stream API port
|
||||||
|
|
||||||
# list of stream IDs that have been set up by setup_harbor_stream_api
|
# list of stream IDs that have been set up by setup_harbor_stream_api
|
||||||
stream_api_streams=[]
|
stream_api_streams=ref([])
|
||||||
|
|
||||||
def setup_harbor_stream_api_general()
|
def setup_harbor_stream_api_general()
|
||||||
def on_list(~protocol, ~data, ~headers, uri) =
|
def on_list(_) =
|
||||||
data = stream_api_streams
|
http.response(status_code=200, headers=[
|
||||||
http.response(protocol=protocol, code=200, headers=[
|
|
||||||
("content-type","application/json"),
|
("content-type","application/json"),
|
||||||
], data=json.stringify(data))
|
], data=json.stringify(stream_api_streams()))
|
||||||
end
|
end
|
||||||
|
|
||||||
harbor.http.register(port=stream_api_port, method="GET", "/streams/", on_list)
|
harbor.http.register.simple(port=stream_api_port, method="GET", "/streams/", on_list)
|
||||||
end
|
end
|
||||||
|
|
||||||
def setup_harbor_stream_api(s) =
|
def setup_harbor_stream_api(s) =
|
||||||
def on_start(~protocol, ~data, ~headers, uri) =
|
def on_start(_) =
|
||||||
s.start()
|
s.start()
|
||||||
http.response(protocol=protocol, code=200, headers=[
|
http.response(status_code=200, headers=[
|
||||||
("content-type","application/json"),
|
("content-type","application/json"),
|
||||||
], data=json.stringify([]))
|
], data=json.stringify([]))
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_stop(~protocol, ~data, ~headers, uri) =
|
def on_stop(_) =
|
||||||
s.stop()
|
s.stop()
|
||||||
http.response(protocol=protocol, code=200, headers=[
|
http.response(status_code=200, headers=[
|
||||||
("content-type","application/json"),
|
("content-type","application/json"),
|
||||||
], data=json.stringify([]))
|
], data=json.stringify([]))
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_info(~protocol, ~data, ~headers, uri) =
|
def on_info(_) =
|
||||||
data = [
|
data = [
|
||||||
("id", s.id()),
|
("id", s.id()),
|
||||||
("last_metadata", json.stringify(s.last_metadata())),
|
("last_metadata", json.stringify(s.last_metadata())),
|
||||||
|
@ -40,16 +39,16 @@ def setup_harbor_stream_api(s) =
|
||||||
("is_ready", json.stringify(s.is_ready())),
|
("is_ready", json.stringify(s.is_ready())),
|
||||||
("is_active", json.stringify(s.is_active())),
|
("is_active", json.stringify(s.is_active())),
|
||||||
]
|
]
|
||||||
http.response(protocol=protocol, code=200, headers=[
|
http.response(status_code=200, headers=[
|
||||||
("content-type","application/json"),
|
("content-type","application/json"),
|
||||||
], data=json.stringify(data))
|
], data=json.stringify(data))
|
||||||
end
|
end
|
||||||
|
|
||||||
s.id()::stream_api_streams
|
stream_api_streams := [...stream_api_streams(), s.id()]
|
||||||
|
|
||||||
harbor.http.register(port=stream_api_port, method="POST", "/streams/#{s.id()}/start", on_start)
|
harbor.http.register.simple(port=stream_api_port, method="POST", "/streams/#{s.id()}/start", on_start)
|
||||||
harbor.http.register(port=stream_api_port, method="POST", "/streams/#{s.id()}/stop", on_stop)
|
harbor.http.register.simple(port=stream_api_port, method="POST", "/streams/#{s.id()}/stop", on_stop)
|
||||||
harbor.http.register(port=stream_api_port, method="GET", "/streams/#{s.id()}", on_info)
|
harbor.http.register.simple(port=stream_api_port, method="GET", "/streams/#{s.id()}", on_info)
|
||||||
|
|
||||||
s
|
s
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,102 @@
|
||||||
FROM busybox
|
# syntax=docker/dockerfile:1.2.1
|
||||||
|
|
||||||
WORKDIR /target/usr/local/bin/
|
FROM alpine AS rootfs
|
||||||
COPY *.sh .
|
|
||||||
RUN dos2unix *.sh
|
RUN apk add --no-cache gnupg
|
||||||
RUN chmod -v +x *.sh
|
|
||||||
|
RUN wget -O- https://raw.githubusercontent.com/archlinuxarm/archlinuxarm-keyring/master/archlinuxarm.gpg | gpg --import
|
||||||
|
|
||||||
|
WORKDIR /target/
|
||||||
|
ARG ALARM_ROOTFS_URL=http://os.archlinuxarm.org/os/ArchLinuxARM-rpi-2-latest.tar.gz
|
||||||
|
RUN wget "${ALARM_ROOTFS_URL}" -O/tmp/rootfs.tar.gz
|
||||||
|
RUN wget "${ALARM_ROOTFS_URL}.sig" -O/tmp/rootfs.tar.gz.sig
|
||||||
|
RUN gpg --verify /tmp/rootfs.tar.gz.sig
|
||||||
|
RUN tar -xvpzf /tmp/rootfs.tar.gz
|
||||||
|
|
||||||
###
|
###
|
||||||
|
# PREPARE LAYER FOR UPDATES AND GENERAL PACKAGE INSTALLATION
|
||||||
|
|
||||||
|
# FROM scratch AS image-base
|
||||||
|
|
||||||
|
# COPY --from=rootfs /target/ /
|
||||||
|
|
||||||
|
FROM archlinux AS image-base
|
||||||
|
|
||||||
|
# Make powerpill not act up later, placing this early for validation consistency
|
||||||
|
RUN sed -i 's,SigLevel\s\+=\s\+Required,SigLevel = PackageRequired,' /etc/pacman.conf
|
||||||
|
|
||||||
|
RUN pacman -Sy --noconfirm
|
||||||
|
RUN pacman-key --init
|
||||||
|
|
||||||
|
# Install core keyring (https://archlinuxarm.org/about/package-signing)
|
||||||
|
# RUN pacman -S --needed --noconfirm archlinuxarm-keyring
|
||||||
|
# RUN pacman-key --populate archlinuxarm
|
||||||
|
RUN pacman-key --populate archlinux
|
||||||
|
RUN pacman -S --needed --noconfirm archlinux-keyring
|
||||||
|
RUN pacman-key --populate archlinux
|
||||||
|
|
||||||
|
# ###
|
||||||
|
# # INSTALL FILESYSTEM PACKAGE UPDATES
|
||||||
|
# # We have to do this with an alternative root since /etc/{hosts,resolv.conf}
|
||||||
|
# # are mounted read-only by Docker.
|
||||||
|
|
||||||
|
# FROM image-base AS updated-filesystem-base
|
||||||
|
|
||||||
|
# COPY --from=image-base / /target/
|
||||||
|
|
||||||
|
FROM image-base AS updated-filesystem-base
|
||||||
|
|
||||||
|
# RUN \
|
||||||
|
# --mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
# --mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
# pacman -r /target/ -S --noconfirm --needed filesystem
|
||||||
|
|
||||||
|
# ###
|
||||||
|
# # LAYER USED FOR INSTALLING UPDATES AND ADDITIONAL PACKAGES USED IN FINAL IMAGE
|
||||||
|
|
||||||
|
# FROM scratch AS base
|
||||||
|
|
||||||
|
# COPY --from=updated-filesystem-base /target/ /
|
||||||
|
|
||||||
|
FROM updated-filesystem-base AS base
|
||||||
|
|
||||||
|
# # Install updates
|
||||||
|
# # NOTE - we install fsck helpers for fat and ext4 in this stage to save on time spent on /boot updates
|
||||||
|
# RUN \
|
||||||
|
# --mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
# --mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
# pacman -Suu --noconfirm --needed dosfstools e2fsprogs
|
||||||
|
|
||||||
|
###
|
||||||
|
# LAYER USED TO COMPILE STUFF
|
||||||
|
|
||||||
|
FROM image-base AS base-devel
|
||||||
|
|
||||||
|
RUN pacman -S --noconfirm base-devel git
|
||||||
|
|
||||||
|
#RUN pacman -S --noconfirm --needed sudo
|
||||||
|
RUN (echo "" && echo "%wheel ALL=(ALL) NOPASSWD: ALL") >> /etc/sudoers
|
||||||
|
|
||||||
|
RUN useradd -r -N -m -G wheel -d /tmp/build -k /var/empty build
|
||||||
|
|
||||||
|
RUN sed -i \
|
||||||
|
-e 's,#MAKEFLAGS=.*,MAKEFLAGS="-j$(getconf _NPROCESSORS_ONLN)",g' \
|
||||||
|
/etc/makepkg.conf
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
chown -Rv build /tmp/build /tmp/build/.cache
|
||||||
|
|
||||||
|
RUN echo "ParallelDownloads = 5" >>/etc/pacman.conf
|
||||||
|
|
||||||
|
USER build
|
||||||
|
|
||||||
|
# Needed for anything commits
|
||||||
|
RUN git config --global user.email "$(whoami)@localhost"
|
||||||
|
RUN git config --global user.name "Build"
|
||||||
|
|
||||||
|
###
|
||||||
|
# FAKESILENCE
|
||||||
|
|
||||||
FROM golang:1 AS fakesilence
|
FROM golang:1 AS fakesilence
|
||||||
|
|
||||||
|
@ -15,27 +106,209 @@ RUN go install -v -ldflags "-s -w" github.com/icedream/fakesilence@"${FAKESILENC
|
||||||
RUN cp -v "$GOPATH"/bin/* /usr/local/bin
|
RUN cp -v "$GOPATH"/bin/* /usr/local/bin
|
||||||
|
|
||||||
###
|
###
|
||||||
|
# YAY
|
||||||
|
|
||||||
# yay build
|
FROM base-devel AS yay
|
||||||
|
|
||||||
FROM archlinux
|
WORKDIR /usr/src/yay
|
||||||
|
RUN git clone --recursive https://aur.archlinux.org/yay.git .
|
||||||
|
RUN \
|
||||||
|
--mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
makepkg -sr --noconfirm --nocheck
|
||||||
|
|
||||||
WORKDIR /usr/src/ndi-feeder/
|
###
|
||||||
RUN pacman --noconfirm -Sy git sudo make binutils fakeroot base-devel
|
# BASE DEVEL (YAY)
|
||||||
RUN echo "" && echo "%wheel ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
|
||||||
RUN useradd -UMr -d /usr/src/ndi-feeder/ -G wheel app
|
|
||||||
RUN chown -R app .
|
|
||||||
|
|
||||||
USER app
|
FROM base-devel AS base-devel-yay
|
||||||
RUN git clone --recursive https://aur.archlinux.org/yay.git yay/
|
|
||||||
RUN cd yay && makepkg --noconfirm -si && cd .. && rm -r yay
|
USER root
|
||||||
RUN yay --noconfirm -S pod2man && sudo rm -r ~/.cache /var/cache/pacman/*
|
COPY --from=yay /usr/src/yay/*.pkg.* /tmp/
|
||||||
RUN yay --noconfirm -S ndi-advanced-sdk && sudo rm -r ~/.cache /var/cache/pacman/*
|
RUN \
|
||||||
RUN yay --noconfirm -S ffmpeg-ndi && sudo rm -r ~/.cache /var/cache/pacman/*
|
--mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
pacman --noconfirm -U /tmp/*.pkg.* && rm /tmp/*.pkg.*
|
||||||
|
|
||||||
|
USER build
|
||||||
|
|
||||||
|
###
|
||||||
|
# POD2MAN
|
||||||
|
|
||||||
|
FROM base-devel-yay AS pod2man
|
||||||
|
|
||||||
|
WORKDIR /usr/src/pod2man
|
||||||
|
|
||||||
|
RUN git clone --recursive https://aur.archlinux.org/pod2man.git .
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
--mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
(. ./PKGBUILD && yay -S --noconfirm --asdeps --provides --needed $(yay -T "${depends[@]}") && (mv -v ~/.cache/yay/*/*.pkg.* . || true))
|
||||||
|
RUN makepkg -sr --noconfirm
|
||||||
|
|
||||||
|
###
|
||||||
|
# NDI-SDK-EMBEDDED
|
||||||
|
|
||||||
|
FROM base-devel-yay AS ndi-sdk-embedded
|
||||||
|
|
||||||
|
WORKDIR /usr/src/ndi-sdk-embedded
|
||||||
|
|
||||||
|
RUN git clone --recursive https://aur.archlinux.org/ndi-sdk-embedded.git .
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
--mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
(. ./PKGBUILD && yay -S --noconfirm --asdeps --provides --needed $(yay -T "${depends[@]}") && (mv -v ~/.cache/yay/*/*.pkg.* . || true))
|
||||||
|
RUN makepkg -sr --noconfirm
|
||||||
|
|
||||||
|
###
|
||||||
|
# NDI-SDK
|
||||||
|
|
||||||
|
FROM base-devel-yay AS ndi-sdk
|
||||||
|
|
||||||
|
WORKDIR /usr/src/ndi-sdk
|
||||||
|
RUN git clone --recursive https://aur.archlinux.org/ndi-sdk.git .
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
--mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
(. ./PKGBUILD && yay -S --noconfirm --asdeps --provides --needed $(yay -T "${depends[@]}") && (mv -v ~/.cache/yay/*/*.pkg.* . || true))
|
||||||
|
RUN \
|
||||||
|
--mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
(. ./PKGBUILD && yay -S --noconfirm --asdeps --provides --needed $(yay -T "${makedepends[@]}"))
|
||||||
|
RUN \
|
||||||
|
--mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
makepkg -sr --noconfirm
|
||||||
|
|
||||||
|
###
|
||||||
|
# NDI-ADVANCED-SDK
|
||||||
|
|
||||||
|
FROM base-devel-yay AS ndi-advanced-sdk
|
||||||
|
|
||||||
|
WORKDIR /usr/src/ndi-advanced-sdk
|
||||||
|
RUN git clone --recursive https://aur.archlinux.org/ndi-advanced-sdk.git .
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
--mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
(. ./PKGBUILD && yay -S --noconfirm --asdeps --provides --needed $(yay -T "${depends[@]}") && (mv -v ~/.cache/yay/*/*.pkg.* . || true))
|
||||||
|
RUN \
|
||||||
|
--mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
(. ./PKGBUILD && yay -S --noconfirm --asdeps --provides --needed $(yay -T "${makedepends[@]}"))
|
||||||
|
RUN \
|
||||||
|
--mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
makepkg -sr --noconfirm
|
||||||
|
|
||||||
|
###
|
||||||
|
# FFMPEG-NDI
|
||||||
|
|
||||||
|
FROM base-devel-yay AS ffmpeg-ndi
|
||||||
|
|
||||||
|
WORKDIR /usr/src/ffmpeg-ndi
|
||||||
|
|
||||||
|
USER root
|
||||||
|
# COPY --from=ndi-sdk-embedded /usr/src/ndi-sdk-embedded/*.pkg.* /tmp/
|
||||||
|
COPY --from=ndi-sdk /usr/src/ndi-sdk/*.pkg.* /tmp/
|
||||||
|
COPY --from=pod2man /usr/src/pod2man/*.pkg.* /tmp/
|
||||||
|
RUN \
|
||||||
|
--mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
yay --noconfirm -U /tmp/*.pkg.* && rm /tmp/*.pkg.*
|
||||||
|
|
||||||
|
USER build
|
||||||
|
RUN git clone --recursive https://aur.archlinux.org/ffmpeg-ndi.git .
|
||||||
|
RUN \
|
||||||
|
--mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
(\
|
||||||
|
. ./PKGBUILD &&\
|
||||||
|
if [ "${#depends[@]}" -eq 0 ]; then exit; fi &&\
|
||||||
|
packages=$(yay -T "${depends[@]}" 2>/dev/null|| true) &&\
|
||||||
|
if [ -z "$packages" ]; then exit; fi &&\
|
||||||
|
yay -S --noconfirm --asdeps --provides --needed $packages &&\
|
||||||
|
find ~/.cache/yay/ -mindepth 2 -maxdepth 2 -name \*.pkg.\* -exec mv {} . \;\
|
||||||
|
)
|
||||||
|
# RUN (. ./PKGBUILD && yay -S --noconfirm --asdeps --provides --needed $(yay -T "${optdepends[@]}") && (mv -v ~/.cache/yay/*/*.pkg.* . || true))
|
||||||
|
RUN \
|
||||||
|
--mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
(\
|
||||||
|
. ./PKGBUILD &&\
|
||||||
|
if [ "${#makedepends[@]}" -eq 0 ]; then exit; fi &&\
|
||||||
|
packages=$(yay -T "${makedepends[@]}" 2>/dev/null|| true) &&\
|
||||||
|
if [ -z "$packages" ]; then exit; fi &&\
|
||||||
|
yay -S --noconfirm --asdeps --provides --needed $packages \
|
||||||
|
)
|
||||||
|
RUN \
|
||||||
|
--mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
makepkg -sr --noconfirm --nocheck
|
||||||
|
|
||||||
|
###
|
||||||
|
# PERMISSIONS FOR FINAL IMAGE FILES
|
||||||
|
|
||||||
|
FROM busybox AS files
|
||||||
|
|
||||||
|
WORKDIR /target/usr/local/bin/
|
||||||
|
COPY *.sh .
|
||||||
|
RUN dos2unix *.sh
|
||||||
|
RUN chmod -v +x *.sh
|
||||||
|
|
||||||
|
###
|
||||||
|
# PACKAGES
|
||||||
|
|
||||||
|
FROM scratch as packages
|
||||||
|
|
||||||
|
COPY --from=ndi-sdk /usr/src/ndi-sdk/*.pkg.* /packages/
|
||||||
|
COPY --from=ffmpeg-ndi /usr/src/ffmpeg-ndi/*.pkg.* /packages/
|
||||||
|
COPY --from=fakesilence /usr/local/bin/fakesilence /target/usr/local/bin/
|
||||||
|
|
||||||
|
###
|
||||||
|
# PACKAGE INSTALL
|
||||||
|
|
||||||
|
FROM base AS install
|
||||||
|
|
||||||
|
USER root
|
||||||
|
# COPY --from=powerpill /usr/src/powerpill/*.pkg.* /tmp/
|
||||||
|
# RUN \
|
||||||
|
# --mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
# --mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
# pacman --noconfirm -U /tmp/*.pkg.*; rm /tmp/*.pkg.*
|
||||||
|
|
||||||
|
#COPY --from=yay /usr/src/yay/*.pkg.* /tmp/
|
||||||
|
COPY --from=ndi-sdk /usr/src/ndi-sdk/*.pkg.* /tmp/
|
||||||
|
COPY --from=ffmpeg-ndi /usr/src/ffmpeg-ndi/*.pkg.* /tmp/
|
||||||
|
RUN \
|
||||||
|
--mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
rm -f /var/cache/pacman/pkg/cache.lck; pacman --noconfirm -U /tmp/*.pkg.*; rm /tmp/*.pkg.*
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
--mount=type=cache,target=/var/cache/pacman/pkg,sharing=locked \
|
||||||
|
--mount=type=cache,target=/tmp/build/.cache \
|
||||||
|
rm -f /var/cache/pacman/pkg/cache.lck; pacman -S --noconfirm --needed sudo realtime-privileges
|
||||||
|
|
||||||
COPY --from=fakesilence /usr/local/bin/fakesilence /usr/local/bin/
|
COPY --from=fakesilence /usr/local/bin/fakesilence /usr/local/bin/
|
||||||
|
|
||||||
COPY --from=0 /target/ /
|
COPY --from=files /target/ /
|
||||||
CMD ["ndi-feeder.sh"]
|
|
||||||
|
|
||||||
|
RUN rm -rf /var/cache/pacman/pkg/*
|
||||||
|
|
||||||
|
###
|
||||||
|
# FINAL IMAGE
|
||||||
|
|
||||||
|
FROM base AS final-image
|
||||||
|
|
||||||
|
# squash all the package installation into a single
|
||||||
|
COPY --from=install / /
|
||||||
|
|
||||||
|
RUN useradd -m -u 1000 -G wheel,realtime,audio,video ndi-feeder
|
||||||
|
RUN echo "ndi-feeder:ndi-feeder" | chpasswd
|
||||||
|
|
||||||
|
USER ndi-feeder
|
||||||
|
CMD ["ndi-feeder.sh"]
|
||||||
STOPSIGNAL SIGTERM
|
STOPSIGNAL SIGTERM
|
||||||
|
|
|
@ -9,8 +9,8 @@ replace (
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/icedream/go-stagelinq v0.0.1
|
github.com/icedream/go-stagelinq v0.0.1
|
||||||
github.com/icedream/livestream-tools/icedreammusic/metacollector e27172751086
|
github.com/icedream/livestream-tools/icedreammusic/metacollector v0.0.0-20221208055945-e27172751086
|
||||||
github.com/icedream/livestream-tools/icedreammusic/tuna d83cb4af0567
|
github.com/icedream/livestream-tools/icedreammusic/tuna v0.0.0-20221205042012-d83cb4af0567
|
||||||
)
|
)
|
||||||
|
|
||||||
require golang.org/x/text v0.4.0 // indirect
|
require golang.org/x/text v0.7.0 // indirect
|
||||||
|
|
|
@ -10,10 +10,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
@ -5,7 +5,7 @@ go 1.19
|
||||||
require (
|
require (
|
||||||
github.com/gin-contrib/cors v1.4.0
|
github.com/gin-contrib/cors v1.4.0
|
||||||
github.com/gin-gonic/gin v1.9.0
|
github.com/gin-gonic/gin v1.9.0
|
||||||
github.com/icedream/livestream-tools/icedreammusic/tuna v0.0.0-20230302092605-950fba55448e
|
github.com/icedream/livestream-tools/icedreammusic/tuna v0.0.0-20230302143311-ad3e9704c029
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|
|
@ -3,7 +3,7 @@ module github.com/icedream/livestream-tools/icedreammusic/tunaposter
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/icedream/livestream-tools/icedreammusic/tuna d83cb4af0567
|
github.com/icedream/livestream-tools/icedreammusic/tuna v0.0.0-20221205042012-d83cb4af0567
|
||||||
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20191105091915-95d230a53780
|
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20191105091915-95d230a53780
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,21 +3,6 @@
|
||||||
"config:base"
|
"config:base"
|
||||||
],
|
],
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
{
|
|
||||||
"matchPackagePatterns": [
|
|
||||||
"github.com/icedream/livestream-tools/icedreammusic/*"
|
|
||||||
],
|
|
||||||
"automerge": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"matchUpdateTypes": [
|
|
||||||
"minor",
|
|
||||||
"patch",
|
|
||||||
"pin",
|
|
||||||
"digest"
|
|
||||||
],
|
|
||||||
"automerge": true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"matchPackageNames": [
|
"matchPackageNames": [
|
||||||
"savonet/liquidsoap-ci-build"
|
"savonet/liquidsoap-ci-build"
|
||||||
|
@ -39,4 +24,4 @@
|
||||||
"packageNameTemplate": "homebrew/{{{ lowercase depNameUppercase }}}"
|
"packageNameTemplate": "homebrew/{{{ lowercase depNameUppercase }}}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue