Compare commits

..

8 Commits

Author SHA1 Message Date
Miroslav Šedivý
1b84c7e7ba fix invalid errors. 2024-07-20 14:48:54 +02:00
Miroslav Šedivý
21a4b2b797 autostart broadcast only if url is set. 2024-06-18 23:35:22 +02:00
Miroslav Šedivý
5e96bca296 update readme. 2024-06-17 23:20:42 +02:00
Miroslav Šedivý
c78d797fe7 fix typo. 2024-06-17 23:16:48 +02:00
Miroslav Šedivý
57596315e9 broadcast_autostart as config option, #398. 2024-06-17 23:14:12 +02:00
Miroslav Šedivý
0d7887e9d2 workaround for firefox read clipboard, #373.
Firefox 122+ incorrectly reports that it can read the clipboard but it can't instead it hangs when reading clipboard, until user clicks on the page and the click itself is not handled by the page at all, also the clipboard reads always fail with "Clipboard read operation is not allowed."
2024-06-16 22:55:13 +02:00
Miroslav Šedivý
978fd8977d google does not archive chrome 111 anymore. 2024-06-16 22:28:32 +02:00
Miroslav Šedivý
4ab5901ba9 sync clipboard only if in focus #373. 2024-06-16 22:27:46 +02:00
14 changed files with 55 additions and 35 deletions

View File

@ -3,7 +3,9 @@ FROM $BASE_IMAGE
# latest working version with EGL: 111.0.5563.146, revert when resolved # latest working version with EGL: 111.0.5563.146, revert when resolved
# 112.0.5615.49 fails: https://github.com/VirtualGL/virtualgl/issues/229 # 112.0.5615.49 fails: https://github.com/VirtualGL/virtualgl/issues/229
ARG SRC_URL="https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_111.0.5563.146-1_amd64.deb" # google does not provide a direct link to the deb file anymore
# ARG SRC_URL="https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_111.0.5563.146-1_amd64.deb"
ARG SRC_URL="https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb"
# #
# install google chrome # install google chrome

View File

@ -313,7 +313,15 @@
} }
get clipboard_read_available() { get clipboard_read_available() {
return 'clipboard' in navigator && typeof navigator.clipboard.readText === 'function' return (
'clipboard' in navigator &&
typeof navigator.clipboard.readText === 'function' &&
// Firefox 122+ incorrectly reports that it can read the clipboard but it can't
// instead it hangs when reading clipboard, until user clicks on the page
// and the click itself is not handled by the page at all, also the clipboard
// reads always fail with "Clipboard read operation is not allowed."
navigator.userAgent.indexOf('Firefox') == -1
)
} }
get clipboard_write_available() { get clipboard_write_available() {
@ -630,7 +638,7 @@
} }
async syncClipboard() { async syncClipboard() {
if (this.clipboard_read_available) { if (this.clipboard_read_available && window.document.hasFocus()) {
try { try {
const text = await navigator.clipboard.readText() const text = await navigator.clipboard.readText()
if (this.clipboard !== text) { if (this.clipboard !== text) {

View File

@ -6,6 +6,7 @@
- Added nvidia support for firefox. - Added nvidia support for firefox.
- Added `?lang=<lang>` parameter to the URL, which will set the language of the interface (by @mbattista). - Added `?lang=<lang>` parameter to the URL, which will set the language of the interface (by @mbattista).
- Added `?show_side=1` and `?mute_chat=1` parameter to the URL, for chat mute and show side (by @mbattista). - Added `?show_side=1` and `?mute_chat=1` parameter to the URL, for chat mute and show side (by @mbattista).
- Added `NEKO_BROADCAST_AUTOSTART` to automatically start or do not start broadcasting when the room is created. By default, it is set to `true` because it was the previous behavior.
### Bugs ### Bugs
- Fix incorrect version sorting for chromium, microsoft-edge, opera and ungoogledchromium. - Fix incorrect version sorting for chromium, microsoft-edge, opera and ungoogledchromium.

View File

@ -107,9 +107,11 @@ nat1to1: <ip>
#### `NEKO_BROADCAST_PIPELINE`: #### `NEKO_BROADCAST_PIPELINE`:
- Makes it possible to create custom gstreamer pipeline used for broadcasting, strings `{url}`, `{device}` and `{display}` will be replaced. - Makes it possible to create custom gstreamer pipeline used for broadcasting, strings `{url}`, `{device}` and `{display}` will be replaced.
#### `NEKO_BROADCAST_URL`: #### `NEKO_BROADCAST_URL`:
- Set a default URL for broadcast streams. Setting this value will automatically enable broadcasting when n.eko starts. It can be disabled/changed later by admins in the GUI. - Set a default URL for broadcast streams. It can be disabled/changed later by admins in the GUI.
- e.g. `rtmp://<your-server>:1935/ingest/<stream-key>` - e.g. `rtmp://<your-server>:1935/ingest/<stream-key>`
#### `NEKO_BROADCAST_AUTOSTART`:
- Automatically start broadcasting when neko starts and broadcast_url is set.
- e.g. `true`
### Server ### Server
#### `NEKO_BIND`: #### `NEKO_BIND`:

View File

@ -22,7 +22,7 @@ type BroacastManagerCtx struct {
started bool started bool
} }
func broadcastNew(pipelineFn func(url string) (string, error), defaultUrl string) *BroacastManagerCtx { func broadcastNew(pipelineFn func(url string) (string, error), url string, started bool) *BroacastManagerCtx {
logger := log.With(). logger := log.With().
Str("module", "capture"). Str("module", "capture").
Str("submodule", "broadcast"). Str("submodule", "broadcast").
@ -31,8 +31,8 @@ func broadcastNew(pipelineFn func(url string) (string, error), defaultUrl string
return &BroacastManagerCtx{ return &BroacastManagerCtx{
logger: logger, logger: logger,
pipelineFn: pipelineFn, pipelineFn: pipelineFn,
url: defaultUrl, url: url,
started: defaultUrl != "", started: started && url != "",
} }
} }

View File

@ -31,7 +31,7 @@ func New(desktop types.DesktopManager, config *config.Capture) *CaptureManagerCt
// sinks // sinks
broadcast: broadcastNew(func(url string) (string, error) { broadcast: broadcastNew(func(url string) (string, error) {
return NewBroadcastPipeline(config.AudioDevice, config.Display, config.BroadcastPipeline, url) return NewBroadcastPipeline(config.AudioDevice, config.Display, config.BroadcastPipeline, url)
}, config.BroadcastUrl), }, config.BroadcastUrl, config.BroadcastAutostart),
audio: streamSinkNew(config.AudioCodec, func() (string, error) { audio: streamSinkNew(config.AudioCodec, func() (string, error) {
return NewAudioPipeline(config.AudioCodec, config.AudioDevice, config.AudioPipeline, config.AudioBitrate) return NewAudioPipeline(config.AudioCodec, config.AudioDevice, config.AudioPipeline, config.AudioBitrate)
}, "audio"), }, "audio"),

View File

@ -36,6 +36,7 @@ type Capture struct {
// broadcast // broadcast
BroadcastPipeline string BroadcastPipeline string
BroadcastUrl string BroadcastUrl string
BroadcastAutostart bool
} }
func (Capture) Init(cmd *cobra.Command) error { func (Capture) Init(cmd *cobra.Command) error {
@ -155,11 +156,16 @@ func (Capture) Init(cmd *cobra.Command) error {
return err return err
} }
cmd.PersistentFlags().String("broadcast_url", "", "URL for broadcasting, setting this value will automatically enable broadcasting") cmd.PersistentFlags().String("broadcast_url", "", "a default default URL for broadcast streams, can be disabled/changed later by admins in the GUI")
if err := viper.BindPFlag("broadcast_url", cmd.PersistentFlags().Lookup("broadcast_url")); err != nil { if err := viper.BindPFlag("broadcast_url", cmd.PersistentFlags().Lookup("broadcast_url")); err != nil {
return err return err
} }
cmd.PersistentFlags().Bool("broadcast_autostart", true, "automatically start broadcasting when neko starts and broadcast_url is set")
if err := viper.BindPFlag("broadcast_autostart", cmd.PersistentFlags().Lookup("broadcast_autostart")); err != nil {
return err
}
return nil return nil
} }
@ -247,4 +253,5 @@ func (s *Capture) Set() {
s.BroadcastPipeline = viper.GetString("broadcast_pipeline") s.BroadcastPipeline = viper.GetString("broadcast_pipeline")
s.BroadcastUrl = viper.GetString("broadcast_url") s.BroadcastUrl = viper.GetString("broadcast_url")
s.BroadcastAutostart = viper.GetBool("broadcast_autostart")
} }

View File

@ -46,9 +46,9 @@ const (
) )
const ( const (
BORADCAST_STATUS = "broadcast/status" BROADCAST_STATUS = "broadcast/status"
BORADCAST_CREATE = "broadcast/create" BROADCAST_CREATE = "broadcast/create"
BORADCAST_DESTROY = "broadcast/destroy" BROADCAST_DESTROY = "broadcast/destroy"
) )
const ( const (

View File

@ -175,7 +175,7 @@ func (h *MessageHandler) adminGive(id string, session types.Session, payload *me
ID: id, ID: id,
Target: payload.ID, Target: payload.ID,
}, nil); err != nil { }, nil); err != nil {
h.logger.Warn().Err(err).Msgf("broadcasting event %s has failed", event.CONTROL_LOCKED) h.logger.Warn().Err(err).Msgf("broadcasting event %s has failed", event.CONTROL_GIVE)
return err return err
} }
@ -207,7 +207,7 @@ func (h *MessageHandler) adminMute(id string, session types.Session, payload *me
Target: target.ID(), Target: target.ID(),
ID: id, ID: id,
}, nil); err != nil { }, nil); err != nil {
h.logger.Warn().Err(err).Msgf("broadcasting event %s has failed", event.ADMIN_UNMUTE) h.logger.Warn().Err(err).Msgf("broadcasting event %s has failed", event.ADMIN_MUTE)
return err return err
} }

View File

@ -6,7 +6,7 @@ import (
"m1k1o/neko/internal/types/message" "m1k1o/neko/internal/types/message"
) )
func (h *MessageHandler) boradcastCreate(session types.Session, payload *message.BroadcastCreate) error { func (h *MessageHandler) broadcastCreate(session types.Session, payload *message.BroadcastCreate) error {
broadcast := h.capture.Broadcast() broadcast := h.capture.Broadcast()
if !session.Admin() { if !session.Admin() {
@ -44,14 +44,14 @@ func (h *MessageHandler) boradcastCreate(session types.Session, payload *message
} }
} }
if err := h.boradcastStatus(nil); err != nil { if err := h.broadcastStatus(nil); err != nil {
return err return err
} }
return nil return nil
} }
func (h *MessageHandler) boradcastDestroy(session types.Session) error { func (h *MessageHandler) broadcastDestroy(session types.Session) error {
broadcast := h.capture.Broadcast() broadcast := h.capture.Broadcast()
if !session.Admin() { if !session.Admin() {
@ -70,18 +70,18 @@ func (h *MessageHandler) boradcastDestroy(session types.Session) error {
broadcast.Stop() broadcast.Stop()
if err := h.boradcastStatus(nil); err != nil { if err := h.broadcastStatus(nil); err != nil {
return err return err
} }
return nil return nil
} }
func (h *MessageHandler) boradcastStatus(session types.Session) error { func (h *MessageHandler) broadcastStatus(session types.Session) error {
broadcast := h.capture.Broadcast() broadcast := h.capture.Broadcast()
msg := message.BroadcastStatus{ msg := message.BroadcastStatus{
Event: event.BORADCAST_STATUS, Event: event.BROADCAST_STATUS,
IsActive: broadcast.Started(), IsActive: broadcast.Started(),
URL: broadcast.Url(), URL: broadcast.Url(),
} }
@ -89,7 +89,7 @@ func (h *MessageHandler) boradcastStatus(session types.Session) error {
// if no session, broadcast change // if no session, broadcast change
if session == nil { if session == nil {
if err := h.sessions.AdminBroadcast(msg, nil); err != nil { if err := h.sessions.AdminBroadcast(msg, nil); err != nil {
h.logger.Warn().Err(err).Msgf("broadcasting event %s has failed", event.BORADCAST_STATUS) h.logger.Warn().Err(err).Msgf("broadcasting event %s has failed", event.BROADCAST_STATUS)
return err return err
} }
@ -102,7 +102,7 @@ func (h *MessageHandler) boradcastStatus(session types.Session) error {
} }
if err := session.Send(msg); err != nil { if err := session.Send(msg); err != nil {
h.logger.Warn().Err(err).Msgf("sending event %s has failed", event.BORADCAST_STATUS) h.logger.Warn().Err(err).Msgf("sending event %s has failed", event.BROADCAST_STATUS)
return err return err
} }

View File

@ -17,7 +17,7 @@ func (h *MessageHandler) chat(id string, session types.Session, payload *message
Content: payload.Content, Content: payload.Content,
ID: id, ID: id,
}, nil); err != nil { }, nil); err != nil {
h.logger.Warn().Err(err).Msgf("broadcasting event %s has failed", event.CONTROL_RELEASE) h.logger.Warn().Err(err).Msgf("broadcasting event %s has failed", event.CHAT_MESSAGE)
return err return err
} }
return nil return nil
@ -34,7 +34,7 @@ func (h *MessageHandler) chatEmote(id string, session types.Session, payload *me
Emote: payload.Emote, Emote: payload.Emote,
ID: id, ID: id,
}, nil); err != nil { }, nil); err != nil {
h.logger.Warn().Err(err).Msgf("broadcasting event %s has failed", event.CONTROL_RELEASE) h.logger.Warn().Err(err).Msgf("broadcasting event %s has failed", event.CHAT_EMOTE)
return err return err
} }
return nil return nil

View File

@ -115,7 +115,7 @@ func (h *MessageHandler) controlGive(id string, session types.Session, payload *
ID: id, ID: id,
Target: payload.ID, Target: payload.ID,
}, nil); err != nil { }, nil); err != nil {
h.logger.Warn().Err(err).Msgf("broadcasting event %s has failed", event.CONTROL_LOCKED) h.logger.Warn().Err(err).Msgf("broadcasting event %s has failed", event.CONTROL_GIVE)
return err return err
} }

View File

@ -148,15 +148,15 @@ func (h *MessageHandler) Message(id string, raw []byte) error {
return h.screenSet(id, session, payload) return h.screenSet(id, session, payload)
}), "%s failed", header.Event) }), "%s failed", header.Event)
// Boradcast Events // Broadcast Events
case event.BORADCAST_CREATE: case event.BROADCAST_CREATE:
payload := &message.BroadcastCreate{} payload := &message.BroadcastCreate{}
return errors.Wrapf( return errors.Wrapf(
utils.Unmarshal(payload, raw, func() error { utils.Unmarshal(payload, raw, func() error {
return h.boradcastCreate(session, payload) return h.broadcastCreate(session, payload)
}), "%s failed", header.Event) }), "%s failed", header.Event)
case event.BORADCAST_DESTROY: case event.BROADCAST_DESTROY:
return errors.Wrapf(h.boradcastDestroy(session), "%s failed", header.Event) return errors.Wrapf(h.broadcastDestroy(session), "%s failed", header.Event)
// Admin Events // Admin Events
case event.ADMIN_LOCK: case event.ADMIN_LOCK:

View File

@ -30,7 +30,7 @@ func (h *MessageHandler) SessionCreated(id string, session types.Session) error
} }
// send broadcast status if admin // send broadcast status if admin
if err := h.boradcastStatus(session); err != nil { if err := h.broadcastStatus(session); err != nil {
return err return err
} }
} }
@ -78,7 +78,7 @@ func (h *MessageHandler) SessionConnected(id string, session types.Session) erro
Event: event.MEMBER_CONNECTED, Event: event.MEMBER_CONNECTED,
Member: session.Member(), Member: session.Member(),
}, nil); err != nil { }, nil); err != nil {
h.logger.Warn().Err(err).Msgf("broadcasting event %s has failed", event.CONTROL_RELEASE) h.logger.Warn().Err(err).Msgf("broadcasting event %s has failed", event.MEMBER_CONNECTED)
return err return err
} }