mirror of
https://github.com/simon-ding/polaris.git
synced 2026-04-20 18:57:31 +08:00
feat: windows desktop app complete
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
24
cmd/doc.go
24
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,26 @@
|
||||
package main
|
||||
|
||||
import "polaris/cmd"
|
||||
import (
|
||||
"os"
|
||||
"polaris/db"
|
||||
"polaris/log"
|
||||
"polaris/server"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd.Start(false)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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) {
|
||||
|
||||
17
ui/lib/ffi/entry/libpolaris_boot_browser.dart
Normal file
17
ui/lib/ffi/entry/libpolaris_boot_browser.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
import 'package:ui/ffi/lib_polaris_boot.dart';
|
||||
|
||||
LibPolarisBoot create() {
|
||||
return LibpolarisBootBrowser();
|
||||
}
|
||||
|
||||
class LibpolarisBootBrowser implements LibPolarisBoot {
|
||||
@override
|
||||
Future<int> start(String cfg) async{
|
||||
throw 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stop() async{
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<void> start() async {
|
||||
@override
|
||||
Future<int> start(String cfg) async {
|
||||
var s = lib
|
||||
.lookup<NativeFunction<Void Function()>>('Start')
|
||||
.asFunction<void Function()>();
|
||||
|
||||
return Isolate.run(s);
|
||||
.lookupFunction<StartFunc, StartFunc>('Start')
|
||||
;
|
||||
var r = s(cfg.toNativeUtf8());
|
||||
if (isNotBlank(r.r1.toDartString())) {
|
||||
throw Exception(r.r1.toDartString());
|
||||
}
|
||||
return r.r0;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stop() async {
|
||||
var s = lib
|
||||
var s = lib
|
||||
.lookup<NativeFunction<Void Function()>>('Stop')
|
||||
.asFunction<void Function()>();
|
||||
return s();
|
||||
}
|
||||
}
|
||||
|
||||
typedef StartFunc = StartReturn Function(Pointer<Utf8> cfg);
|
||||
|
||||
final class StartReturn extends Struct {
|
||||
@Int32()
|
||||
external int r0;
|
||||
external Pointer<Utf8> r1;
|
||||
}
|
||||
18
ui/lib/ffi/lib_polaris_boot.dart
Normal file
18
ui/lib/ffi/lib_polaris_boot.dart
Normal file
@@ -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<int> start(String cfg);
|
||||
|
||||
Future<void> stop();
|
||||
}
|
||||
5
ui/lib/ffi/lib_polaris_boot_stub.dart
Normal file
5
ui/lib/ffi/lib_polaris_boot_stub.dart
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
import 'package:ui/ffi/lib_polaris_boot.dart';
|
||||
|
||||
LibPolarisBoot create() => throw UnimplementedError();
|
||||
@@ -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())));
|
||||
|
||||
@@ -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<NavigatorState>();
|
||||
|
||||
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";
|
||||
|
||||
Reference in New Issue
Block a user