🔧 chore: 初始化番茄小说 API 项目结构
新增项目基础框架,包含核心 API 实现、算法模块、HTTP 处理器等完整功能模块
This commit is contained in:
commit
6981148727
107 files changed
+12463
No files matched your search
@@ -0,0 +1,90 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fanqie/internal/fqapi"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// HandleSearch 搜索处理
|
||||
func HandleSearch(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementSearch()
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
query := r.URL.Query().Get("query")
|
||||
if query == "" {
|
||||
query = r.URL.Query().Get("key")
|
||||
}
|
||||
|
||||
offsetStr := r.URL.Query().Get("offset")
|
||||
offset := 0
|
||||
if offsetStr != "" {
|
||||
offset, _ = strconv.Atoi(offsetStr)
|
||||
}
|
||||
|
||||
tabTypeStr := r.URL.Query().Get("tab_type")
|
||||
tabType := 3
|
||||
if tabTypeStr != "" {
|
||||
tabType, _ = strconv.Atoi(tabTypeStr)
|
||||
}
|
||||
|
||||
result, err := fqapi.HandleBookSearch(query, offset, tabType)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, `{"code": -1, "msg": "搜索失败: %s"}`, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprint(w, result)
|
||||
}
|
||||
|
||||
// HandleDetail 详情处理
|
||||
func HandleDetail(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementDetail()
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
bookID := r.URL.Query().Get("book_id")
|
||||
|
||||
result, err := fqapi.HandleGetDetail(bookID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, `{"code": -1, "msg": "获取详情失败: %s"}`, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprint(w, result)
|
||||
}
|
||||
|
||||
// HandleCatalog 目录处理
|
||||
func HandleCatalog(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementCatalog()
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
bookID := r.URL.Query().Get("book_id")
|
||||
|
||||
result, err := fqapi.HandleGetCatalog(bookID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, `{"code": -1, "msg": "获取目录失败: %s"}`, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprint(w, result)
|
||||
}
|
||||
|
||||
// HandleMultiDetail 批量详情处理
|
||||
func HandleMultiDetail(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
bookID := r.URL.Query().Get("book_id")
|
||||
|
||||
result, err := fqapi.HandleGetMultiDetail(bookID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, `{"code": -1, "msg": "批量获取详情失败: %s"}`, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprint(w, result)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fanqie/internal/fqapi"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// HandleCategoryFront 分类列表处理
|
||||
func HandleCategoryFront(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementCategory()
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
tab := r.URL.Query().Get("tab")
|
||||
|
||||
result, err := fqapi.HandleCategoryFront(tab)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, `{"code": -1, "msg": "获取分类列表失败: %s"}`, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprint(w, result)
|
||||
}
|
||||
|
||||
// HandleCategoryLanding 分类详情处理
|
||||
func HandleCategoryLanding(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementCategory()
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
categoryID := r.URL.Query().Get("category_id")
|
||||
offset := r.URL.Query().Get("offset")
|
||||
wordNumber := r.URL.Query().Get("word_number")
|
||||
bookStatus := r.URL.Query().Get("book_status")
|
||||
sortBy := r.URL.Query().Get("sort_by")
|
||||
|
||||
result, err := fqapi.HandleCategoryLanding(categoryID, offset, wordNumber, bookStatus, sortBy)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, `{"code": -1, "msg": "获取分类详情失败: %s"}`, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprint(w, result)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fanqie/internal/config"
|
||||
"fanqie/internal/fqapi"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
startTime = time.Now()
|
||||
deviceHeader string
|
||||
|
||||
// 请求统计
|
||||
totalRequests int64
|
||||
searchRequests int64
|
||||
detailRequests int64
|
||||
catalogRequests int64
|
||||
contentRequests int64
|
||||
mangaRequests int64
|
||||
audioRequests int64
|
||||
videoRequests int64
|
||||
categoryRequests int64
|
||||
|
||||
// 统计保存定时器
|
||||
statsSaveTicker *time.Ticker
|
||||
)
|
||||
|
||||
// 统计函数
|
||||
func incrementTotal() { atomic.AddInt64(&totalRequests, 1) }
|
||||
func incrementSearch() { atomic.AddInt64(&searchRequests, 1) }
|
||||
func incrementDetail() { atomic.AddInt64(&detailRequests, 1) }
|
||||
func incrementCatalog() { atomic.AddInt64(&catalogRequests, 1) }
|
||||
func incrementContent() { atomic.AddInt64(&contentRequests, 1) }
|
||||
func incrementManga() { atomic.AddInt64(&mangaRequests, 1) }
|
||||
func incrementAudio() { atomic.AddInt64(&audioRequests, 1) }
|
||||
func incrementVideo() { atomic.AddInt64(&videoRequests, 1) }
|
||||
func incrementCategory() { atomic.AddInt64(&categoryRequests, 1) }
|
||||
|
||||
func getTotal() int64 { return atomic.LoadInt64(&totalRequests) }
|
||||
func getSearch() int64 { return atomic.LoadInt64(&searchRequests) }
|
||||
func getDetail() int64 { return atomic.LoadInt64(&detailRequests) }
|
||||
func getCatalog() int64 { return atomic.LoadInt64(&catalogRequests) }
|
||||
func getContent() int64 { return atomic.LoadInt64(&contentRequests) }
|
||||
func getManga() int64 { return atomic.LoadInt64(&mangaRequests) }
|
||||
func getAudio() int64 { return atomic.LoadInt64(&audioRequests) }
|
||||
func getVideo() int64 { return atomic.LoadInt64(&videoRequests) }
|
||||
func getCategory() int64 { return atomic.LoadInt64(&categoryRequests) }
|
||||
|
||||
// SetDeviceHeader 设置设备header
|
||||
func SetDeviceHeader(header string) {
|
||||
deviceHeader = header
|
||||
}
|
||||
|
||||
// LoadStatistics 从数据库加载统计数据
|
||||
func LoadStatistics() {
|
||||
stats, err := fqapi.LoadStatistics()
|
||||
if err != nil {
|
||||
fmt.Printf("加载统计数据失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
atomic.StoreInt64(&totalRequests, stats["total"])
|
||||
atomic.StoreInt64(&searchRequests, stats["search"])
|
||||
atomic.StoreInt64(&detailRequests, stats["detail"])
|
||||
atomic.StoreInt64(&catalogRequests, stats["catalog"])
|
||||
atomic.StoreInt64(&contentRequests, stats["content"])
|
||||
atomic.StoreInt64(&mangaRequests, stats["manga"])
|
||||
atomic.StoreInt64(&audioRequests, stats["audio"])
|
||||
atomic.StoreInt64(&videoRequests, stats["video"])
|
||||
atomic.StoreInt64(&categoryRequests, stats["category"])
|
||||
}
|
||||
|
||||
// SaveStatistics 保存统计数据到数据库
|
||||
func SaveStatistics() {
|
||||
stats := map[string]int64{
|
||||
"total": atomic.LoadInt64(&totalRequests),
|
||||
"search": atomic.LoadInt64(&searchRequests),
|
||||
"detail": atomic.LoadInt64(&detailRequests),
|
||||
"catalog": atomic.LoadInt64(&catalogRequests),
|
||||
"content": atomic.LoadInt64(&contentRequests),
|
||||
"manga": atomic.LoadInt64(&mangaRequests),
|
||||
"audio": atomic.LoadInt64(&audioRequests),
|
||||
"video": atomic.LoadInt64(&videoRequests),
|
||||
"category": atomic.LoadInt64(&categoryRequests),
|
||||
}
|
||||
|
||||
if err := fqapi.SaveStatistics(stats); err != nil {
|
||||
fmt.Printf("保存统计数据失败: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// StartStatisticsSaver 启动统计数据定期保存
|
||||
func StartStatisticsSaver() {
|
||||
// 每30秒保存一次统计数据
|
||||
statsSaveTicker = time.NewTicker(30 * time.Second)
|
||||
go func() {
|
||||
for range statsSaveTicker.C {
|
||||
SaveStatistics()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// StopStatisticsSaver 停止统计数据定期保存
|
||||
func StopStatisticsSaver() {
|
||||
if statsSaveTicker != nil {
|
||||
statsSaveTicker.Stop()
|
||||
// 最后保存一次
|
||||
SaveStatistics()
|
||||
}
|
||||
}
|
||||
|
||||
// RequireAccessKey 验证访问密钥中间件
|
||||
func RequireAccessKey(handler http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
cfg := config.GetConfig()
|
||||
|
||||
// 如果未配置 access_key 或为空,则不启用验证
|
||||
if cfg == nil || cfg.Server.AccessKey == "" {
|
||||
handler(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// 从查询参数或 Header 中获取 key
|
||||
key := r.URL.Query().Get("key")
|
||||
if key == "" {
|
||||
key = r.Header.Get("X-Access-Key")
|
||||
}
|
||||
|
||||
// 验证密钥
|
||||
if key != cfg.Server.AccessKey {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
fmt.Fprintf(w, `{"code": -1, "msg": "访问被拒绝:无效的访问密钥"}`)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证通过,继续处理请求
|
||||
handler(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleMain 主页处理
|
||||
func HandleMain(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
response := map[string]interface{}{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": nil,
|
||||
}
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// HandleSelfInfo 设备信息处理
|
||||
func HandleSelfInfo(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
// 获取设备池统计信息
|
||||
pool := fqapi.GetDeviceKeyPool()
|
||||
poolStats := pool.GetStats()
|
||||
|
||||
response := map[string]interface{}{
|
||||
"code": 0,
|
||||
"message": "SUCCESS",
|
||||
"data": map[string]interface{}{
|
||||
"device_pool_enabled": pool.IsEnabled(),
|
||||
"device_pool_stats": poolStats,
|
||||
},
|
||||
}
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fanqie/internal/config"
|
||||
"fanqie/internal/fqapi"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// HandleContent 内容处理
|
||||
func HandleContent(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementContent()
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
// 从查询参数获取item_id
|
||||
itemID := r.URL.Query().Get("item_id")
|
||||
|
||||
// 获取URL参数(支持动态覆盖配置)
|
||||
textMode := r.URL.Query().Get("text_mode") // 1=纯文本模式
|
||||
imageMode := r.URL.Query().Get("image_mode") // 0=移除图片, 1=保留图片
|
||||
reviewParam := r.URL.Query().Get("review") // 1=启用段评, 0=禁用段评
|
||||
|
||||
result, err := fqapi.HandleGetContent(itemID, textMode, imageMode)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, `{"code": -1, "msg": "获取内容失败: %s"}`, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 判断是否启用段评功能
|
||||
// 优先使用URL参数,如果没有则使用配置文件
|
||||
enableReview := false
|
||||
if reviewParam == "1" {
|
||||
enableReview = true
|
||||
} else if reviewParam == "0" {
|
||||
enableReview = false
|
||||
} else {
|
||||
// 没有URL参数时,使用配置文件
|
||||
cfg := config.GetConfig()
|
||||
if cfg != nil {
|
||||
enableReview = cfg.Fanqie.Content.EnableReview
|
||||
}
|
||||
}
|
||||
|
||||
// 如果启用了段评功能,处理正文内容添加段评标记
|
||||
if enableReview {
|
||||
result = addReviewToContent(result, itemID, r)
|
||||
}
|
||||
|
||||
fmt.Fprint(w, result)
|
||||
}
|
||||
|
||||
// HandleMultiContent 批量内容处理
|
||||
func HandleMultiContent(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
bookID := r.URL.Query().Get("book_id")
|
||||
itemIDs := r.URL.Query().Get("item_ids")
|
||||
textMode := r.URL.Query().Get("text_mode")
|
||||
imageMode := r.URL.Query().Get("image_mode")
|
||||
ignoreFailed := r.URL.Query().Get("ignore_failed")
|
||||
|
||||
result, err := fqapi.HandleMultiContent(itemIDs, bookID, textMode, imageMode, ignoreFailed)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, `{"code": -1, "msg": "批量获取内容失败: %s"}`, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprint(w, result)
|
||||
}
|
||||
|
||||
// HandleItemSummary 章节摘要处理
|
||||
func HandleItemSummary(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
bookID := r.URL.Query().Get("book_id")
|
||||
itemIDs := r.URL.Query().Get("item_ids")
|
||||
|
||||
result, err := fqapi.HandleItemSummary(itemIDs, bookID)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, `{"code": -1, "msg": "获取章节摘要失败: %s"}`, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprint(w, result)
|
||||
}
|
||||
|
||||
// addReviewToContent 为正文内容添加段评标记
|
||||
func addReviewToContent(jsonResult, itemID string, r *http.Request) string {
|
||||
// 解析 JSON 结果
|
||||
var resultData map[string]interface{}
|
||||
if err := fqapi.ParseJSON([]byte(jsonResult), &resultData); err != nil {
|
||||
return jsonResult
|
||||
}
|
||||
|
||||
// 获取 data 字段
|
||||
data, ok := resultData["data"].(map[string]interface{})
|
||||
if !ok {
|
||||
return jsonResult
|
||||
}
|
||||
|
||||
// 获取 content 字段
|
||||
content, ok := data["content"].(string)
|
||||
if !ok || content == "" {
|
||||
return jsonResult
|
||||
}
|
||||
|
||||
// 从响应数据中提取 book_id (data.novel_data.book_id)
|
||||
bookID := ""
|
||||
if novelData, ok := data["novel_data"].(map[string]interface{}); ok {
|
||||
if bookIDVal, ok := novelData["book_id"].(string); ok {
|
||||
bookID = bookIDVal
|
||||
} else if bookIDVal, ok := novelData["book_id"].(float64); ok {
|
||||
bookID = fmt.Sprintf("%.0f", bookIDVal)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取基础 URL
|
||||
cfg := config.GetConfig()
|
||||
baseURL := ""
|
||||
if cfg != nil {
|
||||
host := cfg.Server.Host
|
||||
if host == "0.0.0.0" {
|
||||
host = "localhost"
|
||||
}
|
||||
port := cfg.Server.Port
|
||||
scheme := "http"
|
||||
|
||||
// 检查是否来自外部请求
|
||||
if r.Host != "" {
|
||||
baseURL = scheme + "://" + r.Host
|
||||
} else {
|
||||
baseURL = scheme + "://" + host + ":" + port
|
||||
}
|
||||
}
|
||||
|
||||
// 生成带段评标记的内容
|
||||
newContent, err := fqapi.GenerateParagraphsWithComments(itemID, bookID, content, baseURL)
|
||||
if err != nil {
|
||||
// 如果失败,返回原始内容
|
||||
return jsonResult
|
||||
}
|
||||
|
||||
// 替换 content 字段
|
||||
data["content"] = newContent
|
||||
resultData["data"] = data
|
||||
|
||||
// 重新序列化为 JSON
|
||||
newJSON, err := fqapi.ToJSON(resultData)
|
||||
if err != nil {
|
||||
return jsonResult
|
||||
}
|
||||
|
||||
return newJSON
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fanqie/internal/fqapi"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HandleMangaImageProxy 处理漫画图片代理请求
|
||||
func HandleMangaImageProxy(w http.ResponseWriter, r *http.Request) {
|
||||
// 从URL路径提取参数: /manga/image/{itemID}/{index}
|
||||
path := strings.TrimPrefix(r.URL.Path, "/manga/image/")
|
||||
parts := strings.Split(path, "/")
|
||||
|
||||
if len(parts) != 2 {
|
||||
http.Error(w, "Invalid URL format. Expected: /manga/image/{itemID}/{index}", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
itemID := parts[0]
|
||||
index, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid index parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取解密后的图片数据
|
||||
imageData, mimeType, err := fqapi.HandleMangaImageProxy(itemID, index)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to get image: %s", err.Error()), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// 设置响应头
|
||||
w.Header().Set("Content-Type", mimeType)
|
||||
w.Header().Set("Cache-Control", "public, max-age=3600") // 缓存1小时
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(imageData)))
|
||||
|
||||
// 返回图片数据
|
||||
w.Write(imageData)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fanqie/internal/fqapi"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HandleAudio 音频处理
|
||||
func HandleAudio(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementAudio()
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
itemIDs := r.URL.Query().Get("item_ids")
|
||||
toneID := r.URL.Query().Get("tone_id")
|
||||
output := r.URL.Query().Get("output")
|
||||
|
||||
result, err := fqapi.HandleAudio(itemIDs, toneID, output)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, `{"code": -1, "msg": "获取音频失败: %s"}`, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprint(w, result)
|
||||
}
|
||||
|
||||
// HandleAudioProxy 音频代理处理
|
||||
func HandleAudioProxy(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
|
||||
// 从URL路径提取key: /audio/proxy/{key}
|
||||
path := r.URL.Path
|
||||
key := strings.TrimPrefix(path, "/audio/proxy/")
|
||||
if key == "" || key == path {
|
||||
http.Error(w, "缺少音频key", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// 调用代理处理函数
|
||||
data, contentType, err := fqapi.HandleAudioProxy(key)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// 设置正确的Content-Type
|
||||
if contentType != "" {
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
} else {
|
||||
w.Header().Set("Content-Type", "audio/mpeg")
|
||||
}
|
||||
|
||||
// 直接播放,不强制下载
|
||||
// 浏览器会根据Content-Type决定是播放还是下载
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
// HandleVideo 视频处理
|
||||
func HandleVideo(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementVideo()
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
itemIDs := r.URL.Query().Get("item_ids")
|
||||
|
||||
result, err := fqapi.HandleVideo(itemIDs)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, `{"code": -1, "msg": "获取视频失败: %s"}`, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprint(w, result)
|
||||
}
|
||||
|
||||
// HandleManga 漫画处理
|
||||
func HandleManga(w http.ResponseWriter, r *http.Request) {
|
||||
incrementTotal()
|
||||
incrementManga()
|
||||
itemID := r.URL.Query().Get("item_ids")
|
||||
if itemID == "" {
|
||||
itemID = r.URL.Query().Get("item_id")
|
||||
}
|
||||
|
||||
if itemID == "" {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
fmt.Fprint(w, `{"code": -1, "msg": "缺少item_ids参数"}`)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否要返回HTML格式
|
||||
showHTML := r.URL.Query().Get("show_html") == "1"
|
||||
|
||||
// 检查输出模式参数(优先级高于配置文件)
|
||||
// 支持:output=url / output=proxy / output=data_uri
|
||||
outputMode := r.URL.Query().Get("output")
|
||||
|
||||
result, err := fqapi.HandleGetManga(itemID, showHTML, outputMode)
|
||||
if err != nil {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
fmt.Fprintf(w, `{"code": -1, "msg": "获取漫画失败: %s"}`, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if showHTML {
|
||||
// 返回HTML格式
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if contentMap, ok := result.(map[string]interface{}); ok {
|
||||
if content, ok := contentMap["content"].(string); ok {
|
||||
fmt.Fprint(w, content)
|
||||
return
|
||||
}
|
||||
}
|
||||
fmt.Fprint(w, `<p>未找到漫画内容</p>`)
|
||||
} else {
|
||||
// 返回JSON格式
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
jsonData, err := json.Marshal(map[string]interface{}{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": result,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, `{"code": -1, "msg": "JSON编码失败: %s"}`, err.Error())
|
||||
return
|
||||
}
|
||||
fmt.Fprint(w, string(jsonData))
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user