diff --git a/go/main/controller/client.go b/go/main/controller/client.go index 3e21d7b..0c3148f 100644 --- a/go/main/controller/client.go +++ b/go/main/controller/client.go @@ -24,7 +24,8 @@ import ( func InitClient() { client.Timeout = time.Second * 60 - switchAddress, _ = properties.LoadSwitchAddress() + switchAddress, _ = properties.LoadIntProperty("switchAddress", 1) + imageSwitchAddress, _ = properties.LoadIntProperty("imageSwitchAddress", 1) proxy, _ := properties.LoadProxy() changeProxyUrl(proxy) } @@ -36,15 +37,18 @@ var dialer = &net.Dialer{ } // SwitchAddress -// addr = "172.67.7.24:443" -// addr = "104.20.180.50:443" -// addr = "172.67.208.169:443" -var switchAddress = "" -var switchAddressPattern, _ = regexp.Compile("^.+picacomic\\.com:\\d+$") +var switchAddresses = map[int]string{ + 1: "172.67.7.24:443", + 2: "104.20.180.50:443", + 3: "172.67.208.169:443", +} + +var switchAddress = 1 +var switchAddressPattern, _ = regexp.Compile("^.+pica" + "comic\\.com:\\d+$") func switchAddressContext(ctx context.Context, network, addr string) (net.Conn, error) { - if switchAddressPattern.MatchString(addr) && switchAddress != "" { - addr = switchAddress + if sAddr, ok := switchAddresses[switchAddress]; ok { + addr = sAddr } return dialer.DialContext(ctx, network, addr) } diff --git a/go/main/controller/image.go b/go/main/controller/image.go index 981ad23..0efbc58 100644 --- a/go/main/controller/image.go +++ b/go/main/controller/image.go @@ -2,6 +2,7 @@ package controller import ( "bytes" + "context" "errors" _ "golang.org/x/image/webp" "image" @@ -9,20 +10,46 @@ import ( _ "image/jpeg" _ "image/png" "io/ioutil" + "net" "net/http" "pikapika/main/database/comic_center" "sync" + "time" ) var mutexCounter = -1 var busMutex *sync.Mutex var subMutexes []*sync.Mutex +var imageHttpClient *http.Client + +// imageSwitchAddress +// 图片的分流直接使用 switchAddressPattern 可以正常使用 +// 通过ping发现图片的分流地址与ip一致 +// 这里为了域名与官方一致改为域名分流 +var imageSwitchAddresses = map[int]string{ + 1: "https://storage.wika" + "wika.xyz", + 2: "https://s2.pica" + "comic.com", + 3: "https://s3.pica" + "comic.com", +} + +var imageSwitchAddress int func init() { busMutex = &sync.Mutex{} for i := 0; i < 5; i++ { subMutexes = append(subMutexes, &sync.Mutex{}) } + imageHttpClient = &http.Client{ + Transport: &http.Transport{ + TLSHandshakeTimeout: time.Second * 10, + ExpectContinueTimeout: time.Second * 10, + ResponseHeaderTimeout: time.Second * 10, + IdleConnTimeout: time.Second * 10, + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + return dialer.DialContext(ctx, network, addr) + }, + }, + } } // takeMutex 下载图片获取一个锁, 这样只能同时下载5张图片 @@ -52,6 +79,13 @@ func decodeFromFile(path string) ([]byte, image.Image, string, error) { // 下载图片并decode func decodeFromUrl(fileServer string, path string) ([]byte, image.Image, string, error) { + useClient := imageHttpClient + if imageSwitchAddress == -1 { + useClient = &client.Client + } + if server, ok := imageSwitchAddresses[imageSwitchAddress]; ok { + fileServer = server + } m := takeMutex() m.Lock() defer m.Unlock() @@ -59,7 +93,7 @@ func decodeFromUrl(fileServer string, path string) ([]byte, image.Image, string, if err != nil { return nil, nil, "", err } - response, err := client.Do(request) + response, err := useClient.Do(request) if err != nil { return nil, nil, "", err } diff --git a/go/main/controller/pikapika.go b/go/main/controller/pikapika.go index 1089048..e619b7c 100644 --- a/go/main/controller/pikapika.go +++ b/go/main/controller/pikapika.go @@ -100,16 +100,37 @@ func loadDownloadThreadCount() int { } func setSwitchAddress(nSwitchAddress string) error { - err := properties.SaveSwitchAddress(nSwitchAddress) + num, err := strconv.Atoi(nSwitchAddress) if err != nil { return err } - switchAddress = nSwitchAddress + err = properties.SaveIntProperty("switchAddress", num) + if err != nil { + return err + } + switchAddress = num return nil } func getSwitchAddress() (string, error) { - return switchAddress, nil + return strconv.Itoa(switchAddress), nil +} + +func setImageSwitchAddress(nSwitchAddress string) error { + num, err := strconv.Atoi(nSwitchAddress) + if err != nil { + return err + } + err = properties.SaveIntProperty("imageSwitchAddress", num) + if err != nil { + return err + } + switchAddress = num + return nil +} + +func getImageSwitchAddress() (string, error) { + return strconv.Itoa(imageSwitchAddress), nil } func setProxy(value string) error { @@ -534,6 +555,10 @@ func FlatInvoke(method string, params string) (string, error) { return "", setSwitchAddress(params) case "getSwitchAddress": return getSwitchAddress() + case "setImageSwitchAddress": + return "", setImageSwitchAddress(params) + case "getImageSwitchAddress": + return getImageSwitchAddress() case "setProxy": return "", setProxy(params) case "getProxy": diff --git a/go/main/database/properties/properties.go b/go/main/database/properties/properties.go index 33868a9..c280c96 100644 --- a/go/main/database/properties/properties.go +++ b/go/main/database/properties/properties.go @@ -2,6 +2,7 @@ package properties import ( "errors" + "fmt" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/clause" @@ -65,18 +66,22 @@ func LoadBoolProperty(name string, defaultValue bool) (bool, error) { return strconv.ParseBool(stringValue) } +func LoadIntProperty(name string, defaultValue int) (int, error) { + str, err := LoadProperty(name, fmt.Sprintf("%d", defaultValue)) + if err != nil { + return 0, err + } + return strconv.Atoi(str) +} + +func SaveIntProperty(name string, value int) error { + return SaveProperty(name, strconv.Itoa(value)) +} + func SaveBoolProperty(name string, value bool) error { return SaveProperty(name, strconv.FormatBool(value)) } -func SaveSwitchAddress(value string) error { - return SaveProperty("switch_address", value) -} - -func LoadSwitchAddress() (string, error) { - return LoadProperty("switch_address", "") -} - func SaveProxy(value string) error { return SaveProperty("proxy", value) } diff --git a/lib/basic/Method.dart b/lib/basic/Method.dart index 26f835b..8f430b1 100644 --- a/lib/basic/Method.dart +++ b/lib/basic/Method.dart @@ -50,6 +50,16 @@ class Method { return await _flatInvoke("setSwitchAddress", switchAddress); } + /// 获取当前的图片分流 + Future getImageSwitchAddress() async { + return await _flatInvoke("getImageSwitchAddress", ""); + } + + /// 更换图片分流 + Future setImageSwitchAddress(String switchAddress) async { + return await _flatInvoke("setImageSwitchAddress", switchAddress); + } + /// 获取代理 Future getProxy() async { return await _flatInvoke("getProxy", ""); diff --git a/lib/basic/config/Address.dart b/lib/basic/config/Address.dart index ed61fea..6206c3f 100644 --- a/lib/basic/config/Address.dart +++ b/lib/basic/config/Address.dart @@ -9,10 +9,10 @@ import 'package:flutter/material.dart'; import '../Method.dart'; var _addresses = { - "不分流": "", - "分流1": "172.67.7.24:443", - "分流2": "104.20.180.50:443", - "分流3": "72.67.208.169:443", + "0": "不分流", + "1": "分流1 (推荐)", + "2": "分流2", + "3": "分流3", }; late String _currentAddress; @@ -21,16 +21,11 @@ Future initAddress() async { _currentAddress = await method.getSwitchAddress(); } -String currentAddressName() { - for (var value in _addresses.entries) { - if (value.value == _currentAddress) { - return value.key; - } - } - return ""; +String _currentAddressName() { + return _addresses[_currentAddress] ?? ""; } -Future chooseAddress(BuildContext context) async { +Future _chooseAddress(BuildContext context) async { String? choose = await showDialog( context: context, builder: (BuildContext context) { @@ -39,9 +34,9 @@ Future chooseAddress(BuildContext context) async { children: [ ..._addresses.entries.map( (e) => SimpleDialogOption( - child: Text(e.key), + child: Text(e.value), onPressed: () { - Navigator.of(context).pop(e.value); + Navigator.of(context).pop(e.key); }, ), ), @@ -54,3 +49,18 @@ Future chooseAddress(BuildContext context) async { _currentAddress = choose; } } + +Widget switchAddressSetting() { + return StatefulBuilder( + builder: (BuildContext context, void Function(void Function()) setState) { + return ListTile( + title: Text("分流"), + subtitle: Text(_currentAddressName()), + onTap: () async { + await _chooseAddress(context); + setState(() {}); + }, + ); + }, + ); +} diff --git a/lib/basic/config/ImageAddress.dart b/lib/basic/config/ImageAddress.dart new file mode 100644 index 0000000..522d7c3 --- /dev/null +++ b/lib/basic/config/ImageAddress.dart @@ -0,0 +1,62 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import '../Method.dart'; + +var _imageAddresses = { + "-1": "跟随api分流", + "0": "不分流", + "1": "分流1 (推荐)", + "2": "分流2", + "3": "分流3", +}; + +late String _currentImageAddress; + +Future initImageAddress() async { + _currentImageAddress = await method.getImageSwitchAddress(); +} + +String _currentImageAddressName() { + return _imageAddresses[_currentImageAddress] ?? ""; +} + +Future _chooseImageAddress(BuildContext context) async { + String? choose = await showDialog( + context: context, + builder: (BuildContext context) { + return SimpleDialog( + title: Text('选择图片分流'), + children: [ + ..._imageAddresses.entries.map( + (e) => SimpleDialogOption( + child: Text(e.value), + onPressed: () { + Navigator.of(context).pop(e.key); + }, + ), + ), + ], + ); + }, + ); + if (choose != null) { + await method.setImageSwitchAddress(choose); + _currentImageAddress = choose; + } +} + +Widget imageSwitchAddressSetting() { + return StatefulBuilder( + builder: (BuildContext context, void Function(void Function()) setState) { + return ListTile( + title: Text("图片"), + subtitle: Text(_currentImageAddressName()), + onTap: () async { + await _chooseImageAddress(context); + setState(() {}); + }, + ); + }, + ); +} diff --git a/lib/basic/config/Proxy.dart b/lib/basic/config/Proxy.dart index d765955..4620867 100644 --- a/lib/basic/config/Proxy.dart +++ b/lib/basic/config/Proxy.dart @@ -28,3 +28,18 @@ Future inputProxy(BuildContext context) async { _currentProxy = input; } } + +Widget proxySetting() { + return StatefulBuilder( + builder: (BuildContext context, void Function(void Function()) setState) { + return ListTile( + title: Text("代理服务器"), + subtitle: Text(currentProxyName()), + onTap: () async { + await inputProxy(context); + setState(() {}); + }, + ); + }, + ); +} diff --git a/lib/main.dart b/lib/main.dart index d0dea01..9266095 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,17 +6,17 @@ import 'package:pikapika/basic/Navigatior.dart'; import 'basic/config/Themes.dart'; void main() { - runApp(PikapiApp()); + runApp(PikapikaApp()); } -class PikapiApp extends StatefulWidget { - const PikapiApp({Key? key}) : super(key: key); +class PikapikaApp extends StatefulWidget { + const PikapikaApp({Key? key}) : super(key: key); @override - State createState() => _PikapiAppState(); + State createState() => _PikapikaAppState(); } -class _PikapiAppState extends State { +class _PikapikaAppState extends State { @override void initState() { themeEvent.subscribe(_onChangeTheme); diff --git a/lib/screens/InitScreen.dart b/lib/screens/InitScreen.dart index 1338802..4b7897b 100644 --- a/lib/screens/InitScreen.dart +++ b/lib/screens/InitScreen.dart @@ -11,6 +11,7 @@ import 'package:pikapika/basic/config/DownloadAndExportPath.dart'; import 'package:pikapika/basic/config/DownloadThreadCount.dart'; import 'package:pikapika/basic/config/FullScreenAction.dart'; import 'package:pikapika/basic/config/FullScreenUI.dart'; +import 'package:pikapika/basic/config/ImageAddress.dart'; import 'package:pikapika/basic/config/KeyboardController.dart'; import 'package:pikapika/basic/config/NoAnimation.dart'; import 'package:pikapika/basic/config/PagerAction.dart'; @@ -49,6 +50,7 @@ class _InitScreenState extends State { await initPlatform(); // 必须第一个初始化, 加载设备信息 await initAutoClean(); await initAddress(); + await initImageAddress(); await initProxy(); await initQuality(); await initFont(); diff --git a/lib/screens/components/Images.dart b/lib/screens/components/Images.dart index 6d22734..ffe7ba9 100644 --- a/lib/screens/components/Images.dart +++ b/lib/screens/components/Images.dart @@ -220,7 +220,7 @@ class _RemoteImageState extends State { @override void initState() { - _mock = widget.fileServer == "" || widget.fileServer.contains(".xyz/"); + _mock = widget.fileServer == ""; if (!_mock) { _future = method .remoteImageData(widget.fileServer, widget.path) diff --git a/lib/screens/components/NetworkSetting.dart b/lib/screens/components/NetworkSetting.dart index 6451118..df4f19c 100644 --- a/lib/screens/components/NetworkSetting.dart +++ b/lib/screens/components/NetworkSetting.dart @@ -1,35 +1,18 @@ import 'package:flutter/material.dart'; import 'package:pikapika/basic/config/Address.dart'; +import 'package:pikapika/basic/config/ImageAddress.dart'; import 'package:pikapika/basic/config/Proxy.dart'; // 网络设置 -class NetworkSetting extends StatefulWidget { - @override - State createState() => _NetworkSettingState(); -} - -class _NetworkSettingState extends State { +class NetworkSetting extends StatelessWidget { @override Widget build(BuildContext context) { return Container( child: Column( children: [ - ListTile( - title: Text("分流"), - subtitle: Text(currentAddressName()), - onTap: () async { - await chooseAddress(context); - setState(() {}); - }, - ), - ListTile( - title: Text("代理服务器"), - subtitle: Text(currentProxyName()), - onTap: () async { - await inputProxy(context); - setState(() {}); - }, - ), + switchAddressSetting(), + imageSwitchAddressSetting(), + proxySetting(), ], ), ); diff --git a/test/widget_test.dart b/test/widget_test.dart index a5f1567..d672e3c 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -13,7 +13,7 @@ import 'package:pikapika/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(PikapiApp()); + await tester.pumpWidget(PikapikaApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget);