From ebded5057f43f5c0281af712f3a00ed457a7eb43 Mon Sep 17 00:00:00 2001 From: CGH0S7 <776459475@qq.com> Date: Tue, 23 Jun 2026 17:01:25 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=8F=91=E5=B8=83=20v1.0.1=20?= =?UTF-8?q?=E5=B9=B6=E6=94=AF=E6=8C=81=E7=AB=AF=E5=8F=A3=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/cmd/server/main.go | 46 +++++++++++++++++++++---- backend/internal/stream/server.go | 12 ++++--- frontend/lib/pages/settings_page.dart | 49 +++++++++++++++------------ frontend/pubspec.yaml | 2 +- 4 files changed, 75 insertions(+), 34 deletions(-) diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 55a5aa0..40804f1 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -1,7 +1,10 @@ package main import ( + "flag" + "fmt" "net/http" + "os" "time" "hightube/internal/api" @@ -11,9 +14,16 @@ import ( "hightube/internal/stream" ) +type serverConfig struct { + apiPort int + rtmpPort int +} + func main() { + cfg := parseFlags() + monitor.Init(2000) - monitor.Infof("Starting Hightube Server v1.0.0-Beta4.8") + monitor.Infof("Starting Hightube Server v1.0.1") // Initialize Database and run auto-migrations db.InitDB() @@ -21,28 +31,52 @@ func main() { // Initialize Chat WebSocket Hub chat.InitChat() - srv := stream.NewRTMPServer() + srv := stream.NewRTMPServer(fmt.Sprintf("%d", cfg.rtmpPort)) // Start the API server in a goroutine so it doesn't block the RTMP server go func() { + apiAddr := fmt.Sprintf(":%d", cfg.apiPort) r := api.SetupRouter(srv) httpServer := &http.Server{ - Addr: ":8080", + Addr: apiAddr, Handler: r, ReadHeaderTimeout: 5 * time.Second, IdleTimeout: 60 * time.Second, MaxHeaderBytes: 1 << 20, } - monitor.Infof("API server listening on :8080") - monitor.Infof("Web console listening on :8080/admin") + monitor.Infof("API server listening on %s", apiAddr) + monitor.Infof("Web console listening on %s/admin", apiAddr) if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { monitor.Errorf("Failed to start API server: %v", err) } }() // Setup and start the RTMP server + rtmpAddr := fmt.Sprintf(":%d", cfg.rtmpPort) monitor.Infof("Ready to receive RTMP streams from OBS") - if err := srv.Start(":1935"); err != nil { + if err := srv.Start(rtmpAddr); err != nil { monitor.Errorf("Failed to start RTMP server: %v", err) } } + +func parseFlags() serverConfig { + cfg := serverConfig{} + flag.IntVar(&cfg.apiPort, "api-port", 8080, "API/Web console listen port") + flag.IntVar(&cfg.rtmpPort, "rtmp-port", 1935, "RTMP listen port") + flag.Parse() + + if !validPort(cfg.apiPort) { + fmt.Fprintf(os.Stderr, "invalid --api-port %d: port must be between 1 and 65535\n", cfg.apiPort) + os.Exit(2) + } + if !validPort(cfg.rtmpPort) { + fmt.Fprintf(os.Stderr, "invalid --rtmp-port %d: port must be between 1 and 65535\n", cfg.rtmpPort) + os.Exit(2) + } + + return cfg +} + +func validPort(port int) bool { + return port >= 1 && port <= 65535 +} diff --git a/backend/internal/stream/server.go b/backend/internal/stream/server.go index d974963..9d60c30 100644 --- a/backend/internal/stream/server.go +++ b/backend/internal/stream/server.go @@ -40,6 +40,7 @@ type RTMPServer struct { transcoders map[string][]*variantTranscoder thumbnailJobs map[string]context.CancelFunc internalPublishKey string + rtmpPort string thumbnailDir string mutex sync.RWMutex } @@ -100,13 +101,14 @@ func (w *bufferedWriteFlusher) Flush() error { return nil } -// NewRTMPServer creates and initializes a new media server -func NewRTMPServer() *RTMPServer { +// NewRTMPServer creates and initializes a new media server. +func NewRTMPServer(rtmpPort string) *RTMPServer { s := &RTMPServer{ channels: make(map[string]*pubsub.Queue), transcoders: make(map[string][]*variantTranscoder), thumbnailJobs: make(map[string]context.CancelFunc), internalPublishKey: generateInternalPublishKey(), + rtmpPort: rtmpPort, thumbnailDir: filepath.Join(os.TempDir(), "hightube-thumbnails"), server: &rtmp.Server{}, } @@ -348,8 +350,8 @@ func (s *RTMPServer) startVariantTranscoders(roomID string) { launch := make([]*variantTranscoder, 0, len(supportedQualities)) for quality, profile := range supportedQualities { ctx, cancel := context.WithCancel(context.Background()) - inputURL := fmt.Sprintf("rtmp://127.0.0.1:1935/live/%s", roomID) - outputURL := fmt.Sprintf("rtmp://127.0.0.1:1935/variant/%s/%s/%s", roomID, quality, s.internalPublishKey) + inputURL := fmt.Sprintf("rtmp://127.0.0.1:%s/live/%s", s.rtmpPort, roomID) + outputURL := fmt.Sprintf("rtmp://127.0.0.1:%s/variant/%s/%s/%s", s.rtmpPort, roomID, quality, s.internalPublishKey) cmd := exec.CommandContext( ctx, "ffmpeg", @@ -475,7 +477,7 @@ func (s *RTMPServer) captureThumbnail(roomID string) { "-y", "-loglevel", "error", "-rtmp_live", "live", - "-i", fmt.Sprintf("rtmp://127.0.0.1:1935/live/%s", roomID), + "-i", fmt.Sprintf("rtmp://127.0.0.1:%s/live/%s", s.rtmpPort, roomID), "-frames:v", "1", "-q:v", "4", tempPath, diff --git a/frontend/lib/pages/settings_page.dart b/frontend/lib/pages/settings_page.dart index 294e92b..d6e6953 100644 --- a/frontend/lib/pages/settings_page.dart +++ b/frontend/lib/pages/settings_page.dart @@ -124,7 +124,10 @@ class _SettingsPageState extends State { return Scaffold( appBar: AppBar( - title: Text(l10n.settings, style: TextStyle(fontWeight: FontWeight.bold)), + title: Text( + l10n.settings, + style: TextStyle(fontWeight: FontWeight.bold), + ), centerTitle: true, ), body: SingleChildScrollView( @@ -136,17 +139,21 @@ class _SettingsPageState extends State { _buildProfileSection(auth), const SizedBox(height: 32), ], - + _buildSectionTitle(l10n.language), const SizedBox(height: 16), DropdownButtonFormField( - initialValue: settings.locale == null - ? null - : AppLocalizations.supportedLocales.cast().firstWhere( - (l) => l?.languageCode == settings.locale?.languageCode && - l?.scriptCode == settings.locale?.scriptCode, - orElse: () => null, - ), + initialValue: settings.locale == null + ? null + : AppLocalizations.supportedLocales + .cast() + .firstWhere( + (l) => + l?.languageCode == + settings.locale?.languageCode && + l?.scriptCode == settings.locale?.scriptCode, + orElse: () => null, + ), decoration: InputDecoration( labelText: l10n.selectLanguage, prefixIcon: const Icon(Icons.language), @@ -155,10 +162,7 @@ class _SettingsPageState extends State { ), ), items: [ - DropdownMenuItem( - value: null, - child: Text(l10n.system), - ), + DropdownMenuItem(value: null, child: Text(l10n.system)), DropdownMenuItem( value: const Locale('en'), child: Text(l10n.english), @@ -168,7 +172,10 @@ class _SettingsPageState extends State { child: Text(l10n.simplifiedChinese), ), DropdownMenuItem( - value: const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'), + value: const Locale.fromSubtags( + languageCode: 'zh', + scriptCode: 'Hant', + ), child: Text(l10n.traditionalChinese), ), DropdownMenuItem( @@ -258,7 +265,10 @@ class _SettingsPageState extends State { }, ), const SizedBox(height: 20), - Text(l10n.accentColor, style: Theme.of(context).textTheme.labelLarge), + Text( + l10n.accentColor, + style: Theme.of(context).textTheme.labelLarge, + ), const SizedBox(height: 12), Wrap( spacing: 12, @@ -301,9 +311,7 @@ class _SettingsPageState extends State { SwitchListTile.adaptive( contentPadding: EdgeInsets.zero, title: Text(l10n.livePreviewThumbnails), - subtitle: Text( - l10n.livePreviewThumbnailsDesc, - ), + subtitle: Text(l10n.livePreviewThumbnailsDesc), value: settings.livePreviewThumbnailsEnabled, onChanged: settings.setLivePreviewThumbnailsEnabled, ), @@ -392,10 +400,7 @@ class _SettingsPageState extends State { "Hightube", style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), - Text( - "Version: 1.0.0-beta4.8", - style: TextStyle(color: Colors.grey), - ), + Text("Version: 1.0.1", style: TextStyle(color: Colors.grey)), Text( "Author: Highground-Soft", style: TextStyle(color: Colors.grey), diff --git a/frontend/pubspec.yaml b/frontend/pubspec.yaml index 5bc718a..eaa3222 100644 --- a/frontend/pubspec.yaml +++ b/frontend/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.0-beta4.8 +version: 1.0.1 environment: sdk: ^3.11.1