image switch address

This commit is contained in:
niuhuan 2021-11-30 10:23:49 +08:00
parent caef949f32
commit c95771e637
13 changed files with 213 additions and 63 deletions

View File

@ -24,7 +24,8 @@ import (
func InitClient() { func InitClient() {
client.Timeout = time.Second * 60 client.Timeout = time.Second * 60
switchAddress, _ = properties.LoadSwitchAddress() switchAddress, _ = properties.LoadIntProperty("switchAddress", 1)
imageSwitchAddress, _ = properties.LoadIntProperty("imageSwitchAddress", 1)
proxy, _ := properties.LoadProxy() proxy, _ := properties.LoadProxy()
changeProxyUrl(proxy) changeProxyUrl(proxy)
} }
@ -36,15 +37,18 @@ var dialer = &net.Dialer{
} }
// SwitchAddress // SwitchAddress
// addr = "172.67.7.24:443" var switchAddresses = map[int]string{
// addr = "104.20.180.50:443" 1: "172.67.7.24:443",
// addr = "172.67.208.169:443" 2: "104.20.180.50:443",
var switchAddress = "" 3: "172.67.208.169:443",
var switchAddressPattern, _ = regexp.Compile("^.+picacomic\\.com:\\d+$") }
var switchAddress = 1
var switchAddressPattern, _ = regexp.Compile("^.+pica" + "comic\\.com:\\d+$")
func switchAddressContext(ctx context.Context, network, addr string) (net.Conn, error) { func switchAddressContext(ctx context.Context, network, addr string) (net.Conn, error) {
if switchAddressPattern.MatchString(addr) && switchAddress != "" { if sAddr, ok := switchAddresses[switchAddress]; ok {
addr = switchAddress addr = sAddr
} }
return dialer.DialContext(ctx, network, addr) return dialer.DialContext(ctx, network, addr)
} }

View File

@ -2,6 +2,7 @@ package controller
import ( import (
"bytes" "bytes"
"context"
"errors" "errors"
_ "golang.org/x/image/webp" _ "golang.org/x/image/webp"
"image" "image"
@ -9,20 +10,46 @@ import (
_ "image/jpeg" _ "image/jpeg"
_ "image/png" _ "image/png"
"io/ioutil" "io/ioutil"
"net"
"net/http" "net/http"
"pikapika/main/database/comic_center" "pikapika/main/database/comic_center"
"sync" "sync"
"time"
) )
var mutexCounter = -1 var mutexCounter = -1
var busMutex *sync.Mutex var busMutex *sync.Mutex
var subMutexes []*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() { func init() {
busMutex = &sync.Mutex{} busMutex = &sync.Mutex{}
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
subMutexes = append(subMutexes, &sync.Mutex{}) 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张图片 // takeMutex 下载图片获取一个锁, 这样只能同时下载5张图片
@ -52,6 +79,13 @@ func decodeFromFile(path string) ([]byte, image.Image, string, error) {
// 下载图片并decode // 下载图片并decode
func decodeFromUrl(fileServer string, path string) ([]byte, image.Image, string, error) { 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 := takeMutex()
m.Lock() m.Lock()
defer m.Unlock() defer m.Unlock()
@ -59,7 +93,7 @@ func decodeFromUrl(fileServer string, path string) ([]byte, image.Image, string,
if err != nil { if err != nil {
return nil, nil, "", err return nil, nil, "", err
} }
response, err := client.Do(request) response, err := useClient.Do(request)
if err != nil { if err != nil {
return nil, nil, "", err return nil, nil, "", err
} }

View File

@ -100,16 +100,37 @@ func loadDownloadThreadCount() int {
} }
func setSwitchAddress(nSwitchAddress string) error { func setSwitchAddress(nSwitchAddress string) error {
err := properties.SaveSwitchAddress(nSwitchAddress) num, err := strconv.Atoi(nSwitchAddress)
if err != nil { if err != nil {
return err return err
} }
switchAddress = nSwitchAddress err = properties.SaveIntProperty("switchAddress", num)
if err != nil {
return err
}
switchAddress = num
return nil return nil
} }
func getSwitchAddress() (string, error) { 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 { func setProxy(value string) error {
@ -534,6 +555,10 @@ func FlatInvoke(method string, params string) (string, error) {
return "", setSwitchAddress(params) return "", setSwitchAddress(params)
case "getSwitchAddress": case "getSwitchAddress":
return getSwitchAddress() return getSwitchAddress()
case "setImageSwitchAddress":
return "", setImageSwitchAddress(params)
case "getImageSwitchAddress":
return getImageSwitchAddress()
case "setProxy": case "setProxy":
return "", setProxy(params) return "", setProxy(params)
case "getProxy": case "getProxy":

View File

@ -2,6 +2,7 @@ package properties
import ( import (
"errors" "errors"
"fmt"
"gorm.io/driver/sqlite" "gorm.io/driver/sqlite"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"
@ -65,18 +66,22 @@ func LoadBoolProperty(name string, defaultValue bool) (bool, error) {
return strconv.ParseBool(stringValue) 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 { func SaveBoolProperty(name string, value bool) error {
return SaveProperty(name, strconv.FormatBool(value)) 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 { func SaveProxy(value string) error {
return SaveProperty("proxy", value) return SaveProperty("proxy", value)
} }

View File

@ -50,6 +50,16 @@ class Method {
return await _flatInvoke("setSwitchAddress", switchAddress); return await _flatInvoke("setSwitchAddress", switchAddress);
} }
///
Future<String> getImageSwitchAddress() async {
return await _flatInvoke("getImageSwitchAddress", "");
}
///
Future<dynamic> setImageSwitchAddress(String switchAddress) async {
return await _flatInvoke("setImageSwitchAddress", switchAddress);
}
/// ///
Future<String> getProxy() async { Future<String> getProxy() async {
return await _flatInvoke("getProxy", ""); return await _flatInvoke("getProxy", "");

View File

@ -9,10 +9,10 @@ import 'package:flutter/material.dart';
import '../Method.dart'; import '../Method.dart';
var _addresses = { var _addresses = {
"不分流": "", "0": "不分流",
"分流1": "172.67.7.24:443", "1": "分流1 (推荐)",
"分流2": "104.20.180.50:443", "2": "分流2",
"分流3": "72.67.208.169:443", "3": "分流3",
}; };
late String _currentAddress; late String _currentAddress;
@ -21,16 +21,11 @@ Future<void> initAddress() async {
_currentAddress = await method.getSwitchAddress(); _currentAddress = await method.getSwitchAddress();
} }
String currentAddressName() { String _currentAddressName() {
for (var value in _addresses.entries) { return _addresses[_currentAddress] ?? "";
if (value.value == _currentAddress) {
return value.key;
}
}
return "";
} }
Future<void> chooseAddress(BuildContext context) async { Future<void> _chooseAddress(BuildContext context) async {
String? choose = await showDialog<String>( String? choose = await showDialog<String>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
@ -39,9 +34,9 @@ Future<void> chooseAddress(BuildContext context) async {
children: <Widget>[ children: <Widget>[
..._addresses.entries.map( ..._addresses.entries.map(
(e) => SimpleDialogOption( (e) => SimpleDialogOption(
child: Text(e.key), child: Text(e.value),
onPressed: () { onPressed: () {
Navigator.of(context).pop(e.value); Navigator.of(context).pop(e.key);
}, },
), ),
), ),
@ -54,3 +49,18 @@ Future<void> chooseAddress(BuildContext context) async {
_currentAddress = choose; _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(() {});
},
);
},
);
}

View File

@ -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<void> initImageAddress() async {
_currentImageAddress = await method.getImageSwitchAddress();
}
String _currentImageAddressName() {
return _imageAddresses[_currentImageAddress] ?? "";
}
Future<void> _chooseImageAddress(BuildContext context) async {
String? choose = await showDialog<String>(
context: context,
builder: (BuildContext context) {
return SimpleDialog(
title: Text('选择图片分流'),
children: <Widget>[
..._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(() {});
},
);
},
);
}

View File

@ -28,3 +28,18 @@ Future<dynamic> inputProxy(BuildContext context) async {
_currentProxy = input; _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(() {});
},
);
},
);
}

View File

@ -6,17 +6,17 @@ import 'package:pikapika/basic/Navigatior.dart';
import 'basic/config/Themes.dart'; import 'basic/config/Themes.dart';
void main() { void main() {
runApp(PikapiApp()); runApp(PikapikaApp());
} }
class PikapiApp extends StatefulWidget { class PikapikaApp extends StatefulWidget {
const PikapiApp({Key? key}) : super(key: key); const PikapikaApp({Key? key}) : super(key: key);
@override @override
State<StatefulWidget> createState() => _PikapiAppState(); State<StatefulWidget> createState() => _PikapikaAppState();
} }
class _PikapiAppState extends State<PikapiApp> { class _PikapikaAppState extends State<PikapikaApp> {
@override @override
void initState() { void initState() {
themeEvent.subscribe(_onChangeTheme); themeEvent.subscribe(_onChangeTheme);

View File

@ -11,6 +11,7 @@ import 'package:pikapika/basic/config/DownloadAndExportPath.dart';
import 'package:pikapika/basic/config/DownloadThreadCount.dart'; import 'package:pikapika/basic/config/DownloadThreadCount.dart';
import 'package:pikapika/basic/config/FullScreenAction.dart'; import 'package:pikapika/basic/config/FullScreenAction.dart';
import 'package:pikapika/basic/config/FullScreenUI.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/KeyboardController.dart';
import 'package:pikapika/basic/config/NoAnimation.dart'; import 'package:pikapika/basic/config/NoAnimation.dart';
import 'package:pikapika/basic/config/PagerAction.dart'; import 'package:pikapika/basic/config/PagerAction.dart';
@ -49,6 +50,7 @@ class _InitScreenState extends State<InitScreen> {
await initPlatform(); // , await initPlatform(); // ,
await initAutoClean(); await initAutoClean();
await initAddress(); await initAddress();
await initImageAddress();
await initProxy(); await initProxy();
await initQuality(); await initQuality();
await initFont(); await initFont();

View File

@ -220,7 +220,7 @@ class _RemoteImageState extends State<RemoteImage> {
@override @override
void initState() { void initState() {
_mock = widget.fileServer == "" || widget.fileServer.contains(".xyz/"); _mock = widget.fileServer == "";
if (!_mock) { if (!_mock) {
_future = method _future = method
.remoteImageData(widget.fileServer, widget.path) .remoteImageData(widget.fileServer, widget.path)

View File

@ -1,35 +1,18 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:pikapika/basic/config/Address.dart'; import 'package:pikapika/basic/config/Address.dart';
import 'package:pikapika/basic/config/ImageAddress.dart';
import 'package:pikapika/basic/config/Proxy.dart'; import 'package:pikapika/basic/config/Proxy.dart';
// //
class NetworkSetting extends StatefulWidget { class NetworkSetting extends StatelessWidget {
@override
State<StatefulWidget> createState() => _NetworkSettingState();
}
class _NetworkSettingState extends State<NetworkSetting> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
child: Column( child: Column(
children: [ children: [
ListTile( switchAddressSetting(),
title: Text("分流"), imageSwitchAddressSetting(),
subtitle: Text(currentAddressName()), proxySetting(),
onTap: () async {
await chooseAddress(context);
setState(() {});
},
),
ListTile(
title: Text("代理服务器"),
subtitle: Text(currentProxyName()),
onTap: () async {
await inputProxy(context);
setState(() {});
},
),
], ],
), ),
); );

View File

@ -13,7 +13,7 @@ import 'package:pikapika/main.dart';
void main() { void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async { testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame. // Build our app and trigger a frame.
await tester.pumpWidget(PikapiApp()); await tester.pumpWidget(PikapikaApp());
// Verify that our counter starts at 0. // Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget); expect(find.text('0'), findsOneWidget);