diff --git a/cmd/binding/main.go b/cmd/binding/main.go index 92c71dd..b01bf79 100644 --- a/cmd/binding/main.go +++ b/cmd/binding/main.go @@ -2,20 +2,45 @@ package main import "C" import ( - "os" - "polaris/cmd" + "polaris/db" "polaris/log" + "polaris/server" ) func main() {} +var srv *server.Server +var port int + //export Start -func Start() { - cmd.Start(true) +func Start() (C.int, *C.char) { + if srv != nil { + return C.int(port), nil + } + log.InitLogger(true) + + log.Infof("------------------- Starting Polaris ---------------------") + dbClient, err := db.Open() + if err != nil { + log.Panicf("init db error: %v", err) + return C.int(0), C.CString(err.Error()) + } + + s := server.NewServer(dbClient) + if p, err := s.Start(""); err != nil { + return C.int(0), C.CString(err.Error()) + } else { + port = p + srv = s + return C.int(p), C.CString("") + } + } //export Stop func Stop() { - log.Infof("stop polaris") - os.Exit(0) + if srv != nil { + srv.Stop() + } + srv = nil } diff --git a/cmd/doc.go b/cmd/doc.go index d923b78..1d619dd 100644 --- a/cmd/doc.go +++ b/cmd/doc.go @@ -1,25 +1 @@ package cmd - -import ( - "os" - "polaris/db" - "polaris/log" - "polaris/server" -) - -func Start(sharedLib bool) { - if sharedLib || os.Getenv("GIN_MODE") == "release" { - log.InitLogger(true) - } - - log.Infof("------------------- Starting Polaris ---------------------") - dbClient, err := db.Open() - if err != nil { - log.Panicf("init db error: %v", err) - } - - s := server.NewServer(dbClient) - if err := s.Serve(); err != nil { - log.Errorf("server start error: %v", err) - } -} diff --git a/cmd/polaris/polaris.go b/cmd/polaris/polaris.go index 81053ee..8b5b443 100644 --- a/cmd/polaris/polaris.go +++ b/cmd/polaris/polaris.go @@ -1,7 +1,26 @@ package main -import "polaris/cmd" +import ( + "os" + "polaris/db" + "polaris/log" + "polaris/server" +) func main() { - cmd.Start(false) -} \ No newline at end of file + if os.Getenv("GIN_MODE") == "release" { + log.InitLogger(true) + } + + log.Infof("------------------- Starting Polaris ---------------------") + dbClient, err := db.Open() + if err != nil { + log.Panicf("init db error: %v", err) + } + + s := server.NewServer(dbClient) + if _, err := s.Start(":8080"); err != nil { + log.Errorf("server start error: %v", err) + } + select {} //wait indefinitely +} diff --git a/engine/scheduler.go b/engine/scheduler.go index 55bf684..059ed18 100644 --- a/engine/scheduler.go +++ b/engine/scheduler.go @@ -40,7 +40,7 @@ func (c *Engine) addSysCron() { return nil }) c.registerCronJob("check_series_new_release", "0 0 */12 * * *", c.checkAllSeriesNewSeason) - c.registerCronJob("update_import_lists", "0 30 * * * *", c.periodicallyUpdateImportlist) + c.registerCronJob("update_import_lists", "0 */20 * * * *", c.periodicallyUpdateImportlist) c.schedulers.Range(func(key string, value scheduler) bool { log.Debugf("add cron job: %v", key) diff --git a/pkg/buildin/torrent.go b/pkg/buildin/torrent.go index e74a22a..5122be5 100644 --- a/pkg/buildin/torrent.go +++ b/pkg/buildin/torrent.go @@ -17,7 +17,7 @@ import ( func NewDownloader(downloadDir string) (*Downloader, error) { cfg := torrent.NewDefaultClientConfig() cfg.DataDir = downloadDir - cfg.ListenPort = 51243 + //cfg.ListenPort = 51243 t, err := torrent.NewClient(cfg) if err != nil { return nil, errors.Wrapf(err, "create torrent client") diff --git a/server/server.go b/server/server.go index 7f20d86..9abf117 100644 --- a/server/server.go +++ b/server/server.go @@ -2,6 +2,7 @@ package server import ( "fmt" + "net" "net/http" "net/http/httputil" "net/url" @@ -11,6 +12,7 @@ import ( "polaris/pkg/cache" "polaris/pkg/tmdb" "polaris/ui" + "strconv" "time" ginzap "github.com/gin-contrib/zap" @@ -22,20 +24,20 @@ import ( ) func NewServer(db db.Database) *Server { - r := gin.Default() s := &Server{ - r: r, db: db, + srv: &http.Server{}, language: db.GetLanguage(), monitorNumCache: cache.NewCache[int, int](10 * time.Minute), downloadNumCache: cache.NewCache[int, int](10 * time.Minute), } s.core = engine.NewEngine(db, s.language) + s.setupRoutes() return s } type Server struct { - r *gin.Engine + srv *http.Server db db.Database core *engine.Engine language string @@ -44,20 +46,21 @@ type Server struct { downloadNumCache *cache.Cache[int, int] } -func (s *Server) Serve() error { +func (s *Server) setupRoutes() { s.core.Init() + r := gin.Default() s.jwtSerect = s.db.GetSetting(db.JwtSerectKey) //st, _ := fs.Sub(ui.Web, "build/web") - s.r.Use(static.Serve("/", static.EmbedFolder(ui.Web, "build/web"))) + r.Use(static.Serve("/", static.EmbedFolder(ui.Web, "build/web"))) //s.r.Use(ginzap.Ginzap(log.Logger().Desugar(), time.RFC3339, false)) - s.r.Use(ginzap.RecoveryWithZap(log.Logger().Desugar(), true)) + r.Use(ginzap.RecoveryWithZap(log.Logger().Desugar(), true)) log.SetLogLevel(s.db.GetSetting(db.SettingLogLevel)) //restore log level - s.r.POST("/api/login", HttpHandler(s.Login)) + r.POST("/api/login", HttpHandler(s.Login)) - api := s.r.Group("/api/v1") + api := r.Group("/api/v1") api.Use(s.authModdleware) api.StaticFS("/img", http.Dir(db.ImgPath)) api.StaticFS("/logs", http.Dir(db.LogPath)) @@ -142,9 +145,40 @@ func (s *Server) Serve() error { importlist.POST("/add", HttpHandler(s.addImportlist)) importlist.DELETE("/delete", HttpHandler(s.deleteImportList)) } - log.Infof("----------- Polaris Server Successfully Started ------------") + s.srv.Handler = r - return s.r.Run(":8080") +} + +func (s *Server) Start(addr string) (int, error) { + if addr == "" { + addr = "127.0.0.1:0" // 0 means any available port + } + ln, err := net.Listen("tcp", addr) + if err != nil { + return 0, fmt.Errorf("failed to listen on port: %w", err) + } + + _, port, _ := net.SplitHostPort(ln.Addr().String()) + + p, err := strconv.Atoi(port) + if err != nil { + return 0, fmt.Errorf("failed to convert port to int: %w", err) + } + go func() { + defer ln.Close() + if err := s.srv.Serve(ln); err != nil { + log.Errorf("failed to serve: %v", err) + } + }() + + log.Infof("----------- Polaris Server Successfully Started on Port %d------------", p) + + return p, nil +} + +func (s *Server) Stop() error { + log.Infof("Stopping Polaris Server...") + return s.srv.Close() } func (s *Server) TMDB() (*tmdb.Client, error) { diff --git a/ui/lib/ffi/entry/libpolaris_boot_browser.dart b/ui/lib/ffi/entry/libpolaris_boot_browser.dart new file mode 100644 index 0000000..23b92eb --- /dev/null +++ b/ui/lib/ffi/entry/libpolaris_boot_browser.dart @@ -0,0 +1,17 @@ +import 'package:ui/ffi/lib_polaris_boot.dart'; + +LibPolarisBoot create() { + return LibpolarisBootBrowser(); +} + +class LibpolarisBootBrowser implements LibPolarisBoot { + @override + Future start(String cfg) async{ + throw 0; + } + + @override + Future stop() async{ + } + +} \ No newline at end of file diff --git a/ui/lib/ffi/backend.dart b/ui/lib/ffi/entry/libpolaris_boot_native.dart similarity index 51% rename from ui/lib/ffi/backend.dart rename to ui/lib/ffi/entry/libpolaris_boot_native.dart index 468994f..b870ced 100644 --- a/ui/lib/ffi/backend.dart +++ b/ui/lib/ffi/entry/libpolaris_boot_native.dart @@ -1,11 +1,14 @@ import 'dart:ffi'; import 'dart:io'; -import 'dart:isolate'; +import 'package:ffi/ffi.dart'; import 'package:flutter/foundation.dart'; +import 'package:quiver/strings.dart'; +import 'package:ui/ffi/lib_polaris_boot.dart'; +LibPolarisBoot create() => LibpolarisBootNative(); -class FFIBackend { +class LibpolarisBootNative implements LibPolarisBoot { final lib = DynamicLibrary.open(libname()); static String libname() { @@ -25,17 +28,31 @@ class FFIBackend { } } - Future start() async { + @override + Future start(String cfg) async { var s = lib - .lookup>('Start') - .asFunction(); - - return Isolate.run(s); + .lookupFunction('Start') + ; + var r = s(cfg.toNativeUtf8()); + if (isNotBlank(r.r1.toDartString())) { + throw Exception(r.r1.toDartString()); + } + return r.r0; } + + @override Future stop() async { - var s = lib + var s = lib .lookup>('Stop') .asFunction(); return s(); } } + +typedef StartFunc = StartReturn Function(Pointer cfg); + +final class StartReturn extends Struct { + @Int32() + external int r0; + external Pointer r1; +} diff --git a/ui/lib/ffi/lib_polaris_boot.dart b/ui/lib/ffi/lib_polaris_boot.dart new file mode 100644 index 0000000..46a3cab --- /dev/null +++ b/ui/lib/ffi/lib_polaris_boot.dart @@ -0,0 +1,18 @@ +import 'package:ui/ffi/lib_polaris_boot_stub.dart' + if (dart.library.html) 'package:ui/ffi/entry/libpolaris_boot_browser.dart' + if (dart.library.io) 'package:ui/ffi/entry/libpolaris_boot_native.dart'; + +abstract class LibPolarisBoot { + static LibPolarisBoot? _instance; + + static LibPolarisBoot get instance { + _instance ??= LibPolarisBoot(); + return _instance!; + } + + factory LibPolarisBoot() => create(); + + Future start(String cfg); + + Future stop(); +} diff --git a/ui/lib/ffi/lib_polaris_boot_stub.dart b/ui/lib/ffi/lib_polaris_boot_stub.dart new file mode 100644 index 0000000..6d9f9f5 --- /dev/null +++ b/ui/lib/ffi/lib_polaris_boot_stub.dart @@ -0,0 +1,5 @@ + + +import 'package:ui/ffi/lib_polaris_boot.dart'; + +LibPolarisBoot create() => throw UnimplementedError(); diff --git a/ui/lib/main.dart b/ui/lib/main.dart index d392869..0576818 100644 --- a/ui/lib/main.dart +++ b/ui/lib/main.dart @@ -1,9 +1,11 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:ui/activity.dart'; +import 'package:ui/ffi/lib_polaris_boot.dart'; import 'package:ui/init_wizard.dart'; import 'package:ui/login_page.dart'; import 'package:ui/movie_watchlist.dart'; @@ -16,9 +18,10 @@ import 'package:ui/welcome_page.dart'; import 'package:ui/widgets/utils.dart'; void main() async { - // if (isDesktop()) { - // FFIBackend().start(); - // } + if (!kIsWeb) { + var port = await LibPolarisBoot.instance.start(""); + APIs.port = port; + } initializeDateFormatting() .then((_) => runApp(const ProviderScope(child: MyApp()))); diff --git a/ui/lib/providers/APIs.dart b/ui/lib/providers/APIs.dart index b5a7f26..f75cd91 100644 --- a/ui/lib/providers/APIs.dart +++ b/ui/lib/providers/APIs.dart @@ -8,6 +8,7 @@ import 'package:ui/providers/server_response.dart'; import 'package:ui/widgets/utils.dart'; class APIs { + static int port = 8096; static final _baseUrl = baseUrl(); static final searchUrl = "$_baseUrl/api/v1/media/search"; static final editMediaUrl = "$_baseUrl/api/v1/media/edit"; @@ -63,7 +64,6 @@ class APIs { static final blacklistUrl = "$_baseUrl/api/v1/activity/blacklist"; - static const tmdbApiKey = "tmdb_api_key"; static const downloadDirKey = "download_dir"; @@ -71,10 +71,11 @@ class APIs { GlobalKey(); static String baseUrl() { + if (!kIsWeb) { + return "http://127.0.0.1:$port"; + } + if (kReleaseMode) { - if (!kIsWeb) { - return "http://127.0.0.1:8080"; - } return ""; } return "http://127.0.0.1:8080";