flamenco/internal/upnp_ssdp/server.go
Sybren A. Stüvel 02fac6a4df Change Go package name from git.blender.org to projects.blender.org
Change the package base name of the Go code, from
`git.blender.org/flamenco` to `projects.blender.org/studio/flamenco`.

The old location, `git.blender.org`, has no longer been use since the
[migration to Gitea][1]. The new package names now reflect the actual
location where Flamenco is hosted.

[1]: https://code.blender.org/2023/02/new-blender-development-infrastructure/
2023-08-01 12:42:31 +02:00

129 lines
3.7 KiB
Go

package upnp_ssdp
/* ***** BEGIN GPL LICENSE BLOCK *****
*
* Original Code Copyright (C) 2022 Blender Foundation.
*
* This file is part of Flamenco.
*
* Flamenco is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* Flamenco is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Flamenco. If not, see <https://www.gnu.org/licenses/>.
*
* ***** END GPL LICENSE BLOCK ***** */
import (
"context"
"fmt"
"net/url"
"path"
"github.com/fromkeith/gossdp"
"github.com/rs/zerolog"
"projects.blender.org/studio/flamenco/internal/appinfo"
)
// Server advertises services via UPnP/SSDP.
type Server struct {
ssdp *gossdp.Ssdp
log *zerolog.Logger
wrappedLog *ssdpLogger
}
func NewServer(logger zerolog.Logger) (*Server, error) {
wrap := wrappedLogger(&logger)
ssdp, err := gossdp.NewSsdpWithLogger(nil, wrap)
if err != nil {
return nil, err
}
return &Server{ssdp, &logger, wrap}, nil
}
// AddAdvertisement adds a service advertisement for Flamenco Manager.
// Must be called before calling Run().
func (s *Server) AddAdvertisement(serviceLocation string) {
// Define the service we want to advertise
serverDef := gossdp.AdvertisableServer{
ServiceType: FlamencoServiceType,
DeviceUuid: FlamencoUUID,
Location: serviceLocation,
MaxAge: 3600, // Number of seconds this advertisement is valid for.
}
s.ssdp.AdvertiseServer(serverDef)
s.log.Debug().Str("location", serviceLocation).Msg("UPnP/SSDP location registered")
}
// AddAdvertisementURLs constructs a service location from the given URLs, and
// adds the advertisement for it.
func (s *Server) AddAdvertisementURLs(baseURLs []url.URL) {
for _, url := range baseURLs {
url.Path = path.Join(url.Path, serviceDescriptionPath)
s.AddAdvertisement(url.String())
}
}
// Run starts the advertisement, and blocks until the context is closed.
func (s *Server) Run(ctx context.Context) {
s.log.Info().Msg("UPnP/SSDP advertisement starting")
isStopping := false
go func() {
// There is a bug in the SSDP library, where closing the server can cause a panic.
defer func() {
if isStopping {
// Only capture a panic when we expect one.
value := recover()
s.log.Debug().Interface("value", value).Msg("recovered from panic in SSDP library")
}
}()
s.ssdp.Start()
}()
<-ctx.Done()
s.log.Debug().Msg("UPnP/SSDP advertisement stopping")
// Sneakily disable warnings when shutting down, otherwise the read operation
// from the UDP socket will cause a warning.
tempLog := s.log.Level(zerolog.ErrorLevel)
s.wrappedLog.zlog = &tempLog
isStopping = true
s.ssdp.Stop()
s.wrappedLog.zlog = s.log
s.log.Info().Msg("UPnP/SSDP advertisement stopped")
}
func (s *Server) Description() Description {
return Description{
SpecVersion: SpecVersion{1, 0},
URLBase: "/",
Device: Device{
DeviceType: FlamencoServiceType,
FriendlyName: appinfo.FormattedApplicationInfo(),
ModelName: appinfo.ApplicationName,
ModelDescription: "Flamenco render farm, Manager component",
ModelURL: "https://flamenco.io/",
UDN: fmt.Sprintf("uuid:%s", FlamencoUUID),
Manufacturer: "Blender",
ManufacturerURL: "https://www.blender.org/",
ServiceList: []string{},
PresentationURL: "/",
},
}
}
func (s *Server) DescriptionPath() string {
return serviceDescriptionPath
}