Comic info screen's "Recommendation"
This commit is contained in:
parent
9d66481a16
commit
2dcd37c26c
|
@ -28,11 +28,18 @@ jobs:
|
||||||
with:
|
with:
|
||||||
repository: ${{ github.event.inputs.repo }}
|
repository: ${{ github.event.inputs.repo }}
|
||||||
ref: 'master'
|
ref: 'master'
|
||||||
- name: Checkout submodules
|
|
||||||
run: git submodule update --init --recursive
|
|
||||||
- uses: actions/setup-go@v2
|
- uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.go_version }}
|
go-version: ${{ env.go_version }}
|
||||||
|
- name: Cache go modules
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cache/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
- name: Check release
|
- name: Check release
|
||||||
run: |
|
run: |
|
||||||
cd ci
|
cd ci
|
||||||
|
@ -75,6 +82,39 @@ jobs:
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.go_version }}
|
go-version: ${{ env.go_version }}
|
||||||
|
|
||||||
|
- name: Cache go modules (Linux/Android)
|
||||||
|
if: matrix.config.target == 'android-arm32' || matrix.config.target == 'android-arm64' || matrix.config.target == 'android-x86_64' || matrix.config.target == 'linux'
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cache/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
|
- name: Cache go modules (macOS/ios)
|
||||||
|
if: matrix.config.target == 'macos' || matrix.config.target == 'ios'
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/Library/Caches/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
|
- name: Cache go modules (Windows)
|
||||||
|
if: matrix.config.target == 'windows'
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~\AppData\Local\go-build
|
||||||
|
~\go\pkg\mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
- id: check_asset
|
- id: check_asset
|
||||||
name: Check asset
|
name: Check asset
|
||||||
run: |
|
run: |
|
||||||
|
@ -88,6 +128,20 @@ jobs:
|
||||||
channel: ${{ env.flutter_channel }}
|
channel: ${{ env.flutter_channel }}
|
||||||
flutter-version: ${{ env.flutter_version }}
|
flutter-version: ${{ env.flutter_version }}
|
||||||
|
|
||||||
|
- name: Check core
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: 'niuhuan/pikapika-go-core'
|
||||||
|
token: ${{ secrets.GH_TOKEN }}
|
||||||
|
path: 'go'
|
||||||
|
|
||||||
|
- name: Cache Flutter dependencies (Linux/Android)
|
||||||
|
if: steps.check_asset.outputs.skip_build != 'true' && ( matrix.config.target == 'android-arm32' || matrix.config.target == 'android-arm64' || matrix.config.target == 'android-x86_64' || matrix.config.target == 'linux' )
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: /opt/hostedtoolcache/flutter
|
||||||
|
key: ${{ runner.os }}-flutter
|
||||||
|
|
||||||
- name: Setup java (Android)
|
- name: Setup java (Android)
|
||||||
if: steps.check_asset.outputs.skip_build != 'true' && ( matrix.config.target == 'android-arm32' || matrix.config.target == 'android-arm64' || matrix.config.target == 'android-x86_64' )
|
if: steps.check_asset.outputs.skip_build != 'true' && ( matrix.config.target == 'android-arm32' || matrix.config.target == 'android-arm64' || matrix.config.target == 'android-x86_64' )
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
build
|
|
||||||
.last_goflutter_check
|
|
||||||
.last_go-flutter_check
|
|
||||||
.last_go-flutter_check
|
|
Binary file not shown.
Before Width: | Height: | Size: 33 KiB |
|
@ -1,58 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path"
|
|
||||||
path2 "path"
|
|
||||||
"path/filepath"
|
|
||||||
"pikapika/pikapika/config"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 根据不通系统初始化数据的保存路径
|
|
||||||
func init() {
|
|
||||||
applicationDir, err := os.UserHomeDir()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "windows":
|
|
||||||
// applicationDir = path.Join(applicationDir, "AppData", "Roaming")
|
|
||||||
file, err := exec.LookPath(os.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
path, err := filepath.Abs(file)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
i := strings.LastIndex(path, "/")
|
|
||||||
if i < 0 {
|
|
||||||
i = strings.LastIndex(path, "\\")
|
|
||||||
}
|
|
||||||
if i < 0 {
|
|
||||||
panic(errors.New(" can't find \"/\" or \"\\\""))
|
|
||||||
}
|
|
||||||
applicationDir = path2.Join(path[0:i+1], "data")
|
|
||||||
case "darwin":
|
|
||||||
applicationDir = path.Join(applicationDir, "Library", "Application Support", "pikapika")
|
|
||||||
case "linux":
|
|
||||||
applicationDir = path.Join(applicationDir, ".pikapika")
|
|
||||||
default:
|
|
||||||
panic(errors.New("not supported system"))
|
|
||||||
}
|
|
||||||
if _, err = os.Stat(applicationDir); err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
err = os.MkdirAll(applicationDir, os.FileMode(0700))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config.InitApplication(applicationDir)
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/go-flutter-desktop/go-flutter"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"image"
|
|
||||||
_ "image/gif"
|
|
||||||
_ "image/jpeg"
|
|
||||||
_ "image/png"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"pikapika/pikapika/database/properties"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// vmArguments may be set by hover at compile-time
|
|
||||||
var vmArguments string
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// DO NOT EDIT, add options in options.go
|
|
||||||
mainOptions := []flutter.Option{
|
|
||||||
flutter.OptionVMArguments(strings.Split(vmArguments, ";")),
|
|
||||||
flutter.WindowIcon(iconProvider),
|
|
||||||
}
|
|
||||||
// 窗口初始化大小的处理
|
|
||||||
widthStr, _ := properties.LoadProperty("window_width", "600")
|
|
||||||
heightStr, _ := properties.LoadProperty("window_height", "900")
|
|
||||||
width, _ := strconv.Atoi(widthStr)
|
|
||||||
height, _ := strconv.Atoi(heightStr)
|
|
||||||
if width <= 0 {
|
|
||||||
width = 600
|
|
||||||
}
|
|
||||||
if height <= 0 {
|
|
||||||
height = 900
|
|
||||||
}
|
|
||||||
var runOptions []flutter.Option
|
|
||||||
runOptions = append(runOptions, flutter.WindowInitialDimensions(width, height))
|
|
||||||
fullScreen, _ := properties.LoadBoolProperty("full_screen", false)
|
|
||||||
if fullScreen {
|
|
||||||
runOptions = append(runOptions, flutter.WindowMode(flutter.WindowModeMaximize))
|
|
||||||
}
|
|
||||||
// ------
|
|
||||||
err := flutter.Run(append(append(runOptions, options...), mainOptions...)...)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func iconProvider() ([]image.Image, error) {
|
|
||||||
execPath, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to resolve executable path")
|
|
||||||
}
|
|
||||||
execPath, err = filepath.EvalSymlinks(execPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to eval symlinks for executable path")
|
|
||||||
}
|
|
||||||
imgFile, err := os.Open(filepath.Join(filepath.Dir(execPath), "assets", "icon.png"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to open assets/icon.png")
|
|
||||||
}
|
|
||||||
img, _, err := image.Decode(imgFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to decode image")
|
|
||||||
}
|
|
||||||
return []image.Image{img}, nil
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"github.com/go-flutter-desktop/go-flutter"
|
|
||||||
"github.com/go-flutter-desktop/go-flutter/plugin"
|
|
||||||
"github.com/go-flutter-desktop/plugins/url_launcher"
|
|
||||||
"github.com/go-gl/glfw/v3.3/glfw"
|
|
||||||
"github.com/miguelpruivo/flutter_file_picker/go"
|
|
||||||
"pikapika/pikapika"
|
|
||||||
"pikapika/pikapika/database/properties"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var options = []flutter.Option{
|
|
||||||
flutter.AddPlugin(&PikapikaPlugin{}),
|
|
||||||
flutter.AddPlugin(&file_picker.FilePickerPlugin{}),
|
|
||||||
flutter.AddPlugin(&url_launcher.UrlLauncherPlugin{}),
|
|
||||||
}
|
|
||||||
|
|
||||||
var eventMutex = sync.Mutex{}
|
|
||||||
var eventSink *plugin.EventSink
|
|
||||||
|
|
||||||
type EventHandler struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *EventHandler) OnListen(arguments interface{}, sink *plugin.EventSink) {
|
|
||||||
eventMutex.Lock()
|
|
||||||
defer eventMutex.Unlock()
|
|
||||||
eventSink = sink
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *EventHandler) OnCancel(arguments interface{}) {
|
|
||||||
eventMutex.Lock()
|
|
||||||
defer eventMutex.Unlock()
|
|
||||||
eventSink = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const channelName = "method"
|
|
||||||
|
|
||||||
type PikapikaPlugin struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PikapikaPlugin) InitPlugin(messenger plugin.BinaryMessenger) error {
|
|
||||||
|
|
||||||
channel := plugin.NewMethodChannel(messenger, channelName, plugin.StandardMethodCodec{})
|
|
||||||
|
|
||||||
channel.HandleFunc("flatInvoke", func(arguments interface{}) (interface{}, error) {
|
|
||||||
if argumentsMap, ok := arguments.(map[interface{}]interface{}); ok {
|
|
||||||
if method, ok := argumentsMap["method"].(string); ok {
|
|
||||||
if params, ok := argumentsMap["params"].(string); ok {
|
|
||||||
return pikapika.FlatInvoke(method, params)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, errors.New("params error")
|
|
||||||
})
|
|
||||||
|
|
||||||
exporting := plugin.NewEventChannel(messenger, "flatEvent", plugin.StandardMethodCodec{})
|
|
||||||
exporting.Handle(&EventHandler{})
|
|
||||||
|
|
||||||
pikapika.EventNotify = func(message string) {
|
|
||||||
eventMutex.Lock()
|
|
||||||
defer eventMutex.Unlock()
|
|
||||||
sink := eventSink
|
|
||||||
if sink != nil {
|
|
||||||
sink.Success(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil // no error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PikapikaPlugin) InitPluginGLFW(window *glfw.Window) error {
|
|
||||||
window.SetSizeCallback(func(w *glfw.Window, width int, height int) {
|
|
||||||
go func() {
|
|
||||||
properties.SaveProperty("window_width", strconv.Itoa(width))
|
|
||||||
properties.SaveProperty("window_height", strconv.Itoa(height))
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
window.SetMaximizeCallback(func(w *glfw.Window, iconified bool) {
|
|
||||||
go func() {
|
|
||||||
properties.SaveProperty("full_screen", strconv.FormatBool(iconified))
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
21
go/go.mod
21
go/go.mod
|
@ -1,21 +0,0 @@
|
||||||
module pikapika
|
|
||||||
|
|
||||||
go 1.16
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/PuerkitoBio/goquery v1.7.1
|
|
||||||
github.com/go-flutter-desktop/go-flutter v0.43.0
|
|
||||||
github.com/go-flutter-desktop/plugins/url_launcher v0.1.2
|
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20201108214237-06ea97f0c265
|
|
||||||
github.com/miguelpruivo/flutter_file_picker/go v0.0.0-20210622152105-9f0a811028a0
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
|
||||||
github.com/niuhuan/pica-go v0.0.0-20220415110443-b5c4a97b0103
|
|
||||||
github.com/pkg/errors v0.9.1
|
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b
|
|
||||||
golang.org/x/mobile v0.0.0-20220414153400-ce6a79cf6a13 // indirect
|
|
||||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
|
|
||||||
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7 // indirect
|
|
||||||
golang.org/x/text v0.3.7 // indirect
|
|
||||||
gorm.io/driver/sqlite v1.1.4
|
|
||||||
gorm.io/gorm v1.21.12
|
|
||||||
)
|
|
127
go/go.sum
127
go/go.sum
|
@ -1,127 +0,0 @@
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
|
||||||
github.com/PuerkitoBio/goquery v1.7.1 h1:oE+T06D+1T7LNrn91B4aERsRIeCLJ/oPSa6xB9FPnz4=
|
|
||||||
github.com/PuerkitoBio/goquery v1.7.1/go.mod h1:XY0pP4kfraEmmV1O7Uf6XyjoslwsneBbgeDjLYuN8xY=
|
|
||||||
github.com/Xuanwo/go-locale v1.0.0 h1:oqC32Kyiu2XZq+fxtwEg0mWiv9WyDhyHu+sT5cDkgME=
|
|
||||||
github.com/Xuanwo/go-locale v1.0.0/go.mod h1:kB9tcLfr4Sp+ByIE9SE7vbUkXkGQqel2XH3EHpL0haA=
|
|
||||||
github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE=
|
|
||||||
github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/gen2brain/dlgs v0.0.0-20190708095831-3854608588f7 h1:qA8Mdjwrlv/r/aMqArqO0IMHUiy6ApdW4+8DtKr7PvA=
|
|
||||||
github.com/gen2brain/dlgs v0.0.0-20190708095831-3854608588f7/go.mod h1:/eFcjDXaU2THSOOqLxOPETIbHETnamk8FA/hMjhg/gU=
|
|
||||||
github.com/go-flutter-desktop/go-flutter v0.30.0/go.mod h1:NCryd/AqiRbYSd8pMzQldYkgH1tZIFGt2ToUghZcWGA=
|
|
||||||
github.com/go-flutter-desktop/go-flutter v0.43.0 h1:7tdUbGKmHwdsUnBfC/h7zAO3T67cAkKSCWi9ZDFg25A=
|
|
||||||
github.com/go-flutter-desktop/go-flutter v0.43.0/go.mod h1:GSCn6XOpB0cnYlK9/BdSwxi99t5YD1XEk0v4agI7SS4=
|
|
||||||
github.com/go-flutter-desktop/plugins/url_launcher v0.1.2 h1:oFiIJjotMQvF8rfKWVJrf+1/JgTXShEIsibkiXrQnUw=
|
|
||||||
github.com/go-flutter-desktop/plugins/url_launcher v0.1.2/go.mod h1:GYgRDaLDAJRYvaASQk8HEmI8YJurbZGW5VVDIMxwzBU=
|
|
||||||
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw=
|
|
||||||
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
|
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
|
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20201108214237-06ea97f0c265 h1:BcbKYUZo/TKPsiSh7LymK3p+TNAJJW3OfGO/21sBbiA=
|
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20201108214237-06ea97f0c265/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190915194858-d3ddacdb130f h1:TyqzGm2z1h3AGhjOoRYyeLcW4WlW81MDQkWa+rx/000=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190915194858-d3ddacdb130f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
|
||||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
|
||||||
github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
|
|
||||||
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
|
||||||
github.com/miguelpruivo/flutter_file_picker/go v0.0.0-20210622152105-9f0a811028a0 h1:hXl9AMW20Php3xWlWZr2Acw50tqeblLgtLfLoRCACmA=
|
|
||||||
github.com/miguelpruivo/flutter_file_picker/go v0.0.0-20210622152105-9f0a811028a0/go.mod h1:csuW+TFyYKtiUwNvcvhcpyX4quPI7Pvv0SUogdqCW4I=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
|
||||||
github.com/niuhuan/pica-go v0.0.0-20220224154849-76bf750f8c4d h1:f3V6V1Y+5j/AvhsIGA6aQ/K2Ez1AeYbuCG9uI4fGC6M=
|
|
||||||
github.com/niuhuan/pica-go v0.0.0-20220224154849-76bf750f8c4d/go.mod h1:r76zBgH9AYkv0ptyEVoPUIdt33sT0Ts7xgcg742OZtw=
|
|
||||||
github.com/niuhuan/pica-go v0.0.0-20220415110443-b5c4a97b0103 h1:Qc40/rqbtY0fXAlPEzlfBdQk7wIHTjcTmejmBvdKeco=
|
|
||||||
github.com/niuhuan/pica-go v0.0.0-20220415110443-b5c4a97b0103/go.mod h1:r76zBgH9AYkv0ptyEVoPUIdt33sT0Ts7xgcg742OZtw=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
|
||||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
|
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
|
||||||
golang.org/x/mobile v0.0.0-20220224134551-8a0a1e50732f h1:G/wQ/Mbs60nXhRM80J4DOzy7FEIZjNprzOneArSgOl0=
|
|
||||||
golang.org/x/mobile v0.0.0-20220224134551-8a0a1e50732f/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
|
|
||||||
golang.org/x/mobile v0.0.0-20220307220422-55113b94f09c h1:9J0m/JcA5YXYbamDhF5I3T7cJnR7V75OCLnMCPb5gl4=
|
|
||||||
golang.org/x/mobile v0.0.0-20220307220422-55113b94f09c/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
|
|
||||||
golang.org/x/mobile v0.0.0-20220325161704-447654d348e3 h1:ZDL7hDvJEQEcHVkoZawKmRUgbqn1pOIzb8EinBh5csU=
|
|
||||||
golang.org/x/mobile v0.0.0-20220325161704-447654d348e3/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
|
|
||||||
golang.org/x/mobile v0.0.0-20220414153400-ce6a79cf6a13 h1:C4eR3yV9J3RkP8WKkcQzpcPyr/foOcUtxW72DvJEOlI=
|
|
||||||
golang.org/x/mobile v0.0.0-20220414153400-ce6a79cf6a13/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
|
|
||||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
|
||||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
|
|
||||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200802091954-4b90ce9b60b3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7 h1:BXxu8t6QN0G1uff4bzZzSkpsax8+ALqTGUtz08QrV00=
|
|
||||||
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098 h1:YuekqPskqwCCPM79F1X5Dhv4ezTCj+Ki1oNwiafxkA0=
|
|
||||||
golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gorm.io/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM=
|
|
||||||
gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw=
|
|
||||||
gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
|
||||||
gorm.io/gorm v1.21.12 h1:3fQM0Eiz7jcJEhPggHEpoYnsGZqynMzverL77DV40RM=
|
|
||||||
gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
|
|
@ -1,9 +0,0 @@
|
||||||
#application-name: "pikapi" # Uncomment to modify this value.
|
|
||||||
#executable-name: "pikapi" # Uncomment to modify this value. Only lowercase a-z, numbers, underscores and no spaces
|
|
||||||
#package-name: "pikapi" # Uncomment to modify this value. Only lowercase a-z, numbers and no underscores or spaces
|
|
||||||
organization-name: "niuhuan"
|
|
||||||
license: "" # MANDATORY: Fill in your SPDX license name: https://spdx.org/licenses
|
|
||||||
target: lib/main_desktop.dart
|
|
||||||
# opengl: "none" # Uncomment this line if you have trouble with your OpenGL driver (https://github.com/go-flutter-desktop/go-flutter/issues/272)
|
|
||||||
docker: false
|
|
||||||
engine-version: "" # change to a engine version commit
|
|
|
@ -1,22 +0,0 @@
|
||||||
package mobile
|
|
||||||
|
|
||||||
import (
|
|
||||||
"pikapika/pikapika"
|
|
||||||
"pikapika/pikapika/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
func InitApplication(application string) {
|
|
||||||
config.InitApplication(application)
|
|
||||||
}
|
|
||||||
|
|
||||||
func FlatInvoke(method string, params string) (string, error) {
|
|
||||||
return pikapika.FlatInvoke(method, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
func EventNotify(notify EventNotifyHandler) {
|
|
||||||
pikapika.EventNotify = notify.OnNotify
|
|
||||||
}
|
|
||||||
|
|
||||||
type EventNotifyHandler interface {
|
|
||||||
OnNotify(message string)
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>English</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>{{.executableName}}</string>
|
|
||||||
<key>CFBundleGetInfoString</key>
|
|
||||||
<string>{{.description}}</string>
|
|
||||||
<key>CFBundleIconFile</key>
|
|
||||||
<string>icon.icns</string>
|
|
||||||
<key>NSHighResolutionCapable</key>
|
|
||||||
<true/>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>{{.organizationName}}.{{.packageName}}</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleLongVersionString</key>
|
|
||||||
<string>{{.version}}</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>{{.applicationName}}</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>{{.version}}</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>{{.organizationName}}.{{.packageName}}</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>{{.version}}</string>
|
|
||||||
<key>CSResourcesFileMapped</key>
|
|
||||||
<true/>
|
|
||||||
<key>NSHumanReadableCopyright</key>
|
|
||||||
<string></string>
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string>NSApplication</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
cd "$(dirname "$0")"
|
|
||||||
exec ./build/{{.executableName}}
|
|
|
@ -1,9 +0,0 @@
|
||||||
[Desktop Entry]
|
|
||||||
Version=1.0
|
|
||||||
Type=Application
|
|
||||||
Terminal=false
|
|
||||||
Categories=
|
|
||||||
Comment={{.description}}
|
|
||||||
Name={{.applicationName}}
|
|
||||||
Icon={{.iconPath}}
|
|
||||||
Exec={{.executablePath}}
|
|
|
@ -1,617 +0,0 @@
|
||||||
// 透传Client的功能并增加缓存
|
|
||||||
|
|
||||||
package pikapika
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
source "github.com/niuhuan/pica-go"
|
|
||||||
"golang.org/x/net/proxy"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"pikapika/pikapika/database/comic_center"
|
|
||||||
"pikapika/pikapika/database/network_cache"
|
|
||||||
"pikapika/pikapika/database/properties"
|
|
||||||
"regexp"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func InitClient() {
|
|
||||||
client.Timeout = time.Second * 60
|
|
||||||
switchAddress, _ = properties.LoadIntProperty("switchAddress", 1)
|
|
||||||
imageSwitchAddress, _ = properties.LoadIntProperty("imageSwitchAddress", 1)
|
|
||||||
proxy, _ := properties.LoadProxy()
|
|
||||||
changeProxyUrl(proxy)
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = source.Client{}
|
|
||||||
var dialer = &net.Dialer{
|
|
||||||
Timeout: 30 * time.Second,
|
|
||||||
KeepAlive: 30 * time.Second,
|
|
||||||
}
|
|
||||||
|
|
||||||
// SwitchAddress
|
|
||||||
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("^.+picacomic\\.com:\\d+$")
|
|
||||||
|
|
||||||
func changeProxyUrl(urlStr string) bool {
|
|
||||||
if urlStr == "" {
|
|
||||||
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) {
|
|
||||||
if sAddr, ok := switchAddresses[switchAddress]; ok {
|
|
||||||
addr = sAddr
|
|
||||||
}
|
|
||||||
return dialer.DialContext(ctx, network, addr)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
imageHttpClient.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)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
proxyUrl, err := url.Parse(urlStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
proxy, err := proxy.FromURL(proxyUrl, proxy.Direct)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if sAddr, ok := switchAddresses[switchAddress]; ok {
|
|
||||||
addr = sAddr
|
|
||||||
}
|
|
||||||
return proxy.Dial(network, addr)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
imageHttpClient.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) {
|
|
||||||
proxyUrl, err := url.Parse(urlStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
proxy, err := proxy.FromURL(proxyUrl, proxy.Direct)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return proxy.Dial(network, addr)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func userProfile() (string, error) {
|
|
||||||
return serialize(client.UserProfile())
|
|
||||||
}
|
|
||||||
|
|
||||||
func punchIn() (string, error) {
|
|
||||||
return serialize(client.PunchIn())
|
|
||||||
}
|
|
||||||
|
|
||||||
func categories() (string, error) {
|
|
||||||
key := "CATEGORIES"
|
|
||||||
expire := time.Hour * 3
|
|
||||||
cache := network_cache.LoadCache(key, expire)
|
|
||||||
if cache != "" {
|
|
||||||
return cache, nil
|
|
||||||
}
|
|
||||||
categories, err := client.Categories()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
buff, _ := json.Marshal(&categories)
|
|
||||||
cache = string(buff)
|
|
||||||
network_cache.SaveCache(key, cache)
|
|
||||||
return cache, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func collections(_ string) (string, error) {
|
|
||||||
key := "COLLECTIONS"
|
|
||||||
expire := time.Hour * 3
|
|
||||||
cache := network_cache.LoadCache(key, expire)
|
|
||||||
if cache != "" {
|
|
||||||
return cache, nil
|
|
||||||
}
|
|
||||||
collections, err := client.Collections()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
buff, _ := json.Marshal(&collections)
|
|
||||||
cache = string(buff)
|
|
||||||
network_cache.SaveCache(key, cache)
|
|
||||||
return cache, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func comics(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
Category string `json:"category"`
|
|
||||||
Tag string `json:"tag"`
|
|
||||||
CreatorId string `json:"creatorId"`
|
|
||||||
ChineseTeam string `json:"chineseTeam"`
|
|
||||||
Sort string `json:"sort"`
|
|
||||||
Page int `json:"page"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
return cacheable(
|
|
||||||
fmt.Sprintf("COMICS$%s$%s$%s$%s$%s$%d", paramsStruct.Category, paramsStruct.Tag, paramsStruct.CreatorId, paramsStruct.ChineseTeam, paramsStruct.Sort, paramsStruct.Page),
|
|
||||||
time.Hour*2,
|
|
||||||
func() (interface{}, error) {
|
|
||||||
return client.Comics(paramsStruct.Category, paramsStruct.Tag, paramsStruct.CreatorId, paramsStruct.ChineseTeam, paramsStruct.Sort, paramsStruct.Page)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func searchComics(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
Categories []string `json:"categories"`
|
|
||||||
Keyword string `json:"keyword"`
|
|
||||||
Sort string `json:"sort"`
|
|
||||||
Page int `json:"page"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
categories := paramsStruct.Categories
|
|
||||||
keyword := paramsStruct.Keyword
|
|
||||||
sort := paramsStruct.Sort
|
|
||||||
page := paramsStruct.Page
|
|
||||||
//
|
|
||||||
var categoriesInKey string
|
|
||||||
if len(categories) == 0 {
|
|
||||||
categoriesInKey = ""
|
|
||||||
} else {
|
|
||||||
b, _ := json.Marshal(categories)
|
|
||||||
categoriesInKey = string(b)
|
|
||||||
}
|
|
||||||
return cacheable(
|
|
||||||
fmt.Sprintf("SEARCH$%s$%s$%s$%d", categoriesInKey, keyword, sort, page),
|
|
||||||
time.Hour*2,
|
|
||||||
func() (interface{}, error) {
|
|
||||||
return client.SearchComics(categories, keyword, sort, page)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func randomComics() (string, error) {
|
|
||||||
return cacheable(
|
|
||||||
fmt.Sprintf("RANDOM"),
|
|
||||||
time.Millisecond*1,
|
|
||||||
func() (interface{}, error) {
|
|
||||||
return client.RandomComics()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func leaderboard(typeName string) (string, error) {
|
|
||||||
return cacheable(
|
|
||||||
fmt.Sprintf("LEADERBOARD$%s", typeName),
|
|
||||||
time.Second*200,
|
|
||||||
func() (interface{}, error) {
|
|
||||||
return client.Leaderboard(typeName)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func comicInfo(comicId string) (string, error) {
|
|
||||||
var err error
|
|
||||||
var comic *source.ComicInfo
|
|
||||||
// cache
|
|
||||||
key := fmt.Sprintf("COMIC_INFO$%s", comicId)
|
|
||||||
expire := time.Hour * 24 * 7
|
|
||||||
cache := network_cache.LoadCache(key, expire)
|
|
||||||
if cache != "" {
|
|
||||||
var co source.ComicInfo
|
|
||||||
err = json.Unmarshal([]byte(cache), &co)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
comic = &co
|
|
||||||
} else {
|
|
||||||
// get
|
|
||||||
comic, err = client.ComicInfo(comicId)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
var buff []byte
|
|
||||||
buff, err = json.Marshal(comic)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
cache = string(buff)
|
|
||||||
network_cache.SaveCache(key, cache)
|
|
||||||
}
|
|
||||||
// 标记历史记录
|
|
||||||
view := comic_center.ComicView{}
|
|
||||||
view.ID = comicId
|
|
||||||
view.CreatedAt = comic.CreatedAt
|
|
||||||
view.UpdatedAt = comic.UpdatedAt
|
|
||||||
view.Title = comic.Title
|
|
||||||
view.Author = comic.Author
|
|
||||||
view.PagesCount = int32(comic.PagesCount)
|
|
||||||
view.EpsCount = int32(comic.EpsCount)
|
|
||||||
view.Finished = comic.Finished
|
|
||||||
c, _ := json.Marshal(comic.Categories)
|
|
||||||
view.Categories = string(c)
|
|
||||||
view.ThumbOriginalName = comic.Thumb.OriginalName
|
|
||||||
view.ThumbFileServer = comic.Thumb.FileServer
|
|
||||||
view.ThumbPath = comic.Thumb.Path
|
|
||||||
view.LikesCount = int32(comic.LikesCount)
|
|
||||||
view.Description = comic.Description
|
|
||||||
view.ChineseTeam = comic.ChineseTeam
|
|
||||||
t, _ := json.Marshal(comic.Tags)
|
|
||||||
view.Tags = string(t)
|
|
||||||
view.AllowDownload = comic.AllowDownload
|
|
||||||
view.ViewsCount = int32(comic.ViewsCount)
|
|
||||||
view.IsFavourite = comic.IsFavourite
|
|
||||||
view.IsLiked = comic.IsLiked
|
|
||||||
view.CommentsCount = int32(comic.CommentsCount)
|
|
||||||
err = comic_center.ViewComicUpdateInfo(&view)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
// return
|
|
||||||
return cache, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ComicInfoCleanCache(comicId string) {
|
|
||||||
key := fmt.Sprintf("COMIC_INFO$%s", comicId)
|
|
||||||
network_cache.RemoveCache(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func epPage(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
ComicId string `json:"comicId"`
|
|
||||||
Page int `json:"page"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
comicId := paramsStruct.ComicId
|
|
||||||
page := paramsStruct.Page
|
|
||||||
//
|
|
||||||
return cacheable(
|
|
||||||
fmt.Sprintf("COMIC_EP_PAGE$%s$%d", comicId, page),
|
|
||||||
time.Hour*2,
|
|
||||||
func() (interface{}, error) {
|
|
||||||
return client.ComicEpPage(comicId, page)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func comicPicturePageWithQuality(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
ComicId string `json:"comicId"`
|
|
||||||
EpOrder int `json:"epOrder"`
|
|
||||||
Page int `json:"page"`
|
|
||||||
Quality string `json:"quality"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
comicId := paramsStruct.ComicId
|
|
||||||
epOrder := paramsStruct.EpOrder
|
|
||||||
page := paramsStruct.Page
|
|
||||||
quality := paramsStruct.Quality
|
|
||||||
//
|
|
||||||
return cacheable(
|
|
||||||
fmt.Sprintf("COMIC_EP_PAGE$%s$%ds$%ds$%s", comicId, epOrder, page, quality),
|
|
||||||
time.Hour*2,
|
|
||||||
func() (interface{}, error) {
|
|
||||||
return client.ComicPicturePageWithQuality(comicId, epOrder, page, quality)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func switchLike(comicId string) (string, error) {
|
|
||||||
point, err := client.SwitchLike(comicId)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
// 更新viewLog里面的favour
|
|
||||||
comic_center.ViewComicUpdateLike(comicId, strings.HasPrefix(*point, "un"))
|
|
||||||
// 删除缓存
|
|
||||||
ComicInfoCleanCache(comicId)
|
|
||||||
return *point, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func switchFavourite(comicId string) (string, error) {
|
|
||||||
point, err := client.SwitchFavourite(comicId)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
// 更新viewLog里面的favour
|
|
||||||
comic_center.ViewComicUpdateFavourite(comicId, strings.HasPrefix(*point, "un"))
|
|
||||||
// 删除缓存
|
|
||||||
ComicInfoCleanCache(comicId)
|
|
||||||
return *point, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func favouriteComics(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
Sort string `json:"sort"`
|
|
||||||
Page int `json:"page"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
sort := paramsStruct.Sort
|
|
||||||
page := paramsStruct.Page
|
|
||||||
//
|
|
||||||
point, err := client.FavouriteComics(sort, page)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
str, err := json.Marshal(point)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(str), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func recommendation(comicId string) (string, error) {
|
|
||||||
return cacheable(
|
|
||||||
fmt.Sprintf("RECOMMENDATION$%s", comicId),
|
|
||||||
time.Hour*2,
|
|
||||||
func() (interface{}, error) {
|
|
||||||
return client.ComicRecommendation(comicId)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func comments(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
ComicId string `json:"comicId"`
|
|
||||||
Page int `json:"page"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
comicId := paramsStruct.ComicId
|
|
||||||
page := paramsStruct.Page
|
|
||||||
return cacheable(
|
|
||||||
fmt.Sprintf("COMMENTS$%s$%d", comicId, page),
|
|
||||||
time.Hour*2,
|
|
||||||
func() (interface{}, error) {
|
|
||||||
return client.ComicCommentsPage(comicId, page)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func commentChildren(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
CommentId string `json:"commentId"`
|
|
||||||
Page int `json:"page"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
commentId := paramsStruct.CommentId
|
|
||||||
page := paramsStruct.Page
|
|
||||||
return cacheable(
|
|
||||||
fmt.Sprintf("COMMENT_CHILDREN$%s$%d", commentId, page),
|
|
||||||
time.Hour*2,
|
|
||||||
func() (interface{}, error) {
|
|
||||||
return client.CommentChildren(commentId, page)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func postComment(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
ComicId string `json:"comicId"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
err := client.PostComment(paramsStruct.ComicId, paramsStruct.Content)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
network_cache.RemoveCaches("MY_COMMENTS$%")
|
|
||||||
network_cache.RemoveCaches(fmt.Sprintf("COMMENTS$%s$%%", paramsStruct.ComicId))
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func postChildComment(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
ComicId string `json:"comicId"`
|
|
||||||
CommentId string `json:"commentId"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
err := client.PostChildComment(paramsStruct.CommentId, paramsStruct.Content)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
network_cache.RemoveCaches(fmt.Sprintf("COMMENT_CHILDREN$%s$%%", paramsStruct.CommentId))
|
|
||||||
network_cache.RemoveCaches("MY_COMMENTS$%")
|
|
||||||
network_cache.RemoveCaches(fmt.Sprintf("COMMENTS$%s$%%", paramsStruct.ComicId))
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func postGameChildComment(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
GameId string `json:"gameId"`
|
|
||||||
CommentId string `json:"commentId"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
err := client.PostChildComment(paramsStruct.CommentId, paramsStruct.Content)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
network_cache.RemoveCaches(fmt.Sprintf("GAME_COMMENT_CHILDREN$%s$%%", paramsStruct.CommentId))
|
|
||||||
network_cache.RemoveCaches("MY_COMMENTS$%")
|
|
||||||
network_cache.RemoveCaches(fmt.Sprintf("GAME_COMMENTS$%s$%%", paramsStruct.GameId))
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func switchLikeComment(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
CommentId string `json:"commentId"`
|
|
||||||
ComicId string `json:"comicId"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
rsp, err := client.SwitchLikeComment(paramsStruct.CommentId)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
network_cache.RemoveCaches(fmt.Sprintf("COMMENT_CHILDREN$%s$%%", paramsStruct.CommentId))
|
|
||||||
network_cache.RemoveCaches("MY_COMMENTS$%")
|
|
||||||
network_cache.RemoveCaches(fmt.Sprintf("COMMENTS$%s$%%", paramsStruct.ComicId))
|
|
||||||
return *rsp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func myComments(pageStr string) (string, error) {
|
|
||||||
page, err := strconv.Atoi(pageStr)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return cacheable(
|
|
||||||
fmt.Sprintf("MY_COMMENTS$%d", page),
|
|
||||||
time.Hour*2,
|
|
||||||
func() (interface{}, error) {
|
|
||||||
return client.MyComments(page)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func games(pageStr string) (string, error) {
|
|
||||||
page, err := strconv.Atoi(pageStr)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return cacheable(
|
|
||||||
fmt.Sprintf("GAMES$%d", page),
|
|
||||||
time.Hour*2,
|
|
||||||
func() (interface{}, error) {
|
|
||||||
return client.GamePage(page)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func game(gameId string) (string, error) {
|
|
||||||
return cacheable(
|
|
||||||
fmt.Sprintf("GAME$%s", gameId),
|
|
||||||
time.Hour*2,
|
|
||||||
func() (interface{}, error) {
|
|
||||||
return client.GameInfo(gameId)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func gameComments(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
GameId string `json:"gameId"`
|
|
||||||
Page int `json:"page"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
gameId := paramsStruct.GameId
|
|
||||||
page := paramsStruct.Page
|
|
||||||
return cacheable(
|
|
||||||
fmt.Sprintf("GAME_COMMENTS$%s$%d", gameId, page),
|
|
||||||
time.Hour*2,
|
|
||||||
func() (interface{}, error) {
|
|
||||||
return client.GameCommentsPage(gameId, page)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func postGameComment(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
GameId string `json:"gameId"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
err := client.PostGameComment(paramsStruct.GameId, paramsStruct.Content)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
network_cache.RemoveCaches("MY_COMMENTS$%")
|
|
||||||
network_cache.RemoveCaches(fmt.Sprintf("GAME_COMMENTS$%s$%%", paramsStruct.GameId))
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func gameCommentChildren(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
CommentId string `json:"commentId"`
|
|
||||||
Page int `json:"page"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
commentId := paramsStruct.CommentId
|
|
||||||
page := paramsStruct.Page
|
|
||||||
return cacheable(
|
|
||||||
fmt.Sprintf("GAME_COMMENT_CHILDREN$%s$%d", commentId, page),
|
|
||||||
time.Hour*2,
|
|
||||||
func() (interface{}, error) {
|
|
||||||
return client.GameCommentChildren(commentId, page)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func switchLikeGameComment(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
CommentId string `json:"commentId"`
|
|
||||||
GameId string `json:"gameId"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
rsp, err := client.SwitchLikeComment(paramsStruct.CommentId)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
network_cache.RemoveCaches(fmt.Sprintf("GAME_COMMENT_CHILDREN$%s$%%", paramsStruct.CommentId))
|
|
||||||
network_cache.RemoveCaches("MY_COMMENTS$%")
|
|
||||||
network_cache.RemoveCaches(fmt.Sprintf("GAME_COMMENTS$%s$%%", paramsStruct.GameId))
|
|
||||||
return *rsp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func updatePassword(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
OldPassword string `json:"oldPassword"`
|
|
||||||
NewPassword string `json:"newPassword"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
err = client.UpdatePassword(paramsStruct.OldPassword, paramsStruct.NewPassword)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
setPassword(paramsStruct.NewPassword)
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateSlogan(slogan string) (string, error) {
|
|
||||||
return "", client.UpdateSlogan(slogan)
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateAvatar(avatarBase64 string) (string, error) {
|
|
||||||
buff, err := base64.StdEncoding.DecodeString(avatarBase64)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return "", client.UpdateAvatar(buff)
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
package pikapika
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"pikapika/pikapika/database/comic_center"
|
|
||||||
"pikapika/pikapika/database/network_cache"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EventNotify EventChannel 总线
|
|
||||||
var EventNotify func(message string)
|
|
||||||
|
|
||||||
// 所有的EventChannel都是从这里发出, 格式为json, function代表了是什么事件, content是消息的内容
|
|
||||||
// 消息传到前端后由前端调度分发
|
|
||||||
func onEvent(function string, content string) {
|
|
||||||
event := EventNotify
|
|
||||||
if event != nil {
|
|
||||||
message := map[string]string{
|
|
||||||
"function": function,
|
|
||||||
"content": content,
|
|
||||||
}
|
|
||||||
buff, err := json.Marshal(message)
|
|
||||||
if err == nil {
|
|
||||||
event(string(buff))
|
|
||||||
} else {
|
|
||||||
print("SEND ERR?")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送下载的事件
|
|
||||||
func downloadComicEventSend(comicDownload *comic_center.ComicDownload) {
|
|
||||||
buff, err := json.Marshal(comicDownload)
|
|
||||||
if err == nil {
|
|
||||||
onEvent("DOWNLOAD", string(buff))
|
|
||||||
} else {
|
|
||||||
print("SEND ERR?")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送导出的事件
|
|
||||||
func notifyExport(str string) {
|
|
||||||
onEvent("EXPORT", str)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 缓存接口
|
|
||||||
func cacheable(key string, expire time.Duration, reload func() (interface{}, error)) (string, error) {
|
|
||||||
// CACHE
|
|
||||||
cache := network_cache.LoadCache(key, expire)
|
|
||||||
if cache != "" {
|
|
||||||
return cache, nil
|
|
||||||
}
|
|
||||||
// obj
|
|
||||||
obj, err := reload()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
buff, err := json.Marshal(obj)
|
|
||||||
// push to cache
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
// return
|
|
||||||
cache = string(buff)
|
|
||||||
network_cache.SaveCache(key, cache)
|
|
||||||
return cache, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将interface序列化成字符串, 方便与flutter通信
|
|
||||||
func serialize(point interface{}, err error) (string, error) {
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
buff, err := json.Marshal(point)
|
|
||||||
return string(buff), nil
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path"
|
|
||||||
"pikapika/pikapika"
|
|
||||||
"pikapika/pikapika/database/comic_center"
|
|
||||||
"pikapika/pikapika/database/network_cache"
|
|
||||||
"pikapika/pikapika/database/properties"
|
|
||||||
"pikapika/pikapika/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
// InitApplication 由不同的平台直接调用, 根据提供的路径初始化数据库, 资料文件夹
|
|
||||||
func InitApplication(applicationDir string) {
|
|
||||||
println("初始化 : " + applicationDir)
|
|
||||||
var databasesDir, remoteDir, downloadDir, tmpDir string
|
|
||||||
databasesDir = path.Join(applicationDir, "databases")
|
|
||||||
remoteDir = path.Join(applicationDir, "pictures", "remote")
|
|
||||||
downloadDir = path.Join(applicationDir, "download")
|
|
||||||
tmpDir = path.Join(applicationDir, "tmp")
|
|
||||||
utils.Mkdir(databasesDir)
|
|
||||||
utils.Mkdir(remoteDir)
|
|
||||||
utils.Mkdir(downloadDir)
|
|
||||||
utils.Mkdir(tmpDir)
|
|
||||||
properties.InitDBConnect(databasesDir)
|
|
||||||
network_cache.InitDBConnect(databasesDir)
|
|
||||||
comic_center.InitDBConnect(databasesDir)
|
|
||||||
pikapika.InitClient()
|
|
||||||
pikapika.InitPlugin(remoteDir, downloadDir, tmpDir)
|
|
||||||
}
|
|
|
@ -1,583 +0,0 @@
|
||||||
package comic_center
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gorm.io/driver/sqlite"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"gorm.io/gorm/clause"
|
|
||||||
"path"
|
|
||||||
"pikapika/pikapika/utils"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var mutex = sync.Mutex{}
|
|
||||||
var db *gorm.DB
|
|
||||||
|
|
||||||
func InitDBConnect(databaseDir string) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var err error
|
|
||||||
db, err = gorm.Open(sqlite.Open(path.Join(databaseDir, "comic_center.db")), utils.GormConfig)
|
|
||||||
if err != nil {
|
|
||||||
panic("failed to connect database")
|
|
||||||
}
|
|
||||||
db.AutoMigrate(&ComicView{})
|
|
||||||
db.AutoMigrate(&RemoteImage{})
|
|
||||||
db.AutoMigrate(&ComicDownload{})
|
|
||||||
db.AutoMigrate(&ComicDownloadEp{})
|
|
||||||
db.AutoMigrate(&ComicDownloadPicture{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func Transaction(t func(tx *gorm.DB) error) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Transaction(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NoLockActionViewComicUpdateInfoDB(view *ComicView, db *gorm.DB) error {
|
|
||||||
view.LastViewTime = time.Now()
|
|
||||||
return db.Clauses(clause.OnConflict{
|
|
||||||
Columns: []clause.Column{{Name: "id"}},
|
|
||||||
DoUpdates: clause.AssignmentColumns([]string{
|
|
||||||
"created_at",
|
|
||||||
"updated_at",
|
|
||||||
"title",
|
|
||||||
"author",
|
|
||||||
"pages_count",
|
|
||||||
"eps_count",
|
|
||||||
"finished",
|
|
||||||
"categories",
|
|
||||||
"thumb_original_name",
|
|
||||||
"thumb_file_server",
|
|
||||||
"thumb_path",
|
|
||||||
"likes_count",
|
|
||||||
"description",
|
|
||||||
"chinese_team",
|
|
||||||
"tags",
|
|
||||||
"allow_download",
|
|
||||||
"views_count",
|
|
||||||
"is_favourite",
|
|
||||||
"is_liked",
|
|
||||||
"comments_count",
|
|
||||||
"last_view_time",
|
|
||||||
}),
|
|
||||||
}).Create(view).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func ViewComicUpdateInfo(view *ComicView) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return NoLockActionViewComicUpdateInfoDB(view, db)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ViewComic(comicId string) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Model(&ComicView{}).Where(
|
|
||||||
"id = ?", comicId,
|
|
||||||
).Update(
|
|
||||||
"last_view_time",
|
|
||||||
time.Now(),
|
|
||||||
).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func ViewComicUpdateFavourite(comicId string, favourite bool) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Model(&ComicView{}).Where(
|
|
||||||
"id = ?", comicId,
|
|
||||||
).Update(
|
|
||||||
"is_favourite",
|
|
||||||
favourite,
|
|
||||||
).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func ViewComicUpdateLike(comicId string, like bool) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Model(&ComicView{}).Where(
|
|
||||||
"id = ?", comicId,
|
|
||||||
).Update(
|
|
||||||
"is_like",
|
|
||||||
like,
|
|
||||||
).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func ViewEpAndPicture(comicId string, epOrder int, epTitle string, pictureRank int) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Model(&ComicView{}).Where("id", comicId).Updates(
|
|
||||||
map[string]interface{}{
|
|
||||||
"last_view_time": time.Now(),
|
|
||||||
"last_view_ep_order": epOrder,
|
|
||||||
"last_view_ep_title": epTitle,
|
|
||||||
"last_view_picture_rank": pictureRank,
|
|
||||||
},
|
|
||||||
).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadViewLog(comicId string) (*ComicView, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var view ComicView
|
|
||||||
err := db.First(&view, "id = ?", comicId).Error
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &view, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func FindRemoteImage(fileServer string, path string) *RemoteImage {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var remoteImage RemoteImage
|
|
||||||
err := db.First(&remoteImage, "file_server = ? AND path = ?", fileServer, path).Error
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &remoteImage
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveRemoteImage(remote *RemoteImage) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Clauses(clause.OnConflict{
|
|
||||||
Columns: []clause.Column{{Name: "file_server"}, {Name: "path"}},
|
|
||||||
DoUpdates: clause.AssignmentColumns([]string{
|
|
||||||
"updated_at",
|
|
||||||
"file_size",
|
|
||||||
"format",
|
|
||||||
"width",
|
|
||||||
"height",
|
|
||||||
"local_path",
|
|
||||||
}),
|
|
||||||
}).Create(remote).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateDownload(comic *ComicDownload, epList *[]ComicDownloadEp) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
comic.SelectedEpCount = int32(len(*epList))
|
|
||||||
return db.Transaction(func(tx *gorm.DB) error {
|
|
||||||
err := tx.Create(comic).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, ep := range *epList {
|
|
||||||
err := tx.Create(&ep).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddDownload(comic *ComicDownload, epList *[]ComicDownloadEp) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Transaction(func(tx *gorm.DB) error {
|
|
||||||
err := tx.Model(comic).Where("id = ?", comic.ID).Updates(map[string]interface{}{
|
|
||||||
"created_at": comic.CreatedAt,
|
|
||||||
"updated_at": comic.UpdatedAt,
|
|
||||||
"title": comic.Title,
|
|
||||||
"author": comic.Author,
|
|
||||||
"pages_count": comic.PagesCount,
|
|
||||||
"eps_count": comic.EpsCount,
|
|
||||||
"finished": comic.Finished,
|
|
||||||
"categories": comic.Categories,
|
|
||||||
"thumb_original_name": comic.ThumbOriginalName,
|
|
||||||
"thumb_file_server": comic.ThumbFileServer,
|
|
||||||
"thumb_path": comic.ThumbPath,
|
|
||||||
"description": comic.Description,
|
|
||||||
"chinese_team": comic.ChineseTeam,
|
|
||||||
"tags": comic.Tags,
|
|
||||||
"download_finished": false, // restart
|
|
||||||
}).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = tx.Exec(
|
|
||||||
"UPDATE comic_downloads SET eps_count = selected_ep_count + ? WHERE id = ?",
|
|
||||||
len(*epList), comic.ID,
|
|
||||||
).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, ep := range *epList {
|
|
||||||
err := tx.Create(&ep).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateDownloadLogo(comicId string, fileSize int64, format string, width int32, height int32, localPath string) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Model(&ComicDownload{}).Where("id = ?", comicId).Updates(map[string]interface{}{
|
|
||||||
"thumb_file_size": fileSize,
|
|
||||||
"thumb_format": format,
|
|
||||||
"thumb_width": width,
|
|
||||||
"thumb_height": height,
|
|
||||||
"thumb_local_path": localPath,
|
|
||||||
}).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func FindComicDownloadById(comicId string) (*ComicDownload, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var download ComicDownload
|
|
||||||
err := db.First(&download, "id = ?", comicId).Error
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &download, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListDownloadEpByComicId(comicId string) ([]ComicDownloadEp, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var epList []ComicDownloadEp
|
|
||||||
err := db.Where("comic_id = ?", comicId).Order("ep_order ASC").Find(&epList).Error
|
|
||||||
return epList, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListDownloadPictureByEpId(epId string) ([]ComicDownloadPicture, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var pictureList []ComicDownloadPicture
|
|
||||||
err := db.Where("ep_id = ?", epId).Order("rank_in_ep ASC").Find(&pictureList).Error
|
|
||||||
return pictureList, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func AllDownloads() (*[]ComicDownload, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var downloads []ComicDownload
|
|
||||||
err := db.Table("comic_downloads").
|
|
||||||
Joins("LEFT JOIN comic_views ON comic_views.id = comic_downloads.id").
|
|
||||||
Select("comic_downloads.*").
|
|
||||||
Order("comic_views.last_view_time DESC").
|
|
||||||
Scan(&downloads).Error
|
|
||||||
// err := db.Find(&downloads).Error
|
|
||||||
return &downloads, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadFirstNeedDownload() (*ComicDownload, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var download ComicDownload
|
|
||||||
err := db.First(&download, "download_failed = 0 AND pause = 0 AND deleting = 0 AND download_finished = 0").Error
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &download, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadFirstNeedDownloadEp(comicId string) (*ComicDownloadEp, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var ep ComicDownloadEp
|
|
||||||
err := db.First(
|
|
||||||
&ep,
|
|
||||||
" comic_id = ? AND download_failed = 0 AND download_finished = 0 AND fetched_pictures = 1",
|
|
||||||
comicId,
|
|
||||||
).Error
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &ep, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadNeedDownloadPictures 根据EP.ID获取需要下载的图片
|
|
||||||
func LoadNeedDownloadPictures(epId string, limit int) (*[]ComicDownloadPicture, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var pictures []ComicDownloadPicture
|
|
||||||
err := db.Find(
|
|
||||||
&pictures,
|
|
||||||
"ep_id = ? AND download_failed = 0 AND download_finished = 0 LIMIT ?",
|
|
||||||
epId, limit,
|
|
||||||
).Error
|
|
||||||
return &pictures, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func FetchPictures(comicId string, epId string, list *[]ComicDownloadPicture) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Transaction(func(tx *gorm.DB) error {
|
|
||||||
var rankInEp int32
|
|
||||||
for _, picture := range *list {
|
|
||||||
rankInEp = rankInEp + 1
|
|
||||||
picture.RankInEp = rankInEp
|
|
||||||
err := tx.Create(&picture).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err := tx.Model(&ComicDownloadEp{}).Where("id = ?", epId).Updates(map[string]interface{}{
|
|
||||||
"fetched_pictures": true,
|
|
||||||
"selected_picture_count": len(*list),
|
|
||||||
}).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return tx.Exec(
|
|
||||||
"UPDATE comic_downloads SET selected_picture_count = selected_picture_count + ? WHERE id = ?",
|
|
||||||
len(*list), comicId,
|
|
||||||
).Error
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func DownloadFailed(comicId string) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Model(&ComicDownload{}).Where("id = ?", comicId).Update("download_failed", true).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func DownloadSuccess(comicId string) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Model(&ComicDownload{}).Where("id = ?", comicId).Updates(map[string]interface{}{
|
|
||||||
"download_finished": true,
|
|
||||||
"download_finished_time": time.Now(),
|
|
||||||
}).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func EpFailed(epId string) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Model(&ComicDownloadEp{}).Where("id = ?", epId).Update("download_failed", true).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func EpSuccess(comicId string, epId string) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Transaction(func(tx *gorm.DB) error {
|
|
||||||
err := tx.Model(&ComicDownloadEp{}).Where("id = ?", epId).Updates(map[string]interface{}{
|
|
||||||
"download_finished": true,
|
|
||||||
"download_finished_time": time.Now(),
|
|
||||||
}).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return tx.Exec(
|
|
||||||
"UPDATE comic_downloads SET download_ep_count = download_ep_count + 1 WHERE id = ?",
|
|
||||||
comicId,
|
|
||||||
).Error
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func PictureFailed(pictureId string) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Model(&ComicDownloadPicture{}).Where("id = ?", pictureId).Update("download_failed", true).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func PictureSuccess(
|
|
||||||
comicId string, epId string, pictureId string,
|
|
||||||
fileSize int64, format string, width int32, height int32, localPath string,
|
|
||||||
) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Transaction(func(tx *gorm.DB) error {
|
|
||||||
err := tx.Model(&ComicDownloadPicture{}).Where("id = ?", pictureId).Updates(map[string]interface{}{
|
|
||||||
"file_size": fileSize,
|
|
||||||
"format": format,
|
|
||||||
"width": width,
|
|
||||||
"height": height,
|
|
||||||
"local_path": localPath,
|
|
||||||
"download_finished": true,
|
|
||||||
"download_finished_time": time.Now(),
|
|
||||||
}).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = tx.Exec(
|
|
||||||
"UPDATE comic_download_eps SET download_picture_count = download_picture_count + 1 WHERE id = ?",
|
|
||||||
epId,
|
|
||||||
).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return tx.Exec(
|
|
||||||
"UPDATE comic_downloads SET download_picture_count = download_picture_count + 1 WHERE id = ?",
|
|
||||||
comicId,
|
|
||||||
).Error
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func ResetAll() error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Transaction(func(tx *gorm.DB) error {
|
|
||||||
err := tx.Model(&ComicDownload{}).Where("1 = 1").
|
|
||||||
Update("download_failed", false).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = tx.Model(&ComicDownloadEp{}).Where("1 = 1").
|
|
||||||
Update("download_failed", false).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = tx.Model(&ComicDownloadPicture{}).Where("1 = 1").
|
|
||||||
Update("download_failed", false).Error
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func ViewLogPage(offset int, limit int) (*[]ComicView, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var list []ComicView
|
|
||||||
err := db.Offset(offset).Limit(limit).Order("last_view_time DESC").Find(&list).Error
|
|
||||||
return &list, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func ClearAllViewLog() {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
db.Unscoped().Where("1 = 1").Delete(&ComicView{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteViewLog(id string) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
db.Unscoped().Where("id = ?", id).Delete(&ComicView{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeletingComic() (*ComicDownload, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var download ComicDownload
|
|
||||||
err := db.First(&download, "deleting = 1").Error
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return &download, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func TrueDelete(comicId string) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
err := db.Transaction(func(tx *gorm.DB) error {
|
|
||||||
err := tx.Unscoped().Delete(&ComicDownload{}, "id = ?", comicId).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = tx.Unscoped().Delete(&ComicDownloadEp{}, "comic_id = ?", comicId).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = tx.Unscoped().Delete(&ComicDownloadPicture{}, "comic_id = ?", comicId).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return db.Raw("VACUUM").Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func Deleting(comicId string) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Model(&ComicDownload{}).Where("id = ?", comicId).Updates(map[string]interface{}{
|
|
||||||
"deleting": true,
|
|
||||||
}).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func RemoveAllRemoteImage() error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
err := db.Unscoped().Delete(&RemoteImage{}, "1 = 1").Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return db.Raw("VACUUM").Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func EarliestRemoteImage(earliest time.Time, pageSize int) ([]RemoteImage, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var images []RemoteImage
|
|
||||||
err := db.Where("strftime('%s',updated_at) < strftime('%s',?)", earliest).
|
|
||||||
Order("updated_at").Limit(pageSize).Find(&images).Error
|
|
||||||
return images, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteRemoteImages(images []RemoteImage) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
if len(images) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ids := make([]uint, len(images))
|
|
||||||
for i := 0; i < len(images); i++ {
|
|
||||||
ids[i] = images[i].ID
|
|
||||||
}
|
|
||||||
return db.Unscoped().Model(&RemoteImage{}).Delete("id in ?", ids).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func DownloadInfo(comicId string) (*ComicDownload, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var download ComicDownload
|
|
||||||
err := db.First(&download, "id = ?", comicId).Error
|
|
||||||
if err != nil {
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &download, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateTimeCacheImageTime(id uint) {
|
|
||||||
now := time.Now()
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
err := db.Model(&RemoteImage{}).Where("id = ?", id).Updates(map[string]interface{}{
|
|
||||||
"created_at": now,
|
|
||||||
"updated_at": now,
|
|
||||||
}).Error
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ViewedList(ids []string) (viewedList []ComicView) {
|
|
||||||
err := db.Find(&viewedList, ids).Error
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func VACUUM() error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Raw("VACUUM").Error
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
package comic_center
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RemoteImage struct {
|
|
||||||
gorm.Model
|
|
||||||
FileServer string `gorm:"index:uk_fp,unique" json:"fileServer"`
|
|
||||||
Path string `gorm:"index:uk_fp,unique" json:"path"`
|
|
||||||
FileSize int64 `json:"fileSize"`
|
|
||||||
Format string `json:"format"`
|
|
||||||
Width int32 `json:"width"`
|
|
||||||
Height int32 `json:"height"`
|
|
||||||
LocalPath string `json:"localPath"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ComicSimple struct {
|
|
||||||
ID string `gorm:"primarykey" json:"id"`
|
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
Author string `json:"author"`
|
|
||||||
PagesCount int32 `json:"pagesCount"`
|
|
||||||
EpsCount int32 `json:"epsCount"`
|
|
||||||
Finished bool `json:"finished"`
|
|
||||||
Categories string `json:"categories"`
|
|
||||||
ThumbOriginalName string `json:"thumbOriginalName"`
|
|
||||||
ThumbFileServer string `json:"thumbFileServer"`
|
|
||||||
ThumbPath string `json:"thumbPath"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ComicInfo struct {
|
|
||||||
ComicSimple
|
|
||||||
LikesCount int32 `json:"likesCount"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
ChineseTeam string `json:"chineseTeam"`
|
|
||||||
Tags string `json:"tags"`
|
|
||||||
AllowDownload bool `json:"allowDownload"`
|
|
||||||
ViewsCount int32 `json:"viewsCount"`
|
|
||||||
IsFavourite bool `json:"isFavourite"`
|
|
||||||
IsLiked bool `json:"isLiked"`
|
|
||||||
CommentsCount int32 `json:"commentsCount"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ComicView struct {
|
|
||||||
ComicInfo
|
|
||||||
LastViewTime time.Time `json:"lastViewTime"`
|
|
||||||
LastViewEpOrder int32 `json:"lastViewEpOrder"`
|
|
||||||
LastViewEpTitle string `json:"lastViewEpTitle"`
|
|
||||||
LastViewPictureRank int32 `json:"lastViewPictureRank"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ComicDownload struct {
|
|
||||||
ComicSimple
|
|
||||||
Description string `json:"description"`
|
|
||||||
ChineseTeam string `json:"chineseTeam"`
|
|
||||||
Tags string `json:"tags"`
|
|
||||||
SelectedEpCount int32 `json:"selectedEpCount"`
|
|
||||||
SelectedPictureCount int32 `json:"selectedPictureCount"`
|
|
||||||
DownloadEpCount int32 `json:"downloadEpCount"`
|
|
||||||
DownloadPictureCount int32 `json:"downloadPictureCount"`
|
|
||||||
DownloadFinished bool `json:"downloadFinished"`
|
|
||||||
DownloadFinishedTime time.Time `json:"downloadFinishedTime"`
|
|
||||||
DownloadFailed bool `json:"downloadFailed"`
|
|
||||||
Deleting bool `json:"deleting"`
|
|
||||||
ThumbFileSize int64 `json:"thumbFileSize"`
|
|
||||||
ThumbFormat string `json:"thumbFormat"`
|
|
||||||
ThumbWidth int32 `json:"thumbWidth"`
|
|
||||||
ThumbHeight int32 `json:"thumbHeight"`
|
|
||||||
ThumbLocalPath string `json:"thumbLocalPath"`
|
|
||||||
Pause bool `json:"pause"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ComicDownloadEp struct {
|
|
||||||
ComicId string `gorm:"index:idx_comic_id" json:"comicId"`
|
|
||||||
ID string `gorm:"primarykey" json:"id"`
|
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
|
||||||
EpOrder int32 `json:"epOrder"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
FetchedPictures bool `json:"fetchedPictures"`
|
|
||||||
SelectedPictureCount int32 `json:"selectedPictureCount"`
|
|
||||||
DownloadPictureCount int32 `json:"downloadPictureCount"`
|
|
||||||
DownloadFinished bool `json:"downloadFinish"`
|
|
||||||
DownloadFinishedTime time.Time `json:"downloadFinishTime"`
|
|
||||||
DownloadFailed bool `json:"downloadFailed"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ComicDownloadPicture struct {
|
|
||||||
ID string `gorm:"primarykey" json:"id"`
|
|
||||||
ComicId string `gorm:"index:idx_comic_id" json:"comicId"`
|
|
||||||
EpId string `gorm:"index:idx_ep_id" json:"epId"`
|
|
||||||
EpOrder int32 `gorm:"index:idx_ep_order" json:"epOrder"`
|
|
||||||
RankInEp int32 `json:"rankInEp"`
|
|
||||||
DownloadFinished bool `json:"downloadFinish"`
|
|
||||||
DownloadFinishedTime time.Time `json:"downloadFinishTime"`
|
|
||||||
DownloadFailed bool `json:"downloadFailed"`
|
|
||||||
OriginalName string
|
|
||||||
FileServer string `gorm:"index:idx_fp,priority:1" json:"fileServer"`
|
|
||||||
Path string `gorm:"index:idx_fp,priority:2" json:"path"`
|
|
||||||
FileSize int64 `json:"fileSize"`
|
|
||||||
Format string `json:"format"`
|
|
||||||
Width int32 `json:"width"`
|
|
||||||
Height int32 `json:"height"`
|
|
||||||
LocalPath string `json:"localPath"`
|
|
||||||
}
|
|
|
@ -1,99 +0,0 @@
|
||||||
package network_cache
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"gorm.io/driver/sqlite"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"gorm.io/gorm/clause"
|
|
||||||
"path"
|
|
||||||
"pikapika/pikapika/utils"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var mutex = sync.Mutex{}
|
|
||||||
var db *gorm.DB
|
|
||||||
|
|
||||||
type NetworkCache struct {
|
|
||||||
gorm.Model
|
|
||||||
K string `gorm:"index:uk_k,unique"`
|
|
||||||
V string
|
|
||||||
}
|
|
||||||
|
|
||||||
func InitDBConnect(databaseDir string) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var err error
|
|
||||||
db, err = gorm.Open(sqlite.Open(path.Join(databaseDir, "network_cache.db")), utils.GormConfig)
|
|
||||||
if err != nil {
|
|
||||||
panic("failed to connect database")
|
|
||||||
}
|
|
||||||
db.AutoMigrate(&NetworkCache{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadCache(key string, expire time.Duration) string {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var cache NetworkCache
|
|
||||||
err := db.First(&cache, "k = ? AND updated_at > ?", key, time.Now().Add(expire*-1)).Error
|
|
||||||
if err == nil {
|
|
||||||
return cache.V
|
|
||||||
}
|
|
||||||
if gorm.ErrRecordNotFound == err {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
panic(errors.New("?"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveCache(key string, value string) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
db.Clauses(clause.OnConflict{
|
|
||||||
Columns: []clause.Column{{Name: "k"}},
|
|
||||||
DoUpdates: clause.AssignmentColumns([]string{"created_at", "updated_at", "v"}),
|
|
||||||
}).Create(&NetworkCache{
|
|
||||||
K: key,
|
|
||||||
V: value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func RemoveCache(key string) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
err := db.Unscoped().Delete(&NetworkCache{}, "k = ?", key).Error
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func RemoveCaches(like string) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
err := db.Unscoped().Delete(&NetworkCache{}, "k LIKE ?", like).Error
|
|
||||||
if err == gorm.ErrRecordNotFound {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func RemoveAll() error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
err := db.Unscoped().Delete(&NetworkCache{}, "1 = 1").Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return db.Raw("VACUUM").Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func RemoveEarliest(earliest time.Time) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
err := db.Unscoped().Where("strftime('%s',updated_at) < strftime('%s',?)", earliest).
|
|
||||||
Delete(&NetworkCache{}).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return db.Raw("VACUUM").Error
|
|
||||||
}
|
|
|
@ -1,127 +0,0 @@
|
||||||
package properties
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"gorm.io/driver/sqlite"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"gorm.io/gorm/clause"
|
|
||||||
"path"
|
|
||||||
"pikapika/pikapika/utils"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var mutex = sync.Mutex{}
|
|
||||||
var db *gorm.DB
|
|
||||||
|
|
||||||
func InitDBConnect(databaseDir string) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var err error
|
|
||||||
db, err = gorm.Open(sqlite.Open(path.Join(databaseDir, "properties.db")), utils.GormConfig)
|
|
||||||
if err != nil {
|
|
||||||
panic("failed to connect database")
|
|
||||||
}
|
|
||||||
db.AutoMigrate(&Property{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type Property struct {
|
|
||||||
gorm.Model
|
|
||||||
K string `gorm:"index:uk_k,unique"`
|
|
||||||
V string
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadProperty(name string, defaultValue string) (string, error) {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
var property Property
|
|
||||||
err := db.First(&property, "k", name).Error
|
|
||||||
if err == nil {
|
|
||||||
return property.V, nil
|
|
||||||
}
|
|
||||||
if gorm.ErrRecordNotFound == err {
|
|
||||||
return defaultValue, nil
|
|
||||||
}
|
|
||||||
panic(errors.New("?"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveProperty(name string, value string) error {
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
return db.Clauses(clause.OnConflict{
|
|
||||||
Columns: []clause.Column{{Name: "k"}},
|
|
||||||
DoUpdates: clause.AssignmentColumns([]string{"created_at", "updated_at", "v"}),
|
|
||||||
}).Create(&Property{
|
|
||||||
K: name,
|
|
||||||
V: value,
|
|
||||||
}).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadBoolProperty(name string, defaultValue bool) (bool, error) {
|
|
||||||
stringValue, err := LoadProperty(name, strconv.FormatBool(defaultValue))
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return strconv.ParseBool(stringValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveBoolProperty(name string, value bool) error {
|
|
||||||
return SaveProperty(name, strconv.FormatBool(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
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 SaveProxy(value string) error {
|
|
||||||
return SaveProperty("proxy", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadProxy() (string, error) {
|
|
||||||
return LoadProperty("proxy", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveUsername(value string) error {
|
|
||||||
return SaveProperty("username", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadUsername() (string, error) {
|
|
||||||
return LoadProperty("username", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func SavePassword(value string) error {
|
|
||||||
return SaveProperty("password", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadPassword() (string, error) {
|
|
||||||
return LoadProperty("password", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveToken(value string) {
|
|
||||||
SaveProperty("token", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadToken() (string, error) {
|
|
||||||
return LoadProperty("token", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveTokenTime(value int64) {
|
|
||||||
SaveProperty("token_time", strconv.FormatInt(value, 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadTokenTime() (int64, error) {
|
|
||||||
str, err := LoadProperty("token_time", "0")
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return strconv.ParseInt(str, 10, 64)
|
|
||||||
}
|
|
|
@ -1,498 +0,0 @@
|
||||||
package pikapika
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"image"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
comic_center2 "pikapika/pikapika/database/comic_center"
|
|
||||||
utils2 "pikapika/pikapika/utils"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 使用协程进行后台下载
|
|
||||||
// downloadRunning 如果为false则停止下载
|
|
||||||
// downloadRestart 为true则取消从新启动下载功能
|
|
||||||
|
|
||||||
var downloadThreadCount = 1
|
|
||||||
var downloadThreadFetch = 100
|
|
||||||
|
|
||||||
var downloadRunning = false
|
|
||||||
var downloadRestart = false
|
|
||||||
|
|
||||||
var downloadingComic *comic_center2.ComicDownload
|
|
||||||
var downloadingEp *comic_center2.ComicDownloadEp
|
|
||||||
|
|
||||||
var dlFlag = true
|
|
||||||
|
|
||||||
// 程序启动后仅调用一次, 启动后台线程
|
|
||||||
func downloadBackground() {
|
|
||||||
println("后台线程启动")
|
|
||||||
if dlFlag {
|
|
||||||
dlFlag = false
|
|
||||||
go downloadBegin()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 下载启动/重新启动会暂停三秒
|
|
||||||
func downloadBegin() {
|
|
||||||
time.Sleep(time.Second * 3)
|
|
||||||
go downloadLoadComic()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 下载周期中, 每个下载单元会调用此方法, 如果返回true应该停止当前动作
|
|
||||||
func downloadHasStop() bool {
|
|
||||||
if !downloadRunning {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if downloadRestart {
|
|
||||||
downloadRestart = false
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除下载任务, 当用户要删除下载的时候, 他会被加入删除队列, 而不是直接被删除, 以减少出错
|
|
||||||
func downloadDelete() bool {
|
|
||||||
c, e := comic_center2.DeletingComic()
|
|
||||||
if e != nil {
|
|
||||||
panic(e)
|
|
||||||
}
|
|
||||||
if c != nil {
|
|
||||||
os.RemoveAll(downloadPath(c.ID))
|
|
||||||
e = comic_center2.TrueDelete(c.ID)
|
|
||||||
if e != nil {
|
|
||||||
panic(e)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载第一个需要下载的漫画
|
|
||||||
func downloadLoadComic() {
|
|
||||||
// 每次下载完一个漫画, 或者启动的时候, 首先进行删除任务
|
|
||||||
for downloadDelete() {
|
|
||||||
}
|
|
||||||
// 检测是否需要停止
|
|
||||||
if downloadHasStop() {
|
|
||||||
go downloadBegin()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 找到第一个要下载的漫画, 查库有错误就停止, 因为这些错误很少出现, 一旦出现必然是严重的, 例如数据库文件突然被删除
|
|
||||||
var err error
|
|
||||||
downloadingComic, err = comic_center2.LoadFirstNeedDownload()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// 处理找到的下载任务
|
|
||||||
go downloadInitComic()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化找到的下载任务
|
|
||||||
func downloadInitComic() {
|
|
||||||
// 检测是否需要停止
|
|
||||||
if downloadHasStop() {
|
|
||||||
go downloadBegin()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 若没有漫画要下载则重新启动
|
|
||||||
if downloadingComic == nil {
|
|
||||||
println("没有找到要下载的漫画")
|
|
||||||
go downloadBegin()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 打印日志, 并向前端的eventChannel发送下载信息
|
|
||||||
println("正在下载漫画 " + downloadingComic.Title)
|
|
||||||
downloadComicEventSend(downloadingComic)
|
|
||||||
eps, err := comic_center2.ListDownloadEpByComicId(downloadingComic.ID)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// 找到这个漫画需要下载的EP, 并搜索获取图片地址
|
|
||||||
for _, ep := range eps {
|
|
||||||
// FetchedPictures字段标志着这个章节的图片地址有没有获取过, 如果没有获取过就重新获取
|
|
||||||
if !ep.FetchedPictures {
|
|
||||||
println("正在获取章节的图片 " + downloadingComic.Title + " " + ep.Title)
|
|
||||||
// 搜索图片地址, 如果五次没有请求成功, 就不在请求
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
if client.Token == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err := downloadFetchPictures(&ep)
|
|
||||||
if err != nil {
|
|
||||||
println(err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ep.FetchedPictures = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// 如果未能获取图片地址, 则直接置为失败
|
|
||||||
if !ep.FetchedPictures {
|
|
||||||
println("章节的图片获取失败 " + downloadingComic.Title + " " + ep.Title)
|
|
||||||
err = comic_center2.EpFailed(ep.ID)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println("章节的图片获取成功 " + downloadingComic.Title + " " + ep.Title)
|
|
||||||
downloadingComic.SelectedPictureCount = downloadingComic.SelectedPictureCount + ep.SelectedPictureCount
|
|
||||||
downloadComicEventSend(downloadingComic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获取图片地址结束, 去初始化下载的章节
|
|
||||||
go downloadLoadEp()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取图片地址
|
|
||||||
func downloadFetchPictures(downloadEp *comic_center2.ComicDownloadEp) error {
|
|
||||||
var list []comic_center2.ComicDownloadPicture
|
|
||||||
// 官方的图片只能分页获取, 从第1页开始获取, 每页最多40张图片
|
|
||||||
page := 1
|
|
||||||
for true {
|
|
||||||
rsp, err := client.ComicPicturePage(downloadingComic.ID, int(downloadEp.EpOrder), page)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, doc := range rsp.Docs {
|
|
||||||
list = append(list, comic_center2.ComicDownloadPicture{
|
|
||||||
ID: doc.Id,
|
|
||||||
ComicId: downloadEp.ComicId,
|
|
||||||
EpId: downloadEp.ID,
|
|
||||||
EpOrder: downloadEp.EpOrder,
|
|
||||||
OriginalName: doc.Media.OriginalName,
|
|
||||||
FileServer: doc.Media.FileServer,
|
|
||||||
Path: doc.Media.Path,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// 如果不是最后一页, 页码加1, 获取下一页
|
|
||||||
if rsp.Page < rsp.Pages {
|
|
||||||
page++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// 保存获取到的图片
|
|
||||||
err := comic_center2.FetchPictures(downloadEp.ComicId, downloadEp.ID, &list)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
downloadEp.SelectedPictureCount = int32(len(list))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化下载
|
|
||||||
func downloadLoadEp() {
|
|
||||||
// 周期停止检测
|
|
||||||
if downloadHasStop() {
|
|
||||||
go downloadBegin()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 找到第一个需要下载的章节并去处理 (未下载失败的, 且未完成下载的)
|
|
||||||
var err error
|
|
||||||
downloadingEp, err = comic_center2.LoadFirstNeedDownloadEp(downloadingComic.ID)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
go downloadInitEp()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理需要下载的EP
|
|
||||||
func downloadInitEp() {
|
|
||||||
if downloadingEp == nil {
|
|
||||||
// 所有Ep都下完了, 汇总Download下载情况
|
|
||||||
go downloadSummaryDownload()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 没有下载完则去下载图片
|
|
||||||
println("正在下载章节 " + downloadingEp.Title)
|
|
||||||
go downloadLoadPicture()
|
|
||||||
}
|
|
||||||
|
|
||||||
// EP下载汇总
|
|
||||||
func downloadSummaryDownload() {
|
|
||||||
// 暂停检测
|
|
||||||
if downloadHasStop() {
|
|
||||||
go downloadBegin()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 加载这个漫画的所有EP
|
|
||||||
list, err := comic_center2.ListDownloadEpByComicId(downloadingComic.ID)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// 判断所有章节是否下载完成
|
|
||||||
over := true
|
|
||||||
for _, downloadEp := range list {
|
|
||||||
over = over && downloadEp.DownloadFinished
|
|
||||||
}
|
|
||||||
if over {
|
|
||||||
// 如果所有章节下载完成则下载成功
|
|
||||||
downloadAndExportLogo(downloadingComic)
|
|
||||||
err = comic_center2.DownloadSuccess(downloadingComic.ID)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
downloadingComic.DownloadFinished = true
|
|
||||||
downloadingComic.DownloadFinishedTime = time.Now()
|
|
||||||
} else {
|
|
||||||
// 否则下载失败
|
|
||||||
err = comic_center2.DownloadFailed(downloadingComic.ID)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
downloadingComic.DownloadFailed = true
|
|
||||||
}
|
|
||||||
// 向前端发送下载状态
|
|
||||||
downloadComicEventSend(downloadingComic)
|
|
||||||
// 去下载下一个漫画
|
|
||||||
go downloadLoadComic()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载需要下载的图片
|
|
||||||
func downloadLoadPicture() {
|
|
||||||
// 暂停检测
|
|
||||||
if downloadHasStop() {
|
|
||||||
go downloadBegin()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 获取到这个章节需要下载的图片
|
|
||||||
downloadingPictures, err := comic_center2.LoadNeedDownloadPictures(downloadingEp.ID, downloadThreadFetch)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// 如果不需要下载
|
|
||||||
if len(*downloadingPictures) == 0 {
|
|
||||||
// 所有图片都下完了, 汇总EP下载情况
|
|
||||||
go downloadSummaryEp()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 线程池
|
|
||||||
channel := make(chan int, downloadThreadCount)
|
|
||||||
defer close(channel)
|
|
||||||
wg := sync.WaitGroup{}
|
|
||||||
for i := 0; i < len(*downloadingPictures); i++ {
|
|
||||||
// 暂停检测
|
|
||||||
if downloadHasStop() {
|
|
||||||
wg.Wait()
|
|
||||||
go downloadBegin()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
channel <- 0
|
|
||||||
wg.Add(1)
|
|
||||||
// 不放入携程, 防止i已经变化
|
|
||||||
picPoint := &((*downloadingPictures)[i])
|
|
||||||
go func() {
|
|
||||||
downloadPicture(picPoint)
|
|
||||||
<-channel
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
// 再次新一轮的下载, 直至 len(*downloadingPictures) == 0
|
|
||||||
go downloadLoadPicture()
|
|
||||||
}
|
|
||||||
|
|
||||||
var downloadEventChannelMutex = sync.Mutex{}
|
|
||||||
|
|
||||||
// 这里不能使用暂停检测, 多次检测会导致问题
|
|
||||||
func downloadPicture(downloadingPicture *comic_center2.ComicDownloadPicture) {
|
|
||||||
// 下载图片, 最多重试5次
|
|
||||||
println("正在下载图片 " + fmt.Sprintf("%d", downloadingPicture.RankInEp))
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
err := downloadThePicture(downloadingPicture)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
func() {
|
|
||||||
downloadEventChannelMutex.Lock()
|
|
||||||
defer downloadEventChannelMutex.Unlock()
|
|
||||||
// 对下载的漫画临时变量热更新并通知前端
|
|
||||||
downloadingPicture.DownloadFinished = true
|
|
||||||
downloadingEp.DownloadPictureCount = downloadingEp.DownloadPictureCount + 1
|
|
||||||
downloadingComic.DownloadPictureCount = downloadingComic.DownloadPictureCount + 1
|
|
||||||
downloadComicEventSend(downloadingComic)
|
|
||||||
}()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// 没能下载成功, 图片置为下载失败
|
|
||||||
if !downloadingPicture.DownloadFinished {
|
|
||||||
err := comic_center2.PictureFailed(downloadingPicture.ID)
|
|
||||||
if err != nil {
|
|
||||||
// ??? panic X channel ???
|
|
||||||
// panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 下载指定图片
|
|
||||||
func downloadThePicture(picturePoint *comic_center2.ComicDownloadPicture) error {
|
|
||||||
// 为了不和页面前端浏览的数据冲突, 使用url做hash锁
|
|
||||||
lock := utils2.HashLock(fmt.Sprintf("%s$%s", picturePoint.FileServer, picturePoint.Path))
|
|
||||||
lock.Lock()
|
|
||||||
defer lock.Unlock()
|
|
||||||
// 图片保存位置使用相对路径储存, 使用绝对路径操作
|
|
||||||
picturePath := fmt.Sprintf("%s/%d/%d", picturePoint.ComicId, picturePoint.EpOrder, picturePoint.RankInEp)
|
|
||||||
realPath := downloadPath(picturePath)
|
|
||||||
// 从缓存获取图片
|
|
||||||
buff, img, format, err := decodeFromCache(picturePoint.FileServer, picturePoint.Path)
|
|
||||||
if err != nil {
|
|
||||||
// 若缓存不存在, 则从网络获取
|
|
||||||
buff, img, format, err = decodeFromUrl(picturePoint.FileServer, picturePoint.Path)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 将图片保存到文件
|
|
||||||
dir := filepath.Dir(realPath)
|
|
||||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
|
||||||
os.Mkdir(dir, utils2.CreateDirMode)
|
|
||||||
}
|
|
||||||
err = ioutil.WriteFile(downloadPath(picturePath), buff, utils2.CreateFileMode)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 下载时同时导出
|
|
||||||
downloadAndExport(downloadingComic, downloadingEp, picturePoint, buff, format)
|
|
||||||
// 存入数据库
|
|
||||||
return comic_center2.PictureSuccess(
|
|
||||||
picturePoint.ComicId,
|
|
||||||
picturePoint.EpId,
|
|
||||||
picturePoint.ID,
|
|
||||||
int64(len(buff)),
|
|
||||||
format,
|
|
||||||
int32(img.Bounds().Dx()),
|
|
||||||
int32(img.Bounds().Dy()),
|
|
||||||
picturePath,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EP 下载内容汇总
|
|
||||||
func downloadSummaryEp() {
|
|
||||||
// 暂停检测
|
|
||||||
if downloadHasStop() {
|
|
||||||
go downloadBegin()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 找到所有下载的图片
|
|
||||||
list, err := comic_center2.ListDownloadPictureByEpId(downloadingEp.ID)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// 全部下载完成置为成功, 否则置为失败
|
|
||||||
over := true
|
|
||||||
for _, downloadPicture := range list {
|
|
||||||
over = over && downloadPicture.DownloadFinished
|
|
||||||
}
|
|
||||||
if over {
|
|
||||||
err = comic_center2.EpSuccess(downloadingEp.ComicId, downloadingEp.ID)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = comic_center2.EpFailed(downloadingEp.ID)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 去加载下一个EP
|
|
||||||
go downloadLoadEp()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 边下载边导出(导出路径)
|
|
||||||
var downloadAndExportPath = ""
|
|
||||||
|
|
||||||
// 边下载边导出(导出图片)
|
|
||||||
func downloadAndExport(
|
|
||||||
downloadingComic *comic_center2.ComicDownload,
|
|
||||||
downloadingEp *comic_center2.ComicDownloadEp,
|
|
||||||
downloadingPicture *comic_center2.ComicDownloadPicture,
|
|
||||||
buff []byte,
|
|
||||||
format string,
|
|
||||||
) {
|
|
||||||
if downloadAndExportPath == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if i, e := os.Stat(downloadAndExportPath); e == nil {
|
|
||||||
if i.IsDir() {
|
|
||||||
// 进入漫画目录
|
|
||||||
comicDir := path.Join(downloadAndExportPath, utils2.ReasonableFileName(downloadingComic.Title))
|
|
||||||
i, e = os.Stat(comicDir)
|
|
||||||
if e != nil {
|
|
||||||
if os.IsNotExist(e) {
|
|
||||||
e = os.Mkdir(comicDir, utils2.CreateDirMode)
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if e != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 进入章节目录
|
|
||||||
epDir := path.Join(comicDir, utils2.ReasonableFileName(fmt.Sprintf("%02d - ", downloadingEp.EpOrder)+downloadingEp.Title))
|
|
||||||
i, e = os.Stat(epDir)
|
|
||||||
if e != nil {
|
|
||||||
if os.IsNotExist(e) {
|
|
||||||
e = os.Mkdir(epDir, utils2.CreateDirMode)
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if e != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 写入文件
|
|
||||||
filePath := path.Join(epDir, fmt.Sprintf("%03d.%s", downloadingPicture.RankInEp, aliasFormat(format)))
|
|
||||||
ioutil.WriteFile(filePath, buff, utils2.CreateFileMode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 边下载边导出(导出logo)
|
|
||||||
func downloadAndExportLogo(
|
|
||||||
downloadingComic *comic_center2.ComicDownload,
|
|
||||||
) {
|
|
||||||
if downloadAndExportPath == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
comicLogoPath := downloadPath(path.Join(downloadingComic.ID, "logo"))
|
|
||||||
if _, e := os.Stat(comicLogoPath); e == nil {
|
|
||||||
buff, e := ioutil.ReadFile(comicLogoPath)
|
|
||||||
if e == nil {
|
|
||||||
_, f, e := image.Decode(bytes.NewBuffer(buff))
|
|
||||||
if e == nil {
|
|
||||||
if i, e := os.Stat(downloadAndExportPath); e == nil {
|
|
||||||
if i.IsDir() {
|
|
||||||
// 进入漫画目录
|
|
||||||
comicDir := path.Join(downloadAndExportPath, utils2.ReasonableFileName(downloadingComic.Title))
|
|
||||||
i, e = os.Stat(comicDir)
|
|
||||||
if e != nil {
|
|
||||||
if os.IsNotExist(e) {
|
|
||||||
e = os.Mkdir(comicDir, utils2.CreateDirMode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if e != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 写入文件
|
|
||||||
filePath := path.Join(comicDir, fmt.Sprintf("%s.%s", "logo", aliasFormat(f)))
|
|
||||||
ioutil.WriteFile(filePath, buff, utils2.CreateFileMode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// jpeg的拓展名
|
|
||||||
func aliasFormat(format string) string {
|
|
||||||
if format == "jpeg" {
|
|
||||||
return "jpg"
|
|
||||||
}
|
|
||||||
return format
|
|
||||||
}
|
|
|
@ -1,510 +0,0 @@
|
||||||
package pikapika
|
|
||||||
|
|
||||||
import (
|
|
||||||
"archive/tar"
|
|
||||||
"archive/zip"
|
|
||||||
"compress/gzip"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"pikapika/pikapika/database/comic_center"
|
|
||||||
"pikapika/pikapika/utils"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 通过局域网导出
|
|
||||||
|
|
||||||
var exportingListener net.Listener
|
|
||||||
var exportingConn net.Conn
|
|
||||||
|
|
||||||
func exportComicUsingSocket(comicId string) (int, error) {
|
|
||||||
var err error
|
|
||||||
exportingListener, err = net.Listen("tcp", ":0")
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
go handleExportingConn(comicId)
|
|
||||||
return exportingListener.Addr().(*net.TCPAddr).Port, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleExportingConn(comicId string) {
|
|
||||||
defer exportingListener.Close()
|
|
||||||
var err error
|
|
||||||
exportingConn, err = exportingListener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
notifyExport(fmt.Sprintf("导出失败"))
|
|
||||||
println(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer exportingConn.Close()
|
|
||||||
gw := gzip.NewWriter(exportingConn)
|
|
||||||
defer gw.Close()
|
|
||||||
tw := tar.NewWriter(gw)
|
|
||||||
defer tw.Close()
|
|
||||||
err = exportComicDownloadFetch(comicId, func(path string, size int64) (io.Writer, error) {
|
|
||||||
header := tar.Header{}
|
|
||||||
header.Name = path
|
|
||||||
header.Size = size
|
|
||||||
return tw, tw.WriteHeader(&header)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
notifyExport(fmt.Sprintf("导出失败"))
|
|
||||||
} else {
|
|
||||||
notifyExport(fmt.Sprintf("导出成功"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportComicUsingSocketExit() error {
|
|
||||||
if exportingConn != nil {
|
|
||||||
exportingConn.Close()
|
|
||||||
}
|
|
||||||
if exportingListener != nil {
|
|
||||||
exportingListener.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportComicDownload(params string) (filePath string, err error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
ComicId string `json:"comicId"`
|
|
||||||
Dir string `json:"dir"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
comicId := paramsStruct.ComicId
|
|
||||||
dir := paramsStruct.Dir
|
|
||||||
println(fmt.Sprintf("导出 %s 到 %s", comicId, dir))
|
|
||||||
comic, err := comic_center.FindComicDownloadById(comicId)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if comic == nil {
|
|
||||||
err = errors.New("not found")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !comic.DownloadFinished {
|
|
||||||
err = errors.New("not download finish")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
name := strings.TrimSpace(paramsStruct.Name)
|
|
||||||
if len(name) > 0 {
|
|
||||||
name = utils.ReasonableFileName(name) + ".zip"
|
|
||||||
} else {
|
|
||||||
name = fmt.Sprintf("%s-%s.zip", utils.ReasonableFileName(comic.Title), time.Now().Format("2006_01_02_15_04_05.999"))
|
|
||||||
}
|
|
||||||
filePath = path.Join(dir, name)
|
|
||||||
ex, err := utils.Exists(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if ex {
|
|
||||||
err = errors.New("exists")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
println(fmt.Sprintf("ZIP : %s", filePath))
|
|
||||||
fileStream, err := os.Create(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer fileStream.Close()
|
|
||||||
zipWriter := zip.NewWriter(fileStream)
|
|
||||||
defer zipWriter.Close()
|
|
||||||
err = exportComicDownloadFetch(comicId, func(path string, size int64) (io.Writer, error) {
|
|
||||||
header := tar.Header{}
|
|
||||||
header.Name = path
|
|
||||||
header.Size = size
|
|
||||||
return zipWriter.Create(path)
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportComicDownloadFetch(comicId string, onWriteFile func(path string, size int64) (io.Writer, error)) error {
|
|
||||||
comic, err := comic_center.FindComicDownloadById(comicId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if comic == nil {
|
|
||||||
return errors.New("not found")
|
|
||||||
}
|
|
||||||
if !comic.DownloadFinished {
|
|
||||||
return errors.New("not download finish")
|
|
||||||
}
|
|
||||||
epList, err := comic_center.ListDownloadEpByComicId(comicId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
jsonComic := JsonComicDownload{}
|
|
||||||
jsonComic.ComicDownload = *comic
|
|
||||||
jsonComic.EpList = make([]JsonComicDownloadEp, 0)
|
|
||||||
for _, ep := range epList {
|
|
||||||
jsonEp := JsonComicDownloadEp{}
|
|
||||||
jsonEp.ComicDownloadEp = ep
|
|
||||||
jsonEp.PictureList = make([]JsonComicDownloadPicture, 0)
|
|
||||||
pictures, err := comic_center.ListDownloadPictureByEpId(ep.ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, picture := range pictures {
|
|
||||||
jsonPicture := JsonComicDownloadPicture{}
|
|
||||||
jsonPicture.ComicDownloadPicture = picture
|
|
||||||
jsonPicture.SrcPath = fmt.Sprintf("pictures/%04d_%04d", ep.EpOrder, picture.RankInEp)
|
|
||||||
notifyExport(fmt.Sprintf("正在导出 EP:%d PIC:%d", ep.EpOrder, picture.RankInEp))
|
|
||||||
entryWriter, err := onWriteFile(jsonPicture.SrcPath, jsonPicture.FileSize)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
source, err := os.Open(downloadPath(picture.LocalPath))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = func() (int64, error) {
|
|
||||||
defer source.Close()
|
|
||||||
return io.Copy(entryWriter, source)
|
|
||||||
}()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
jsonEp.PictureList = append(jsonEp.PictureList, jsonPicture)
|
|
||||||
}
|
|
||||||
jsonComic.EpList = append(jsonComic.EpList, jsonEp)
|
|
||||||
}
|
|
||||||
if comic.ThumbLocalPath != "" {
|
|
||||||
logoBuff, err := ioutil.ReadFile(downloadPath(comic.ThumbLocalPath))
|
|
||||||
if err == nil {
|
|
||||||
entryWriter, err := onWriteFile("logo", int64(len(logoBuff)))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = entryWriter.Write(logoBuff)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// JS
|
|
||||||
{
|
|
||||||
buff, err := json.Marshal(&jsonComic)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logoBuff := append([]byte("data = "), buff...)
|
|
||||||
if err == nil {
|
|
||||||
entryWriter, err := onWriteFile("data.js", int64(len(logoBuff)))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = entryWriter.Write(logoBuff)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// HTML
|
|
||||||
{
|
|
||||||
var htmlBuff = []byte(indexHtml)
|
|
||||||
if err == nil {
|
|
||||||
entryWriter, err := onWriteFile("index.html", int64(len(htmlBuff)))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = entryWriter.Write(htmlBuff)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println("OK")
|
|
||||||
//
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const indexHtml = `
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<style>
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#leftNav {
|
|
||||||
position: fixed;
|
|
||||||
width: 350px;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#leftNav > * {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
#leftNav > ul {
|
|
||||||
background: #333;
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#leftNav > #slider {
|
|
||||||
margin-top: 1em;
|
|
||||||
float: right;
|
|
||||||
width: 40px;
|
|
||||||
border: none;
|
|
||||||
background: rgba(0, 0, 0, .4);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
#title > img {
|
|
||||||
display: block;
|
|
||||||
width: 80px;
|
|
||||||
margin: 30px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#title > p {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#title {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#leftNav > ul a {
|
|
||||||
margin: auto;
|
|
||||||
display: block;
|
|
||||||
color: white;
|
|
||||||
height: 40px;
|
|
||||||
line-height: 40px;
|
|
||||||
text-align: center;
|
|
||||||
width: 280px;
|
|
||||||
border-top: #666 solid 1px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#leftNav > ul a:hover,#leftNav > ul a.active {
|
|
||||||
background: rgba(255, 255, 255, .1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#content {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content img {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script src="data.js"></script>
|
|
||||||
<script>
|
|
||||||
function changeLeftNav() {
|
|
||||||
var doc = document.getElementById("leftNav")
|
|
||||||
if (doc.style.left) {
|
|
||||||
doc.style.left = ""
|
|
||||||
} else {
|
|
||||||
doc.style.left = "-300px"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeEp(epIndex) {
|
|
||||||
var ps = data.epList[epIndex].pictureList;
|
|
||||||
document.getElementById('content').innerHTML = "";
|
|
||||||
var d = document.createElement('div');
|
|
||||||
d.id = 'd';
|
|
||||||
document.getElementById('content').append(d);
|
|
||||||
for (var i = 0; i < ps.length; i++) {
|
|
||||||
var img = document.createElement('img');
|
|
||||||
img.src = ps[i].srcPath;
|
|
||||||
document.getElementById('content').append(img);
|
|
||||||
}
|
|
||||||
document.getElementById('d').scrollIntoView();
|
|
||||||
changeLeftNav();
|
|
||||||
var as = document.getElementById('leftNav').getElementsByTagName('a');
|
|
||||||
for (var i = 0; i < ps.length; i++) {
|
|
||||||
if(epIndex == i){
|
|
||||||
as[i].classList = ["active"];
|
|
||||||
}else{
|
|
||||||
as[i].className = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="leftNav">
|
|
||||||
<ul>
|
|
||||||
<li id="title">
|
|
||||||
<script>
|
|
||||||
document.write('<img src="logo" /> <br/>')
|
|
||||||
document.write('<p>' + data.title + '</p>');
|
|
||||||
</script>
|
|
||||||
</li>
|
|
||||||
<script>
|
|
||||||
for (var i = 0; i < data.epList.length; i++) {
|
|
||||||
document.write('<li><a href="javascript:changeEp(' + i + ')">' + data.epList[i].title + '</a></li>');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</ul>
|
|
||||||
<button id="slider" onclick="changeLeftNav();">切换</button>
|
|
||||||
</div>
|
|
||||||
<div id="content">
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`
|
|
||||||
|
|
||||||
func exportComicDownloadToJPG(params string) error {
|
|
||||||
var paramsStruct struct {
|
|
||||||
ComicId string `json:"comicId"`
|
|
||||||
Dir string `json:"dir"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
comicId := paramsStruct.ComicId
|
|
||||||
dir := paramsStruct.Dir
|
|
||||||
println(fmt.Sprintf("导出 %s 到 %s", comicId, dir))
|
|
||||||
comic, err := comic_center.FindComicDownloadById(comicId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if comic == nil {
|
|
||||||
return errors.New("not found")
|
|
||||||
}
|
|
||||||
if !comic.DownloadFinished {
|
|
||||||
return errors.New("not download finish")
|
|
||||||
}
|
|
||||||
name := strings.TrimSpace(paramsStruct.Name)
|
|
||||||
if len(name) > 0 {
|
|
||||||
name = utils.ReasonableFileName(name)
|
|
||||||
} else {
|
|
||||||
name = fmt.Sprintf("%s-%s", utils.ReasonableFileName(comic.Title), time.Now().Format("2006_01_02_15_04_05.999"))
|
|
||||||
}
|
|
||||||
dirPath := path.Join(dir, name)
|
|
||||||
println(fmt.Sprintf("DIR : %s", dirPath))
|
|
||||||
ex, err := utils.Exists(dirPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if ex {
|
|
||||||
return errors.New("exists")
|
|
||||||
}
|
|
||||||
err = os.Mkdir(dirPath, utils.CreateDirMode)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = os.Mkdir(path.Join(dirPath, "pictures"), utils.CreateDirMode)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
epList, err := comic_center.ListDownloadEpByComicId(comicId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
jsonComic := JsonComicDownload{}
|
|
||||||
jsonComic.ComicDownload = *comic
|
|
||||||
jsonComic.EpList = make([]JsonComicDownloadEp, 0)
|
|
||||||
for _, ep := range epList {
|
|
||||||
jsonEp := JsonComicDownloadEp{}
|
|
||||||
jsonEp.ComicDownloadEp = ep
|
|
||||||
jsonEp.PictureList = make([]JsonComicDownloadPicture, 0)
|
|
||||||
pictures, err := comic_center.ListDownloadPictureByEpId(ep.ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, picture := range pictures {
|
|
||||||
jsonPicture := JsonComicDownloadPicture{}
|
|
||||||
jsonPicture.ComicDownloadPicture = picture
|
|
||||||
jsonPicture.SrcPath = fmt.Sprintf("pictures/%04d_%04d.%s", ep.EpOrder, picture.RankInEp, picture.Format)
|
|
||||||
notifyExport(fmt.Sprintf("正在导出 EP:%d PIC:%d", ep.EpOrder, picture.RankInEp))
|
|
||||||
entryWriter, err := os.Create(path.Join(dirPath, jsonPicture.SrcPath))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = func() error {
|
|
||||||
defer entryWriter.Close()
|
|
||||||
source, err := os.Open(downloadPath(picture.LocalPath))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = func() (int64, error) {
|
|
||||||
defer source.Close()
|
|
||||||
return io.Copy(entryWriter, source)
|
|
||||||
}()
|
|
||||||
return err
|
|
||||||
}()
|
|
||||||
jsonEp.PictureList = append(jsonEp.PictureList, jsonPicture)
|
|
||||||
}
|
|
||||||
jsonComic.EpList = append(jsonComic.EpList, jsonEp)
|
|
||||||
}
|
|
||||||
if comic.ThumbLocalPath != "" {
|
|
||||||
logoBuff, err := ioutil.ReadFile(downloadPath(comic.ThumbLocalPath))
|
|
||||||
if err == nil {
|
|
||||||
entryWriter, err := os.Create(path.Join(dirPath, "logo"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer entryWriter.Close()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = entryWriter.Write(logoBuff)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// JS
|
|
||||||
{
|
|
||||||
buff, err := json.Marshal(&jsonComic)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logoBuff := append([]byte("data = "), buff...)
|
|
||||||
if err == nil {
|
|
||||||
|
|
||||||
entryWriter, err := os.Create(path.Join(dirPath, "data.js"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer entryWriter.Close()
|
|
||||||
_, err = entryWriter.Write(logoBuff)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// HTML
|
|
||||||
{
|
|
||||||
var htmlBuff = []byte(indexHtml)
|
|
||||||
if err == nil {
|
|
||||||
entryWriter, err := os.Create(path.Join(dirPath, "index.html"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer entryWriter.Close()
|
|
||||||
_, err = entryWriter.Write(htmlBuff)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println("OK")
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package pikapika
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"github.com/PuerkitoBio/goquery"
|
|
||||||
"net/http"
|
|
||||||
"regexp"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var downloadGameUrlPattern, _ = regexp.Compile("^https://game\\.eroge\\.xyz/hhh\\.php\\?id=\\d+$")
|
|
||||||
|
|
||||||
func downloadGame(url string) (string, error) {
|
|
||||||
if downloadGameUrlPattern.MatchString(url) {
|
|
||||||
return cacheable(fmt.Sprintf("GAME_PAGE$%s", url), time.Hour*1000, func() (interface{}, error) {
|
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Set("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36")
|
|
||||||
rsp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rsp.Body.Close()
|
|
||||||
doc, err := goquery.NewDocumentFromReader(rsp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
find := doc.Find("a.layui-btn")
|
|
||||||
list := make([]string, find.Size())
|
|
||||||
find.Each(func(i int, selection *goquery.Selection) {
|
|
||||||
list[i] = selection.AttrOr("href", "")
|
|
||||||
})
|
|
||||||
return list, nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return "", errors.New("not support url")
|
|
||||||
}
|
|
|
@ -1,130 +0,0 @@
|
||||||
package pikapika
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
_ "golang.org/x/image/webp"
|
|
||||||
"image"
|
|
||||||
_ "image/gif"
|
|
||||||
_ "image/jpeg"
|
|
||||||
_ "image/png"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"pikapika/pikapika/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张图片
|
|
||||||
func takeMutex() *sync.Mutex {
|
|
||||||
busMutex.Lock()
|
|
||||||
defer busMutex.Unlock()
|
|
||||||
mutexCounter = (mutexCounter + 1) % len(subMutexes)
|
|
||||||
return subMutexes[mutexCounter]
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeInfoFromBuff(buff []byte) (image.Image, string, error) {
|
|
||||||
buffer := bytes.NewBuffer(buff)
|
|
||||||
return image.Decode(buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeFromFile(path string) ([]byte, image.Image, string, error) {
|
|
||||||
b, e := ioutil.ReadFile(path)
|
|
||||||
if e != nil {
|
|
||||||
return nil, nil, "", e
|
|
||||||
}
|
|
||||||
i, f, e := decodeInfoFromBuff(b)
|
|
||||||
if e != nil {
|
|
||||||
return nil, nil, "", e
|
|
||||||
}
|
|
||||||
return b, i, f, e
|
|
||||||
}
|
|
||||||
|
|
||||||
// 下载图片并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()
|
|
||||||
request, err := http.NewRequest("GET", fileServer+"/static/"+path, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, "", err
|
|
||||||
}
|
|
||||||
response, err := useClient.Do(request)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, "", err
|
|
||||||
}
|
|
||||||
defer response.Body.Close()
|
|
||||||
if response.StatusCode != 200 {
|
|
||||||
return nil, nil, "", errors.New("code is not 200")
|
|
||||||
}
|
|
||||||
buff, err := ioutil.ReadAll(response.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, "", err
|
|
||||||
}
|
|
||||||
img, format, err := decodeInfoFromBuff(buff)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, "", err
|
|
||||||
}
|
|
||||||
return buff, img, format, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// decodeFromCache 仅下载使用
|
|
||||||
func decodeFromCache(fileServer string, path string) ([]byte, image.Image, string, error) {
|
|
||||||
cache := comic_center.FindRemoteImage(fileServer, path)
|
|
||||||
if cache != nil {
|
|
||||||
buff, err := ioutil.ReadFile(remotePath(cache.LocalPath))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, "", err
|
|
||||||
}
|
|
||||||
img, format, err := decodeInfoFromBuff(buff)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, "", err
|
|
||||||
}
|
|
||||||
return buff, img, format, err
|
|
||||||
}
|
|
||||||
return nil, nil, "", errors.New("not found")
|
|
||||||
}
|
|
|
@ -1,196 +0,0 @@
|
||||||
package pikapika
|
|
||||||
|
|
||||||
import (
|
|
||||||
"archive/tar"
|
|
||||||
"archive/zip"
|
|
||||||
"compress/gzip"
|
|
||||||
"encoding/json"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
path2 "path"
|
|
||||||
"pikapika/pikapika/database/comic_center"
|
|
||||||
"pikapika/pikapika/utils"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func importComicDownloadUsingSocket(addr string) error {
|
|
||||||
//
|
|
||||||
conn, err := net.Dial("tcp", addr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
gr, err := gzip.NewReader(conn)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tr := tar.NewReader(gr)
|
|
||||||
//
|
|
||||||
zipPath := path2.Join(tmpDir, "tmp.zip")
|
|
||||||
closed := false
|
|
||||||
zipFile, err := os.Create(zipPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if !closed {
|
|
||||||
zipFile.Close()
|
|
||||||
}
|
|
||||||
os.Remove(zipPath)
|
|
||||||
}()
|
|
||||||
zipWriter := zip.NewWriter(zipFile)
|
|
||||||
defer func() {
|
|
||||||
if !closed {
|
|
||||||
zipWriter.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
//
|
|
||||||
for {
|
|
||||||
header, err := tr.Next()
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if header.Typeflag != tar.TypeReg {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
writer, err := zipWriter.Create(header.Name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = io.Copy(writer, tr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = zipWriter.Close()
|
|
||||||
zipFile.Close()
|
|
||||||
closed = true
|
|
||||||
return importComicDownload(zipPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func importComicDownload(zipPath string) error {
|
|
||||||
zip, err := zip.OpenReader(zipPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer zip.Close()
|
|
||||||
dataJs, err := zip.Open("data.js")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer dataJs.Close()
|
|
||||||
dataBuff, err := ioutil.ReadAll(dataJs)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data := strings.TrimLeft(string(dataBuff), "data = ")
|
|
||||||
var jsonComicDownload JsonComicDownload
|
|
||||||
err = json.Unmarshal([]byte(data), &jsonComicDownload)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return comic_center.Transaction(func(tx *gorm.DB) error {
|
|
||||||
// 删除
|
|
||||||
err := tx.Unscoped().Delete(&comic_center.ComicDownload{}, "id = ?", jsonComicDownload.ID).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = tx.Unscoped().Delete(&comic_center.ComicDownloadEp{}, "comic_id = ?", jsonComicDownload.ID).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = tx.Unscoped().Delete(&comic_center.ComicDownloadPicture{}, "comic_id = ?", jsonComicDownload.ID).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 插入
|
|
||||||
err = tx.Save(&jsonComicDownload.ComicDownload).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, ep := range jsonComicDownload.EpList {
|
|
||||||
err = tx.Save(&ep.ComicDownloadEp).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, picture := range ep.PictureList {
|
|
||||||
notifyExport("事务 : " + picture.LocalPath)
|
|
||||||
err = tx.Save(&picture.ComicDownloadPicture).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// VIEW日志
|
|
||||||
view := comic_center.ComicView{}
|
|
||||||
view.ID = jsonComicDownload.ID
|
|
||||||
view.CreatedAt = jsonComicDownload.CreatedAt
|
|
||||||
view.UpdatedAt = jsonComicDownload.UpdatedAt
|
|
||||||
view.Title = jsonComicDownload.Title
|
|
||||||
view.Author = jsonComicDownload.Author
|
|
||||||
view.PagesCount = jsonComicDownload.PagesCount
|
|
||||||
view.EpsCount = jsonComicDownload.EpsCount
|
|
||||||
view.Finished = jsonComicDownload.Finished
|
|
||||||
c, _ := json.Marshal(jsonComicDownload.Categories)
|
|
||||||
view.Categories = string(c)
|
|
||||||
view.ThumbOriginalName = jsonComicDownload.ThumbOriginalName
|
|
||||||
view.ThumbFileServer = jsonComicDownload.ThumbFileServer
|
|
||||||
view.ThumbPath = jsonComicDownload.ThumbPath
|
|
||||||
view.LikesCount = 0
|
|
||||||
view.Description = jsonComicDownload.Description
|
|
||||||
view.ChineseTeam = jsonComicDownload.ChineseTeam
|
|
||||||
t, _ := json.Marshal(jsonComicDownload.Tags)
|
|
||||||
view.Tags = string(t)
|
|
||||||
view.AllowDownload = true
|
|
||||||
view.ViewsCount = 0
|
|
||||||
view.IsFavourite = false
|
|
||||||
view.IsLiked = false
|
|
||||||
view.CommentsCount = 0
|
|
||||||
err = comic_center.NoLockActionViewComicUpdateInfoDB(&view, tx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 覆盖文件
|
|
||||||
comicDirPath := downloadPath(jsonComicDownload.ID)
|
|
||||||
utils.Mkdir(comicDirPath)
|
|
||||||
logoReader, err := zip.Open("logo")
|
|
||||||
if err == nil {
|
|
||||||
defer logoReader.Close()
|
|
||||||
logoBuff, err := ioutil.ReadAll(logoReader)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ioutil.WriteFile(path2.Join(comicDirPath, "logo"), logoBuff, utils.CreateFileMode)
|
|
||||||
}
|
|
||||||
for _, ep := range jsonComicDownload.EpList {
|
|
||||||
utils.Mkdir(path2.Join(comicDirPath, strconv.Itoa(int(ep.EpOrder))))
|
|
||||||
for _, picture := range ep.PictureList {
|
|
||||||
notifyExport("写入 : " + picture.LocalPath)
|
|
||||||
zipEntry, err := zip.Open(picture.SrcPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = func() error {
|
|
||||||
defer zipEntry.Close()
|
|
||||||
entryBuff, err := ioutil.ReadAll(zipEntry)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return ioutil.WriteFile(downloadPath(picture.LocalPath), entryBuff, utils.CreateFileMode)
|
|
||||||
}()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 结束
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package pikapika
|
|
||||||
|
|
||||||
import (
|
|
||||||
"pikapika/pikapika/database/comic_center"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DisplayImageData struct {
|
|
||||||
FileSize int64 `json:"fileSize"`
|
|
||||||
Format string `json:"format"`
|
|
||||||
Width int32 `json:"width"`
|
|
||||||
Height int32 `json:"height"`
|
|
||||||
FinalPath string `json:"finalPath"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ComicDownloadPictureWithFinalPath struct {
|
|
||||||
comic_center.ComicDownloadPicture
|
|
||||||
FinalPath string `json:"finalPath"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JsonComicDownload struct {
|
|
||||||
comic_center.ComicDownload
|
|
||||||
EpList []JsonComicDownloadEp `json:"epList"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JsonComicDownloadEp struct {
|
|
||||||
comic_center.ComicDownloadEp
|
|
||||||
PictureList []JsonComicDownloadPicture `json:"pictureList"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JsonComicDownloadPicture struct {
|
|
||||||
comic_center.ComicDownloadPicture
|
|
||||||
SrcPath string `json:"srcPath"`
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package pikapika
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 获取IP的集合
|
|
||||||
func clientIpSet() (string, error) {
|
|
||||||
address, err := net.InterfaceAddrs()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
ipSet := make([]string, 0)
|
|
||||||
for _, address := range address {
|
|
||||||
// 检查ip地址判断是否回环地址
|
|
||||||
if ipNet, ok := address.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
|
|
||||||
if ipNet.IP.To4() != nil {
|
|
||||||
ipSet = append(ipSet, ipNet.IP.To4().String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.Join(ipSet, ","), nil
|
|
||||||
}
|
|
|
@ -1,755 +0,0 @@
|
||||||
package pikapika
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/md5"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
source "github.com/niuhuan/pica-go"
|
|
||||||
"image/jpeg"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
path2 "path"
|
|
||||||
"pikapika/pikapika/database/comic_center"
|
|
||||||
"pikapika/pikapika/database/network_cache"
|
|
||||||
"pikapika/pikapika/database/properties"
|
|
||||||
"pikapika/pikapika/utils"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
remoteDir string
|
|
||||||
downloadDir string
|
|
||||||
tmpDir string
|
|
||||||
)
|
|
||||||
|
|
||||||
var initFlag bool
|
|
||||||
|
|
||||||
func InitPlugin(_remoteDir string, _downloadDir string, _tmpDir string) {
|
|
||||||
if initFlag {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
initFlag = true
|
|
||||||
remoteDir = _remoteDir
|
|
||||||
downloadDir = _downloadDir
|
|
||||||
tmpDir = _tmpDir
|
|
||||||
comic_center.ResetAll()
|
|
||||||
downloadAndExportPath = loadDownloadAndExportPath()
|
|
||||||
downloadThreadCount = loadDownloadThreadCount()
|
|
||||||
go downloadBackground()
|
|
||||||
downloadRunning = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func remotePath(path string) string {
|
|
||||||
return path2.Join(remoteDir, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func downloadPath(path string) string {
|
|
||||||
return path2.Join(downloadDir, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveProperty(params string) error {
|
|
||||||
var paramsStruct struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Value string `json:"value"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
return properties.SaveProperty(paramsStruct.Name, paramsStruct.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadProperty(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
DefaultValue string `json:"defaultValue"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
return properties.LoadProperty(paramsStruct.Name, paramsStruct.DefaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveDownloadAndExportPath(value string) error {
|
|
||||||
err := properties.SaveProperty("downloadAndExportPath", value)
|
|
||||||
if err == nil {
|
|
||||||
downloadAndExportPath = value
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadDownloadAndExportPath() string {
|
|
||||||
p, _ := properties.LoadProperty("downloadAndExportPath", "")
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveDownloadThreadCount(value int) {
|
|
||||||
strValue := strconv.Itoa(value)
|
|
||||||
properties.SaveProperty("downloadThreadCount", strValue)
|
|
||||||
downloadThreadCount = value
|
|
||||||
downloadRestart = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadDownloadThreadCount() int {
|
|
||||||
count, err := properties.LoadProperty("downloadThreadCount", "2")
|
|
||||||
if err != nil {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
i, err := strconv.Atoi(count)
|
|
||||||
if err != nil {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func setSwitchAddress(nSwitchAddress string) error {
|
|
||||||
num, err := strconv.Atoi(nSwitchAddress)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = properties.SaveIntProperty("switchAddress", num)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switchAddress = num
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSwitchAddress() (string, error) {
|
|
||||||
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 {
|
|
||||||
err := properties.SaveProxy(value)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
changeProxyUrl(value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getProxy() (string, error) {
|
|
||||||
return properties.LoadProxy()
|
|
||||||
}
|
|
||||||
|
|
||||||
func setUsername(value string) error {
|
|
||||||
return properties.SaveUsername(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUsername() (string, error) {
|
|
||||||
return properties.LoadUsername()
|
|
||||||
}
|
|
||||||
|
|
||||||
func setPassword(value string) error {
|
|
||||||
return properties.SavePassword(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPassword() (string, error) {
|
|
||||||
return properties.LoadPassword()
|
|
||||||
}
|
|
||||||
|
|
||||||
func preLogin() (string, error) {
|
|
||||||
token, _ := properties.LoadToken()
|
|
||||||
tokenTime, _ := properties.LoadTokenTime()
|
|
||||||
if token != "" && tokenTime > 0 {
|
|
||||||
if utils.Timestamp()-(1000*60*60*24) < tokenTime {
|
|
||||||
client.Token = token
|
|
||||||
return "true", nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err := login()
|
|
||||||
if err == nil {
|
|
||||||
return "true", nil
|
|
||||||
}
|
|
||||||
return "false", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func login() error {
|
|
||||||
username, _ := properties.LoadUsername()
|
|
||||||
password, _ := properties.LoadPassword()
|
|
||||||
if password == "" || username == "" {
|
|
||||||
return errors.New(" 需要设定用户名和密码 ")
|
|
||||||
}
|
|
||||||
err := client.Login(username, password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
properties.SaveToken(client.Token)
|
|
||||||
properties.SaveTokenTime(utils.Timestamp())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func register(params string) error {
|
|
||||||
var dto source.RegisterDto
|
|
||||||
err := json.Unmarshal([]byte(params), &dto)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return client.Register(dto)
|
|
||||||
}
|
|
||||||
|
|
||||||
func clearToken() error {
|
|
||||||
properties.SaveTokenTime(0)
|
|
||||||
properties.SaveToken("")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func remoteImageData(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
FileServer string `json:"fileServer"`
|
|
||||||
Path string `json:"path"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
fileServer := paramsStruct.FileServer
|
|
||||||
path := paramsStruct.Path
|
|
||||||
lock := utils.HashLock(fmt.Sprintf("%s$%s", fileServer, path))
|
|
||||||
lock.Lock()
|
|
||||||
defer lock.Unlock()
|
|
||||||
cache := comic_center.FindRemoteImage(fileServer, path)
|
|
||||||
if cache == nil {
|
|
||||||
remote, err := decodeAndSaveImage(fileServer, path)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
cache = remote
|
|
||||||
} else {
|
|
||||||
go comic_center.UpdateTimeCacheImageTime(cache.ID)
|
|
||||||
}
|
|
||||||
display := DisplayImageData{
|
|
||||||
FileSize: cache.FileSize,
|
|
||||||
Format: cache.Format,
|
|
||||||
Width: cache.Width,
|
|
||||||
Height: cache.Height,
|
|
||||||
FinalPath: remotePath(cache.LocalPath),
|
|
||||||
}
|
|
||||||
return serialize(&display, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func remoteImagePreload(params string) error {
|
|
||||||
var paramsStruct struct {
|
|
||||||
FileServer string `json:"fileServer"`
|
|
||||||
Path string `json:"path"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
fileServer := paramsStruct.FileServer
|
|
||||||
path := paramsStruct.Path
|
|
||||||
lock := utils.HashLock(fmt.Sprintf("%s$%s", fileServer, path))
|
|
||||||
lock.Lock()
|
|
||||||
defer lock.Unlock()
|
|
||||||
cache := comic_center.FindRemoteImage(fileServer, path)
|
|
||||||
var err error
|
|
||||||
if cache == nil {
|
|
||||||
_, err = decodeAndSaveImage(fileServer, path)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeAndSaveImage(fileServer string, path string) (*comic_center.RemoteImage, error) {
|
|
||||||
buff, img, format, err := decodeFromUrl(fileServer, path)
|
|
||||||
if err != nil {
|
|
||||||
println(fmt.Sprintf("decode error : %s/static/%s %s", fileServer, path, err.Error()))
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
local :=
|
|
||||||
fmt.Sprintf("%x",
|
|
||||||
md5.Sum([]byte(fmt.Sprintf("%s$%s", fileServer, path))),
|
|
||||||
)
|
|
||||||
real := remotePath(local)
|
|
||||||
err = ioutil.WriteFile(
|
|
||||||
real,
|
|
||||||
buff, os.FileMode(0600),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
remote := comic_center.RemoteImage{
|
|
||||||
FileServer: fileServer,
|
|
||||||
Path: path,
|
|
||||||
FileSize: int64(len(buff)),
|
|
||||||
Format: format,
|
|
||||||
Width: int32(img.Bounds().Dx()),
|
|
||||||
Height: int32(img.Bounds().Dy()),
|
|
||||||
LocalPath: local,
|
|
||||||
}
|
|
||||||
err = comic_center.SaveRemoteImage(&remote)
|
|
||||||
return &remote, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func downloadImagePath(path string) (string, error) {
|
|
||||||
return downloadPath(path), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createDownload(params string) error {
|
|
||||||
var paramsStruct struct {
|
|
||||||
Comic comic_center.ComicDownload `json:"comic"`
|
|
||||||
EpList []comic_center.ComicDownloadEp `json:"epList"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
comic := paramsStruct.Comic
|
|
||||||
epList := paramsStruct.EpList
|
|
||||||
if comic.Title == "" || len(epList) == 0 {
|
|
||||||
return errors.New("params error")
|
|
||||||
}
|
|
||||||
err := comic_center.CreateDownload(&comic, &epList)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// 创建文件夹
|
|
||||||
utils.Mkdir(downloadPath(comic.ID))
|
|
||||||
// 复制图标
|
|
||||||
downloadComicLogo(&comic)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func downloadComicLogo(comic *comic_center.ComicDownload) {
|
|
||||||
lock := utils.HashLock(fmt.Sprintf("%s$%s", comic.ThumbFileServer, comic.ThumbPath))
|
|
||||||
lock.Lock()
|
|
||||||
defer lock.Unlock()
|
|
||||||
buff, image, format, err := decodeFromCache(comic.ThumbFileServer, comic.ThumbPath)
|
|
||||||
if err != nil {
|
|
||||||
buff, image, format, err = decodeFromUrl(comic.ThumbFileServer, comic.ThumbPath)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
comicLogoPath := path2.Join(comic.ID, "logo")
|
|
||||||
ioutil.WriteFile(downloadPath(comicLogoPath), buff, utils.CreateFileMode)
|
|
||||||
comic_center.UpdateDownloadLogo(
|
|
||||||
comic.ID,
|
|
||||||
int64(len(buff)),
|
|
||||||
format,
|
|
||||||
int32(image.Bounds().Dx()),
|
|
||||||
int32(image.Bounds().Dy()),
|
|
||||||
comicLogoPath,
|
|
||||||
)
|
|
||||||
comic.ThumbFileSize = int64(len(buff))
|
|
||||||
comic.ThumbFormat = format
|
|
||||||
comic.ThumbWidth = int32(image.Bounds().Dx())
|
|
||||||
comic.ThumbHeight = int32(image.Bounds().Dy())
|
|
||||||
comic.ThumbLocalPath = comicLogoPath
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
println(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func addDownload(params string) error {
|
|
||||||
var paramsStruct struct {
|
|
||||||
Comic comic_center.ComicDownload `json:"comic"`
|
|
||||||
EpList []comic_center.ComicDownloadEp `json:"epList"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
comic := paramsStruct.Comic
|
|
||||||
epList := paramsStruct.EpList
|
|
||||||
if comic.Title == "" || len(epList) == 0 {
|
|
||||||
return errors.New("params error")
|
|
||||||
}
|
|
||||||
return comic_center.AddDownload(&comic, &epList)
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteDownloadComic(comicId string) error {
|
|
||||||
err := comic_center.Deleting(comicId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
downloadRestart = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadDownloadComic(comicId string) (string, error) {
|
|
||||||
download, err := comic_center.FindComicDownloadById(comicId)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if download == nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
comic_center.ViewComic(comicId) // VIEW
|
|
||||||
return serialize(download, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func allDownloads() (string, error) {
|
|
||||||
return serialize(comic_center.AllDownloads())
|
|
||||||
}
|
|
||||||
|
|
||||||
func downloadEpList(comicId string) (string, error) {
|
|
||||||
return serialize(comic_center.ListDownloadEpByComicId(comicId))
|
|
||||||
}
|
|
||||||
|
|
||||||
func viewLogPage(params string) (string, error) {
|
|
||||||
var paramsStruct struct {
|
|
||||||
Offset int `json:"offset"`
|
|
||||||
Limit int `json:"limit"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
return serialize(comic_center.ViewLogPage(paramsStruct.Offset, paramsStruct.Limit))
|
|
||||||
}
|
|
||||||
|
|
||||||
func downloadPicturesByEpId(epId string) (string, error) {
|
|
||||||
return serialize(comic_center.ListDownloadPictureByEpId(epId))
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDownloadRunning() bool {
|
|
||||||
return downloadRunning
|
|
||||||
}
|
|
||||||
|
|
||||||
func setDownloadRunning(status bool) {
|
|
||||||
downloadRunning = status
|
|
||||||
}
|
|
||||||
|
|
||||||
func cleanNetworkCache() error {
|
|
||||||
err := network_cache.RemoveAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
notifyExport("清理结束")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func cleanImageCache() error {
|
|
||||||
notifyExport("清理图片缓存")
|
|
||||||
err := comic_center.RemoveAllRemoteImage()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
notifyExport("清理图片文件")
|
|
||||||
os.RemoveAll(remoteDir)
|
|
||||||
utils.Mkdir(remoteDir)
|
|
||||||
notifyExport("清理结束")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func clean() error {
|
|
||||||
var err error
|
|
||||||
notifyExport("清理网络缓存")
|
|
||||||
err = network_cache.RemoveAll()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
notifyExport("清理图片缓存")
|
|
||||||
err = comic_center.RemoveAllRemoteImage()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
notifyExport("清理图片文件")
|
|
||||||
os.RemoveAll(remoteDir)
|
|
||||||
utils.Mkdir(remoteDir)
|
|
||||||
notifyExport("清理结束")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func autoClean(expire int64) error {
|
|
||||||
now := time.Now()
|
|
||||||
earliest := now.Add(time.Second * time.Duration(0-expire))
|
|
||||||
err := network_cache.RemoveEarliest(earliest)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
pageSize := 10
|
|
||||||
for true {
|
|
||||||
images, err := comic_center.EarliestRemoteImage(earliest, pageSize)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(images) == 0 {
|
|
||||||
return comic_center.VACUUM()
|
|
||||||
}
|
|
||||||
// delete data & remove pic
|
|
||||||
err = comic_center.DeleteRemoteImages(images)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for i := 0; i < len(images); i++ {
|
|
||||||
err = os.Remove(remotePath(images[i].LocalPath))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func storeViewEp(params string) error {
|
|
||||||
var paramsStruct struct {
|
|
||||||
ComicId string `json:"comicId"`
|
|
||||||
EpOrder int `json:"epOrder"`
|
|
||||||
EpTitle string `json:"epTitle"`
|
|
||||||
PictureRank int `json:"pictureRank"`
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
return comic_center.ViewEpAndPicture(
|
|
||||||
paramsStruct.ComicId,
|
|
||||||
paramsStruct.EpOrder,
|
|
||||||
paramsStruct.EpTitle,
|
|
||||||
paramsStruct.PictureRank,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadView(comicId string) (string, error) {
|
|
||||||
view, err := comic_center.LoadViewLog(comicId)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
if view != nil {
|
|
||||||
b, err := json.Marshal(view)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(b), nil
|
|
||||||
}
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertImageToJPEG100(params string) error {
|
|
||||||
var paramsStruct struct {
|
|
||||||
Path string `json:"path"`
|
|
||||||
Dir string `json:"dir"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal([]byte(params), ¶msStruct)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, i, _, err := decodeFromFile(paramsStruct.Path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
to := path2.Join(paramsStruct.Dir, path2.Base(paramsStruct.Path)+".jpg")
|
|
||||||
stream, err := os.Create(to)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer stream.Close()
|
|
||||||
return jpeg.Encode(stream, i, &jpeg.Options{Quality: 100})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查更新只能使用defaultHttpClient, 而不能使用pika的client, 否则会 "tls handshake failure"
|
|
||||||
func defaultHttpClientGet(url string) (string, error) {
|
|
||||||
rsp, err := http.DefaultClient.Get(url)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer rsp.Body.Close()
|
|
||||||
buff, err := ioutil.ReadAll(rsp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(buff), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadViewedList(params string) (string, error) {
|
|
||||||
var ids []string
|
|
||||||
err := json.Unmarshal([]byte(params), &ids)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
viewedList := comic_center.ViewedList(ids)
|
|
||||||
ids = make([]string, len(viewedList))
|
|
||||||
for i, view := range viewedList {
|
|
||||||
ids[i] = view.ID
|
|
||||||
}
|
|
||||||
return serialize(ids, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func FlatInvoke(method string, params string) (string, error) {
|
|
||||||
switch method {
|
|
||||||
case "saveProperty":
|
|
||||||
return "", saveProperty(params)
|
|
||||||
case "loadProperty":
|
|
||||||
return loadProperty(params)
|
|
||||||
case "setSwitchAddress":
|
|
||||||
return "", setSwitchAddress(params)
|
|
||||||
case "getSwitchAddress":
|
|
||||||
return getSwitchAddress()
|
|
||||||
case "setImageSwitchAddress":
|
|
||||||
return "", setImageSwitchAddress(params)
|
|
||||||
case "getImageSwitchAddress":
|
|
||||||
return getImageSwitchAddress()
|
|
||||||
case "setProxy":
|
|
||||||
return "", setProxy(params)
|
|
||||||
case "getProxy":
|
|
||||||
return getProxy()
|
|
||||||
case "setUsername":
|
|
||||||
return "", setUsername(params)
|
|
||||||
case "setPassword":
|
|
||||||
return "", setPassword(params)
|
|
||||||
case "getUsername":
|
|
||||||
return getUsername()
|
|
||||||
case "getPassword":
|
|
||||||
return getPassword()
|
|
||||||
case "preLogin":
|
|
||||||
return preLogin()
|
|
||||||
case "login":
|
|
||||||
return "", login()
|
|
||||||
case "register":
|
|
||||||
return "", register(params)
|
|
||||||
case "clearToken":
|
|
||||||
return "", clearToken()
|
|
||||||
case "userProfile":
|
|
||||||
return userProfile()
|
|
||||||
case "punchIn":
|
|
||||||
return punchIn()
|
|
||||||
case "categories":
|
|
||||||
return categories()
|
|
||||||
case "comics":
|
|
||||||
return comics(params)
|
|
||||||
case "searchComics":
|
|
||||||
return searchComics(params)
|
|
||||||
case "randomComics":
|
|
||||||
return randomComics()
|
|
||||||
case "leaderboard":
|
|
||||||
return leaderboard(params)
|
|
||||||
case "comicInfo":
|
|
||||||
return comicInfo(params)
|
|
||||||
case "comicEpPage":
|
|
||||||
return epPage(params)
|
|
||||||
case "comicPicturePageWithQuality":
|
|
||||||
return comicPicturePageWithQuality(params)
|
|
||||||
case "switchLike":
|
|
||||||
return switchLike(params)
|
|
||||||
case "switchFavourite":
|
|
||||||
return switchFavourite(params)
|
|
||||||
case "favouriteComics":
|
|
||||||
return favouriteComics(params)
|
|
||||||
case "recommendation":
|
|
||||||
return recommendation(params)
|
|
||||||
case "comments":
|
|
||||||
return comments(params)
|
|
||||||
case "commentChildren":
|
|
||||||
return commentChildren(params)
|
|
||||||
case "myComments":
|
|
||||||
return myComments(params)
|
|
||||||
case "postComment":
|
|
||||||
return postComment(params)
|
|
||||||
case "postChildComment":
|
|
||||||
return postChildComment(params)
|
|
||||||
case "game":
|
|
||||||
return game(params)
|
|
||||||
case "games":
|
|
||||||
return games(params)
|
|
||||||
case "gameComments":
|
|
||||||
return gameComments(params)
|
|
||||||
case "postGameComment":
|
|
||||||
return postGameComment(params)
|
|
||||||
case "gameCommentChildren":
|
|
||||||
return gameCommentChildren(params)
|
|
||||||
case "switchLikeGameComment":
|
|
||||||
return switchLikeGameComment(params)
|
|
||||||
case "postGameChildComment":
|
|
||||||
return postGameChildComment(params)
|
|
||||||
case "viewLogPage":
|
|
||||||
return viewLogPage(params)
|
|
||||||
case "clearAllViewLog":
|
|
||||||
comic_center.ClearAllViewLog()
|
|
||||||
return "", nil
|
|
||||||
case "deleteViewLog":
|
|
||||||
comic_center.DeleteViewLog(params)
|
|
||||||
return "", nil
|
|
||||||
case "cleanNetworkCache":
|
|
||||||
return "", cleanNetworkCache()
|
|
||||||
case "cleanImageCache":
|
|
||||||
return "", cleanImageCache()
|
|
||||||
case "clean":
|
|
||||||
return "", clean()
|
|
||||||
case "autoClean":
|
|
||||||
expire, err := strconv.ParseInt(params, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return "", autoClean(expire)
|
|
||||||
case "storeViewEp":
|
|
||||||
return "", storeViewEp(params)
|
|
||||||
case "loadView":
|
|
||||||
return loadView(params)
|
|
||||||
case "downloadRunning":
|
|
||||||
return strconv.FormatBool(getDownloadRunning()), nil
|
|
||||||
case "setDownloadRunning":
|
|
||||||
b, e := strconv.ParseBool(params)
|
|
||||||
if e != nil {
|
|
||||||
return "", e
|
|
||||||
}
|
|
||||||
setDownloadRunning(b)
|
|
||||||
return "", nil
|
|
||||||
case "createDownload":
|
|
||||||
return "", createDownload(params)
|
|
||||||
case "addDownload":
|
|
||||||
return "", addDownload(params)
|
|
||||||
case "loadDownloadComic":
|
|
||||||
return loadDownloadComic(params)
|
|
||||||
case "allDownloads":
|
|
||||||
return allDownloads()
|
|
||||||
case "deleteDownloadComic":
|
|
||||||
return "", deleteDownloadComic(params)
|
|
||||||
case "downloadEpList":
|
|
||||||
return downloadEpList(params)
|
|
||||||
case "downloadPicturesByEpId":
|
|
||||||
return downloadPicturesByEpId(params)
|
|
||||||
case "resetAllDownloads":
|
|
||||||
return "", comic_center.ResetAll()
|
|
||||||
case "exportComicDownload":
|
|
||||||
return exportComicDownload(params)
|
|
||||||
case "exportComicDownloadToJPG":
|
|
||||||
return "", exportComicDownloadToJPG(params)
|
|
||||||
case "exportComicUsingSocket":
|
|
||||||
i, e := exportComicUsingSocket(params)
|
|
||||||
return fmt.Sprintf("%d", i), e
|
|
||||||
case "exportComicUsingSocketExit":
|
|
||||||
return "", exportComicUsingSocketExit()
|
|
||||||
case "importComicDownload":
|
|
||||||
return "", importComicDownload(params)
|
|
||||||
case "importComicDownloadUsingSocket":
|
|
||||||
return "", importComicDownloadUsingSocket(params)
|
|
||||||
case "remoteImageData":
|
|
||||||
return remoteImageData(params)
|
|
||||||
case "remoteImagePreload":
|
|
||||||
return "", remoteImagePreload(params)
|
|
||||||
case "clientIpSet":
|
|
||||||
return clientIpSet()
|
|
||||||
case "downloadImagePath":
|
|
||||||
return downloadImagePath(params)
|
|
||||||
case "downloadGame":
|
|
||||||
return downloadGame(params)
|
|
||||||
case "convertImageToJPEG100":
|
|
||||||
return "", convertImageToJPEG100(params)
|
|
||||||
case "loadDownloadAndExportPath":
|
|
||||||
return loadDownloadAndExportPath(), nil
|
|
||||||
case "saveDownloadAndExportPath":
|
|
||||||
return "", saveDownloadAndExportPath(params)
|
|
||||||
case "saveDownloadThreadCount":
|
|
||||||
i, e := strconv.Atoi(params)
|
|
||||||
if e != nil {
|
|
||||||
return "", e
|
|
||||||
}
|
|
||||||
saveDownloadThreadCount(i)
|
|
||||||
return "", nil
|
|
||||||
case "loadDownloadThreadCount":
|
|
||||||
return strconv.Itoa(loadDownloadThreadCount()), nil
|
|
||||||
case "switchLikeComment":
|
|
||||||
return switchLikeComment(params)
|
|
||||||
case "updatePassword":
|
|
||||||
return updatePassword(params)
|
|
||||||
case "updateSlogan":
|
|
||||||
return updateSlogan(params)
|
|
||||||
case "updateAvatar":
|
|
||||||
return updateAvatar(params)
|
|
||||||
case "defaultHttpClientGet":
|
|
||||||
return defaultHttpClientGet(params)
|
|
||||||
case "loadViewedList":
|
|
||||||
return loadViewedList(params)
|
|
||||||
case "collections":
|
|
||||||
return collections(params)
|
|
||||||
}
|
|
||||||
return "", errors.New("method not found : " + method)
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"gorm.io/gorm/logger"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
CreateDirMode = os.FileMode(0700)
|
|
||||||
CreateFileMode = os.FileMode(0600)
|
|
||||||
GormConfig = &gorm.Config{
|
|
||||||
Logger: logger.Default.LogMode(logger.Info),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Mkdir(dir string) {
|
|
||||||
if _, err := os.Stat(dir); err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
err = os.MkdirAll(dir, CreateDirMode)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReasonableFileName(title string) string {
|
|
||||||
title = strings.ReplaceAll(title, "\\", "_")
|
|
||||||
title = strings.ReplaceAll(title, "/", "_")
|
|
||||||
title = strings.ReplaceAll(title, "*", "_")
|
|
||||||
title = strings.ReplaceAll(title, "?", "_")
|
|
||||||
title = strings.ReplaceAll(title, "<", "_")
|
|
||||||
title = strings.ReplaceAll(title, ">", "_")
|
|
||||||
title = strings.ReplaceAll(title, "|", "_")
|
|
||||||
return title
|
|
||||||
}
|
|
||||||
|
|
||||||
func Exists(name string) (bool, error) {
|
|
||||||
_, err := os.Stat(name)
|
|
||||||
if err == nil {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return false, err
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"hash/fnv"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var hashMutex []*sync.Mutex
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
for i := 0; i < 32; i++ {
|
|
||||||
hashMutex = append(hashMutex, &sync.Mutex{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HashLock Hash一样的图片不同时处理
|
|
||||||
func HashLock(key string) *sync.Mutex {
|
|
||||||
hash := fnv.New32()
|
|
||||||
hash.Write([]byte(key))
|
|
||||||
return hashMutex[int(hash.Sum32()%uint32(len(hashMutex)))]
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
package utils
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// Timestamp 获取当前的Unix时间戳
|
|
||||||
func Timestamp() int64 {
|
|
||||||
return time.Now().UnixNano() / int64(time.Millisecond)
|
|
||||||
}
|
|
|
@ -285,7 +285,7 @@ class Method {
|
||||||
return ComicsPage.fromJson(json.decode(rsp));
|
return ComicsPage.fromJson(json.decode(rsp));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 看了此漫画的人还看了...(此接口似乎失效了)
|
/// 看了此漫画的人还看了...
|
||||||
Future<List<ComicSimple>> recommendation(String comicId) async {
|
Future<List<ComicSimple>> recommendation(String comicId) async {
|
||||||
String rsp = await _flatInvoke("recommendation", comicId);
|
String rsp = await _flatInvoke("recommendation", comicId);
|
||||||
List list = json.decode(rsp);
|
List list = json.decode(rsp);
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:pikapika/basic/Navigator.dart';
|
||||||
import 'package:pikapika/screens/ComicsScreen.dart';
|
import 'package:pikapika/screens/ComicsScreen.dart';
|
||||||
import 'package:pikapika/screens/components/CommentMainType.dart';
|
import 'package:pikapika/screens/components/CommentMainType.dart';
|
||||||
import 'package:pikapika/screens/components/ItemBuilder.dart';
|
import 'package:pikapika/screens/components/ItemBuilder.dart';
|
||||||
|
import 'package:pikapika/screens/components/Recommendation.dart';
|
||||||
|
|
||||||
import 'ComicReaderScreen.dart';
|
import 'ComicReaderScreen.dart';
|
||||||
import 'DownloadConfirmScreen.dart';
|
import 'DownloadConfirmScreen.dart';
|
||||||
|
@ -111,10 +112,12 @@ class _ComicInfoScreenState extends State<ComicInfoScreen> with RouteAware {
|
||||||
var _tabs = <Widget>[
|
var _tabs = <Widget>[
|
||||||
Tab(text: '章节 (${_comicInfo.epsCount})'),
|
Tab(text: '章节 (${_comicInfo.epsCount})'),
|
||||||
Tab(text: '评论 (${_comicInfo.commentsCount})'),
|
Tab(text: '评论 (${_comicInfo.commentsCount})'),
|
||||||
|
const Tab(text: '推荐'),
|
||||||
];
|
];
|
||||||
var _views = <Widget>[
|
var _views = <Widget>[
|
||||||
_buildEpWrap(_epListFuture, _comicInfo),
|
_buildEpWrap(_epListFuture, _comicInfo),
|
||||||
CommentList(CommentMainType.COMIC, _comicInfo.id),
|
CommentList(CommentMainType.COMIC, _comicInfo.id),
|
||||||
|
Recommendation(comicId: _comicInfo.id),
|
||||||
];
|
];
|
||||||
return DefaultTabController(
|
return DefaultTabController(
|
||||||
length: _tabs.length,
|
length: _tabs.length,
|
||||||
|
|
|
@ -7,7 +7,6 @@ import 'ItemBuilder.dart';
|
||||||
import 'Images.dart';
|
import 'Images.dart';
|
||||||
|
|
||||||
// 看过此本子的也在看
|
// 看过此本子的也在看
|
||||||
// 一直返回空数组, 所以没有使用
|
|
||||||
class Recommendation extends StatefulWidget {
|
class Recommendation extends StatefulWidget {
|
||||||
final String comicId;
|
final String comicId;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue