chore: 发布 v1.0.1 并支持端口参数
This commit is contained in:
@@ -1,7 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hightube/internal/api"
|
"hightube/internal/api"
|
||||||
@@ -11,9 +14,16 @@ import (
|
|||||||
"hightube/internal/stream"
|
"hightube/internal/stream"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type serverConfig struct {
|
||||||
|
apiPort int
|
||||||
|
rtmpPort int
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
cfg := parseFlags()
|
||||||
|
|
||||||
monitor.Init(2000)
|
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
|
// Initialize Database and run auto-migrations
|
||||||
db.InitDB()
|
db.InitDB()
|
||||||
@@ -21,28 +31,52 @@ func main() {
|
|||||||
// Initialize Chat WebSocket Hub
|
// Initialize Chat WebSocket Hub
|
||||||
chat.InitChat()
|
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
|
// Start the API server in a goroutine so it doesn't block the RTMP server
|
||||||
go func() {
|
go func() {
|
||||||
|
apiAddr := fmt.Sprintf(":%d", cfg.apiPort)
|
||||||
r := api.SetupRouter(srv)
|
r := api.SetupRouter(srv)
|
||||||
httpServer := &http.Server{
|
httpServer := &http.Server{
|
||||||
Addr: ":8080",
|
Addr: apiAddr,
|
||||||
Handler: r,
|
Handler: r,
|
||||||
ReadHeaderTimeout: 5 * time.Second,
|
ReadHeaderTimeout: 5 * time.Second,
|
||||||
IdleTimeout: 60 * time.Second,
|
IdleTimeout: 60 * time.Second,
|
||||||
MaxHeaderBytes: 1 << 20,
|
MaxHeaderBytes: 1 << 20,
|
||||||
}
|
}
|
||||||
monitor.Infof("API server listening on :8080")
|
monitor.Infof("API server listening on %s", apiAddr)
|
||||||
monitor.Infof("Web console listening on :8080/admin")
|
monitor.Infof("Web console listening on %s/admin", apiAddr)
|
||||||
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
monitor.Errorf("Failed to start API server: %v", err)
|
monitor.Errorf("Failed to start API server: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Setup and start the RTMP server
|
// Setup and start the RTMP server
|
||||||
|
rtmpAddr := fmt.Sprintf(":%d", cfg.rtmpPort)
|
||||||
monitor.Infof("Ready to receive RTMP streams from OBS")
|
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)
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ type RTMPServer struct {
|
|||||||
transcoders map[string][]*variantTranscoder
|
transcoders map[string][]*variantTranscoder
|
||||||
thumbnailJobs map[string]context.CancelFunc
|
thumbnailJobs map[string]context.CancelFunc
|
||||||
internalPublishKey string
|
internalPublishKey string
|
||||||
|
rtmpPort string
|
||||||
thumbnailDir string
|
thumbnailDir string
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
}
|
}
|
||||||
@@ -100,13 +101,14 @@ func (w *bufferedWriteFlusher) Flush() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRTMPServer creates and initializes a new media server
|
// NewRTMPServer creates and initializes a new media server.
|
||||||
func NewRTMPServer() *RTMPServer {
|
func NewRTMPServer(rtmpPort string) *RTMPServer {
|
||||||
s := &RTMPServer{
|
s := &RTMPServer{
|
||||||
channels: make(map[string]*pubsub.Queue),
|
channels: make(map[string]*pubsub.Queue),
|
||||||
transcoders: make(map[string][]*variantTranscoder),
|
transcoders: make(map[string][]*variantTranscoder),
|
||||||
thumbnailJobs: make(map[string]context.CancelFunc),
|
thumbnailJobs: make(map[string]context.CancelFunc),
|
||||||
internalPublishKey: generateInternalPublishKey(),
|
internalPublishKey: generateInternalPublishKey(),
|
||||||
|
rtmpPort: rtmpPort,
|
||||||
thumbnailDir: filepath.Join(os.TempDir(), "hightube-thumbnails"),
|
thumbnailDir: filepath.Join(os.TempDir(), "hightube-thumbnails"),
|
||||||
server: &rtmp.Server{},
|
server: &rtmp.Server{},
|
||||||
}
|
}
|
||||||
@@ -348,8 +350,8 @@ func (s *RTMPServer) startVariantTranscoders(roomID string) {
|
|||||||
launch := make([]*variantTranscoder, 0, len(supportedQualities))
|
launch := make([]*variantTranscoder, 0, len(supportedQualities))
|
||||||
for quality, profile := range supportedQualities {
|
for quality, profile := range supportedQualities {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
inputURL := fmt.Sprintf("rtmp://127.0.0.1:1935/live/%s", roomID)
|
inputURL := fmt.Sprintf("rtmp://127.0.0.1:%s/live/%s", s.rtmpPort, roomID)
|
||||||
outputURL := fmt.Sprintf("rtmp://127.0.0.1:1935/variant/%s/%s/%s", roomID, quality, s.internalPublishKey)
|
outputURL := fmt.Sprintf("rtmp://127.0.0.1:%s/variant/%s/%s/%s", s.rtmpPort, roomID, quality, s.internalPublishKey)
|
||||||
cmd := exec.CommandContext(
|
cmd := exec.CommandContext(
|
||||||
ctx,
|
ctx,
|
||||||
"ffmpeg",
|
"ffmpeg",
|
||||||
@@ -475,7 +477,7 @@ func (s *RTMPServer) captureThumbnail(roomID string) {
|
|||||||
"-y",
|
"-y",
|
||||||
"-loglevel", "error",
|
"-loglevel", "error",
|
||||||
"-rtmp_live", "live",
|
"-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",
|
"-frames:v", "1",
|
||||||
"-q:v", "4",
|
"-q:v", "4",
|
||||||
tempPath,
|
tempPath,
|
||||||
|
|||||||
@@ -124,7 +124,10 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(l10n.settings, style: TextStyle(fontWeight: FontWeight.bold)),
|
title: Text(
|
||||||
|
l10n.settings,
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
@@ -136,17 +139,21 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
_buildProfileSection(auth),
|
_buildProfileSection(auth),
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: 32),
|
||||||
],
|
],
|
||||||
|
|
||||||
_buildSectionTitle(l10n.language),
|
_buildSectionTitle(l10n.language),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
DropdownButtonFormField<Locale?>(
|
DropdownButtonFormField<Locale?>(
|
||||||
initialValue: settings.locale == null
|
initialValue: settings.locale == null
|
||||||
? null
|
? null
|
||||||
: AppLocalizations.supportedLocales.cast<Locale?>().firstWhere(
|
: AppLocalizations.supportedLocales
|
||||||
(l) => l?.languageCode == settings.locale?.languageCode &&
|
.cast<Locale?>()
|
||||||
l?.scriptCode == settings.locale?.scriptCode,
|
.firstWhere(
|
||||||
orElse: () => null,
|
(l) =>
|
||||||
),
|
l?.languageCode ==
|
||||||
|
settings.locale?.languageCode &&
|
||||||
|
l?.scriptCode == settings.locale?.scriptCode,
|
||||||
|
orElse: () => null,
|
||||||
|
),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: l10n.selectLanguage,
|
labelText: l10n.selectLanguage,
|
||||||
prefixIcon: const Icon(Icons.language),
|
prefixIcon: const Icon(Icons.language),
|
||||||
@@ -155,10 +162,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
items: [
|
items: [
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(value: null, child: Text(l10n.system)),
|
||||||
value: null,
|
|
||||||
child: Text(l10n.system),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
value: const Locale('en'),
|
value: const Locale('en'),
|
||||||
child: Text(l10n.english),
|
child: Text(l10n.english),
|
||||||
@@ -168,7 +172,10 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
child: Text(l10n.simplifiedChinese),
|
child: Text(l10n.simplifiedChinese),
|
||||||
),
|
),
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
value: const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'),
|
value: const Locale.fromSubtags(
|
||||||
|
languageCode: 'zh',
|
||||||
|
scriptCode: 'Hant',
|
||||||
|
),
|
||||||
child: Text(l10n.traditionalChinese),
|
child: Text(l10n.traditionalChinese),
|
||||||
),
|
),
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
@@ -258,7 +265,10 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
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),
|
const SizedBox(height: 12),
|
||||||
Wrap(
|
Wrap(
|
||||||
spacing: 12,
|
spacing: 12,
|
||||||
@@ -301,9 +311,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
SwitchListTile.adaptive(
|
SwitchListTile.adaptive(
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
title: Text(l10n.livePreviewThumbnails),
|
title: Text(l10n.livePreviewThumbnails),
|
||||||
subtitle: Text(
|
subtitle: Text(l10n.livePreviewThumbnailsDesc),
|
||||||
l10n.livePreviewThumbnailsDesc,
|
|
||||||
),
|
|
||||||
value: settings.livePreviewThumbnailsEnabled,
|
value: settings.livePreviewThumbnailsEnabled,
|
||||||
onChanged: settings.setLivePreviewThumbnailsEnabled,
|
onChanged: settings.setLivePreviewThumbnailsEnabled,
|
||||||
),
|
),
|
||||||
@@ -392,10 +400,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
"Hightube",
|
"Hightube",
|
||||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
Text(
|
Text("Version: 1.0.1", style: TextStyle(color: Colors.grey)),
|
||||||
"Version: 1.0.0-beta4.8",
|
|
||||||
style: TextStyle(color: Colors.grey),
|
|
||||||
),
|
|
||||||
Text(
|
Text(
|
||||||
"Author: Highground-Soft",
|
"Author: Highground-Soft",
|
||||||
style: TextStyle(color: Colors.grey),
|
style: TextStyle(color: Colors.grey),
|
||||||
|
|||||||
@@ -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
|
# 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
|
# 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.
|
# 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:
|
environment:
|
||||||
sdk: ^3.11.1
|
sdk: ^3.11.1
|
||||||
|
|||||||
Reference in New Issue
Block a user