code refactoring, dependencies update
This commit is contained in:
@@ -63,6 +63,7 @@ languages:
|
||||
livestreamDownloadInfo: |
|
||||
This will monitor yet to start livestream. Each process will be executed with --wait-for-video 10.
|
||||
If an already started livestream is provided it will be still downloaded but its progress will not be tracked.
|
||||
Once started the livestream will be migrated to the downloads page.
|
||||
livestreamExperimentalWarning: This feature is still experimental. Something might break!
|
||||
german:
|
||||
urlInput: Video URL
|
||||
@@ -123,6 +124,7 @@ languages:
|
||||
livestreamDownloadInfo: |
|
||||
This will monitor yet to start livestream. Each process will be executed with --wait-for-video 10.
|
||||
If an already started livestream is provided it will be still downloaded but its progress will not be tracked.
|
||||
Once started the livestream will be migrated to the downloads page.
|
||||
livestreamExperimentalWarning: This feature is still experimental. Something might break!
|
||||
french:
|
||||
urlInput: URL vidéo de YouTube ou d'un autre service pris en charge
|
||||
@@ -185,6 +187,7 @@ languages:
|
||||
livestreamDownloadInfo: |
|
||||
This will monitor yet to start livestream. Each process will be executed with --wait-for-video 10.
|
||||
If an already started livestream is provided it will be still downloaded but its progress will not be tracked.
|
||||
Once started the livestream will be migrated to the downloads page.
|
||||
livestreamExperimentalWarning: This feature is still experimental. Something might break!
|
||||
italian:
|
||||
urlInput: URL Video (uno per linea)
|
||||
@@ -244,6 +247,7 @@ languages:
|
||||
livestreamDownloadInfo: |
|
||||
This will monitor yet to start livestream. Each process will be executed with --wait-for-video 10.
|
||||
If an already started livestream is provided it will be still downloaded but its progress will not be tracked.
|
||||
Once started the livestream will be migrated to the downloads page.
|
||||
livestreamExperimentalWarning: This feature is still experimental. Something might break!
|
||||
chinese:
|
||||
urlInput: 视频 URL
|
||||
@@ -304,6 +308,7 @@ languages:
|
||||
livestreamDownloadInfo: |
|
||||
This will monitor yet to start livestream. Each process will be executed with --wait-for-video 10.
|
||||
If an already started livestream is provided it will be still downloaded but its progress will not be tracked.
|
||||
Once started the livestream will be migrated to the downloads page.
|
||||
livestreamExperimentalWarning: This feature is still experimental. Something might break!
|
||||
spanish:
|
||||
urlInput: URL de YouTube u otro servicio compatible
|
||||
@@ -362,6 +367,7 @@ languages:
|
||||
livestreamDownloadInfo: |
|
||||
This will monitor yet to start livestream. Each process will be executed with --wait-for-video 10.
|
||||
If an already started livestream is provided it will be still downloaded but its progress will not be tracked.
|
||||
Once started the livestream will be migrated to the downloads page.
|
||||
livestreamExperimentalWarning: This feature is still experimental. Something might break!
|
||||
russian:
|
||||
urlInput: URL-адрес YouTube или любого другого поддерживаемого сервиса
|
||||
@@ -420,6 +426,7 @@ languages:
|
||||
livestreamDownloadInfo: |
|
||||
This will monitor yet to start livestream. Each process will be executed with --wait-for-video 10.
|
||||
If an already started livestream is provided it will be still downloaded but its progress will not be tracked.
|
||||
Once started the livestream will be migrated to the downloads page.
|
||||
livestreamExperimentalWarning: This feature is still experimental. Something might break!
|
||||
korean:
|
||||
urlInput: YouTube나 다른 지원되는 사이트의 URL
|
||||
@@ -478,6 +485,7 @@ languages:
|
||||
livestreamDownloadInfo: |
|
||||
This will monitor yet to start livestream. Each process will be executed with --wait-for-video 10.
|
||||
If an already started livestream is provided it will be still downloaded but its progress will not be tracked.
|
||||
Once started the livestream will be migrated to the downloads page.
|
||||
livestreamExperimentalWarning: This feature is still experimental. Something might break!
|
||||
japanese:
|
||||
urlInput: YouTubeまたはサポート済み動画のURL
|
||||
@@ -537,6 +545,7 @@ languages:
|
||||
livestreamDownloadInfo: |
|
||||
This will monitor yet to start livestream. Each process will be executed with --wait-for-video 10.
|
||||
If an already started livestream is provided it will be still downloaded but its progress will not be tracked.
|
||||
Once started the livestream will be migrated to the downloads page.
|
||||
livestreamExperimentalWarning: This feature is still experimental. Something might break!
|
||||
catalan:
|
||||
urlInput: URL de YouTube o d'un altre servei compatible
|
||||
@@ -595,6 +604,7 @@ languages:
|
||||
livestreamDownloadInfo: |
|
||||
This will monitor yet to start livestream. Each process will be executed with --wait-for-video 10.
|
||||
If an already started livestream is provided it will be still downloaded but its progress will not be tracked.
|
||||
Once started the livestream will be migrated to the downloads page.
|
||||
livestreamExperimentalWarning: This feature is still experimental. Something might break!
|
||||
ukrainian:
|
||||
urlInput: URL-адреса YouTube або будь-якого іншого підтримуваного сервісу
|
||||
@@ -653,6 +663,7 @@ languages:
|
||||
livestreamDownloadInfo: |
|
||||
This will monitor yet to start livestream. Each process will be executed with --wait-for-video 10.
|
||||
If an already started livestream is provided it will be still downloaded but its progress will not be tracked.
|
||||
Once started the livestream will be migrated to the downloads page.
|
||||
livestreamExperimentalWarning: This feature is still experimental. Something might break!
|
||||
polish:
|
||||
urlInput: Adres URL YouTube lub innej obsługiwanej usługi
|
||||
@@ -711,6 +722,7 @@ languages:
|
||||
livestreamDownloadInfo: |
|
||||
This will monitor yet to start livestream. Each process will be executed with --wait-for-video 10.
|
||||
If an already started livestream is provided it will be still downloaded but its progress will not be tracked.
|
||||
Once started the livestream will be migrated to the downloads page.
|
||||
livestreamExperimentalWarning: This feature is still experimental. Something might break!
|
||||
swedish:
|
||||
urlInput: Videolänk (en per rad)
|
||||
@@ -775,4 +787,5 @@ languages:
|
||||
livestreamDownloadInfo: |
|
||||
This will monitor yet to start livestream. Each process will be executed with --wait-for-video 10.
|
||||
If an already started livestream is provided it will be still downloaded but its progress will not be tracked.
|
||||
Once started the livestream will be migrated to the downloads page.
|
||||
livestreamExperimentalWarning: This feature is still experimental. Something might break!
|
||||
|
||||
@@ -39,10 +39,11 @@ type DownloadInfo = {
|
||||
}
|
||||
|
||||
export enum ProcessStatus {
|
||||
Pending = 0,
|
||||
Downloading,
|
||||
Completed,
|
||||
Errored,
|
||||
PENDING = 0,
|
||||
DOWNLOADING,
|
||||
COMPLETED,
|
||||
ERRORED,
|
||||
LIVESTREAM,
|
||||
}
|
||||
|
||||
type DownloadProgress = {
|
||||
|
||||
@@ -56,14 +56,16 @@ export function isRPCResponse(object: any): object is RPCResponse<any> {
|
||||
|
||||
export function mapProcessStatus(status: ProcessStatus) {
|
||||
switch (status) {
|
||||
case ProcessStatus.Pending:
|
||||
case ProcessStatus.PENDING:
|
||||
return 'Pending'
|
||||
case ProcessStatus.Downloading:
|
||||
case ProcessStatus.DOWNLOADING:
|
||||
return 'Downloading'
|
||||
case ProcessStatus.Completed:
|
||||
case ProcessStatus.COMPLETED:
|
||||
return 'Completed'
|
||||
case ProcessStatus.Errored:
|
||||
case ProcessStatus.ERRORED:
|
||||
return 'Error'
|
||||
case ProcessStatus.LIVESTREAM:
|
||||
return 'Livestream'
|
||||
default:
|
||||
return 'Pending'
|
||||
}
|
||||
|
||||
14
go.mod
14
go.mod
@@ -11,11 +11,11 @@ require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/reactivex/rxgo/v2 v2.5.0
|
||||
golang.org/x/oauth2 v0.21.0
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/sys v0.22.0
|
||||
golang.org/x/oauth2 v0.22.0
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/sys v0.24.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/sqlite v1.31.1
|
||||
modernc.org/sqlite v1.32.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -32,9 +32,9 @@ require (
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
github.com/teivah/onecontext v1.3.0 // indirect
|
||||
golang.org/x/crypto v0.25.0 // indirect
|
||||
modernc.org/gc/v3 v3.0.0-20240722195230-4a140ff9c08e // indirect
|
||||
modernc.org/libc v1.55.7 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
modernc.org/gc/v3 v3.0.0-20240801135723-a856999a2e4a // indirect
|
||||
modernc.org/libc v1.59.9 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
modernc.org/strutil v1.2.0 // indirect
|
||||
|
||||
16
go.sum
16
go.sum
@@ -63,6 +63,8 @@ go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
||||
@@ -72,14 +74,20 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
|
||||
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@@ -98,17 +106,23 @@ modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||
modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y=
|
||||
modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s=
|
||||
modernc.org/ccgo/v4 v4.20.5 h1:s04akhT2dysD0DFOlv9fkQ6oUTLPYgMnnDk9oaqjszM=
|
||||
modernc.org/ccgo/v4 v4.20.7 h1:skrinQsjxWfvj6nbC3ztZPJy+NuwmB3hV9zX/pthNYQ=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
|
||||
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||
modernc.org/gc/v2 v2.4.3 h1:Ik4ZcMbC7aY4ZDPUhzXVXi7GMub9QcXLTfXn3mWpNw8=
|
||||
modernc.org/gc/v2 v2.5.0 h1:bJ9ChznK1L1mUtAQtxi0wi5AtAs5jQuw4PrPHO5pb6M=
|
||||
modernc.org/gc/v3 v3.0.0-20240722195230-4a140ff9c08e h1:WPC4v0rNIFb2PY+nBBEEKyugPPRHPzUgyN3xZPpGK58=
|
||||
modernc.org/gc/v3 v3.0.0-20240722195230-4a140ff9c08e/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||
modernc.org/gc/v3 v3.0.0-20240801135723-a856999a2e4a h1:CfbpOLEo2IwNzJdMvE8aiRbPMxoTpgAJeyePh0SmO8M=
|
||||
modernc.org/gc/v3 v3.0.0-20240801135723-a856999a2e4a/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||
modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
|
||||
modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
|
||||
modernc.org/libc v1.55.7 h1:/5PMGAF3tyZhK72WpoqeLNtgUUpYMrnhT+Gm/5tVDgs=
|
||||
modernc.org/libc v1.55.7/go.mod h1:JXguUpMkbw1gknxspNE9XaG+kk9hDAAnBxpA6KGLiyA=
|
||||
modernc.org/libc v1.59.9 h1:k+nNDDakwipimgmJ1D9H466LhFeSkaPPycAs1OZiDmY=
|
||||
modernc.org/libc v1.59.9/go.mod h1:EY/egGEU7Ju66eU6SBqCNYaFUDuc4npICkMWnU5EE3A=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
|
||||
@@ -119,6 +133,8 @@ modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
|
||||
modernc.org/sqlite v1.31.1 h1:XVU0VyzxrYHlBhIs1DiEgSl0ZtdnPtbLVy8hSkzxGrs=
|
||||
modernc.org/sqlite v1.31.1/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA=
|
||||
modernc.org/sqlite v1.32.0 h1:6BM4uGza7bWypsw4fdLRsLxut6bHe4c58VeqjRgST8s=
|
||||
modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
@@ -64,7 +64,8 @@ func (m *MessageQueue) downloadConsumer() {
|
||||
|
||||
if p.Progress.Status != StatusCompleted {
|
||||
if p.Livestream {
|
||||
go p.Start() // livestreams have higher priorty and will ignore the queue
|
||||
// livestreams have higher priorty and they ignore the semaphore
|
||||
go p.Start()
|
||||
} else {
|
||||
p.Start()
|
||||
}
|
||||
|
||||
@@ -168,10 +168,6 @@ func (p *Process) Start() {
|
||||
ETA: progress.Eta,
|
||||
}
|
||||
|
||||
if p.Livestream {
|
||||
p.Progress.Status = StatusLivestream
|
||||
}
|
||||
|
||||
slog.Info("progress",
|
||||
slog.String("id", p.getShortId()),
|
||||
slog.String("url", p.Url),
|
||||
|
||||
@@ -51,11 +51,13 @@ type serverConfig struct {
|
||||
mdb *internal.MemoryDB
|
||||
db *sql.DB
|
||||
mq *internal.MessageQueue
|
||||
lm *livestream.Monitor
|
||||
}
|
||||
|
||||
func RunBlocking(cfg *RunConfig) {
|
||||
var mdb internal.MemoryDB
|
||||
|
||||
// ---- LOGGING ---------------------------------------------------
|
||||
logWriters := []io.Writer{
|
||||
os.Stdout,
|
||||
logging.NewObservableLogger(), // for web-ui
|
||||
@@ -84,6 +86,7 @@ func RunBlocking(cfg *RunConfig) {
|
||||
|
||||
// make the new logger the default one with all the new writers
|
||||
slog.SetDefault(logger)
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
db, err := sql.Open("sqlite", cfg.DBPath)
|
||||
if err != nil {
|
||||
@@ -99,9 +102,12 @@ func RunBlocking(cfg *RunConfig) {
|
||||
panic(err)
|
||||
}
|
||||
mq.SetupConsumers()
|
||||
|
||||
go mdb.Restore(mq)
|
||||
|
||||
lm := livestream.NewMonitor(mq, &mdb)
|
||||
go lm.Schedule()
|
||||
go lm.Restore()
|
||||
|
||||
srv := newServer(serverConfig{
|
||||
frontend: cfg.App,
|
||||
swagger: cfg.Swagger,
|
||||
@@ -110,6 +116,7 @@ func RunBlocking(cfg *RunConfig) {
|
||||
mdb: &mdb,
|
||||
mq: mq,
|
||||
db: db,
|
||||
lm: lm,
|
||||
})
|
||||
|
||||
go gracefulShutdown(srv, &mdb)
|
||||
@@ -140,18 +147,14 @@ func RunBlocking(cfg *RunConfig) {
|
||||
}
|
||||
|
||||
func newServer(c serverConfig) *http.Server {
|
||||
lm := livestream.NewMonitor(c.mq, c.mdb)
|
||||
go lm.Schedule()
|
||||
go lm.Restore()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
lm.Persist()
|
||||
c.lm.Persist()
|
||||
time.Sleep(time.Minute * 5)
|
||||
}
|
||||
}()
|
||||
|
||||
service := ytdlpRPC.Container(c.mdb, c.mq, lm)
|
||||
service := ytdlpRPC.Container(c.mdb, c.mq, c.lm)
|
||||
rpc.Register(service)
|
||||
|
||||
r := chi.NewRouter()
|
||||
@@ -236,6 +239,7 @@ func gracefulShutdown(srv *http.Server, db *internal.MemoryDB) {
|
||||
|
||||
defer func() {
|
||||
db.Persist()
|
||||
|
||||
stop()
|
||||
srv.Shutdown(context.Background())
|
||||
}()
|
||||
|
||||
Reference in New Issue
Block a user