fix(wechatdb): hardlink v4 & init db (#216)

This commit is contained in:
Sarv
2025-08-24 23:48:21 +08:00
committed by GitHub
parent cf47d1bdc5
commit 9c221e6e0d
8 changed files with 92 additions and 37 deletions
+25 -4
View File
@@ -307,10 +307,9 @@ func (s *Service) handleMedia(c *gin.Context, _type string) {
var _err error
for _, k := range keys {
if len(k) != 32 {
absolutePath := filepath.Join(s.conf.GetDataDir(), k)
if _, err := os.Stat(absolutePath); err == nil {
c.Redirect(http.StatusFound, "/data/"+k)
if strings.Contains(k, "/") {
if absolutePath, err := s.findPath(_type, k); err == nil {
c.Redirect(http.StatusFound, "/data/"+absolutePath)
return
}
}
@@ -339,6 +338,28 @@ func (s *Service) handleMedia(c *gin.Context, _type string) {
}
}
func (s *Service) findPath(_type string, key string) (string, error) {
absolutePath := filepath.Join(s.conf.GetDataDir(), key)
if _, err := os.Stat(absolutePath); err == nil {
return key, nil
}
switch _type {
case "image":
for _, suffix := range []string{"_h.dat", ".dat", "_t.dat"} {
if _, err := os.Stat(absolutePath + suffix); err == nil {
return key + suffix, nil
}
}
case "video":
for _, suffix := range []string{".mp4", "_thumb.jpg"} {
if _, err := os.Stat(absolutePath + suffix); err == nil {
return key + suffix, nil
}
}
}
return "", errors.ErrMediaNotFound
}
func (s *Service) handleMediaData(c *gin.Context) {
relativePath := filepath.Clean(c.Param("path"))
+4
View File
@@ -118,3 +118,7 @@ func Err(c *gin.Context, err error) {
c.JSON(http.StatusInternalServerError, err.Error())
}
func Is(err, target error) bool {
return errors.Is(err, target)
}
+6 -16
View File
@@ -245,14 +245,9 @@ func (m *Message) PlainTextContent() string {
keylist = append(keylist, md5)
}
}
if m.Contents["imgfile"] != nil {
if imgfile, ok := m.Contents["imgfile"].(string); ok {
keylist = append(keylist, imgfile)
}
}
if m.Contents["thumb"] != nil {
if thumb, ok := m.Contents["thumb"].(string); ok {
keylist = append(keylist, thumb)
if m.Contents["path"] != nil {
if path, ok := m.Contents["path"].(string); ok {
keylist = append(keylist, path)
}
}
return fmt.Sprintf("![图片](http://%s/image/%s)", m.Contents["host"], strings.Join(keylist, ","))
@@ -275,14 +270,9 @@ func (m *Message) PlainTextContent() string {
keylist = append(keylist, rawmd5)
}
}
if m.Contents["videofile"] != nil {
if videofile, ok := m.Contents["videofile"].(string); ok {
keylist = append(keylist, videofile)
}
}
if m.Contents["thumb"] != nil {
if thumb, ok := m.Contents["thumb"].(string); ok {
keylist = append(keylist, thumb)
if m.Contents["path"] != nil {
if path, ok := m.Contents["path"].(string); ok {
keylist = append(keylist, path)
}
}
return fmt.Sprintf("![视频](http://%s/video/%s)", m.Contents["host"], strings.Join(keylist, ","))
+2 -4
View File
@@ -90,12 +90,10 @@ func (m *MessageV4) Wrap(talker string) *Message {
if _m.Type == 3 && packedInfo.Image != nil {
_talkerMd5Bytes := md5.Sum([]byte(talker))
talkerMd5 := hex.EncodeToString(_talkerMd5Bytes[:])
_m.Contents["imgfile"] = filepath.Join("msg", "attach", talkerMd5, _m.Time.Format("2006-01"), "Img", fmt.Sprintf("%s.dat", packedInfo.Image.Md5))
_m.Contents["thumb"] = filepath.Join("msg", "attach", talkerMd5, _m.Time.Format("2006-01"), "Img", fmt.Sprintf("%s_t.dat", packedInfo.Image.Md5))
_m.Contents["path"] = filepath.Join("msg", "attach", talkerMd5, _m.Time.Format("2006-01"), "Img", packedInfo.Image.Md5)
}
if _m.Type == 43 && packedInfo.Video != nil {
_m.Contents["videofile"] = filepath.Join("msg", "video", _m.Time.Format("2006-01"), fmt.Sprintf("%s.mp4", packedInfo.Video.Md5))
_m.Contents["thumb"] = filepath.Join("msg", "video", _m.Time.Format("2006-01"), fmt.Sprintf("%s_thumb.jpg", packedInfo.Video.Md5))
_m.Contents["path"] = filepath.Join("msg", "video", _m.Time.Format("2006-01"), packedInfo.Video.Md5)
}
}
}
+32 -2
View File
@@ -162,6 +162,10 @@ func (ds *DataSource) initMessageDbs() error {
infos[i].EndTime = infos[i+1].StartTime
}
}
if len(ds.messageInfos) > 0 && len(infos) < len(ds.messageInfos) {
log.Warn().Msgf("message db count decreased from %d to %d, skip init", len(ds.messageInfos), len(infos))
return nil
}
ds.messageInfos = infos
return nil
}
@@ -603,10 +607,20 @@ func (ds *DataSource) GetMedia(ctx context.Context, _type string, key string) (*
switch _type {
case "image":
table = "image_hardlink_info_v3"
// 4.1.0 版本开始使用 v4 表
if !ds.IsExist(Media, table) {
table = "image_hardlink_info_v4"
}
case "video":
table = "video_hardlink_info_v3"
if !ds.IsExist(Media, table) {
table = "video_hardlink_info_v4"
}
case "file":
table = "file_hardlink_info_v3"
if !ds.IsExist(Media, table) {
table = "file_hardlink_info_v4"
}
case "voice":
return ds.GetVoice(ctx, key)
default:
@@ -659,8 +673,8 @@ func (ds *DataSource) GetMedia(ctx context.Context, _type string, key string) (*
mediaV4.Type = _type
media = mediaV4.Wrap()
// 跳过缩略图
if _type == "image" && !strings.Contains(media.Name, "_t") {
// 优先返回高清图
if _type == "image" && strings.HasSuffix(mediaV4.Name, "_h.dat") {
break
}
}
@@ -672,6 +686,22 @@ func (ds *DataSource) GetMedia(ctx context.Context, _type string, key string) (*
return media, nil
}
func (ds *DataSource) IsExist(_db string, table string) bool {
db, err := ds.dbm.GetDB(_db)
if err != nil {
return false
}
var tableName string
query := "SELECT name FROM sqlite_master WHERE type='table' AND name=?;"
if err = db.QueryRow(query, table).Scan(&tableName); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return false
}
return false
}
return true
}
func (ds *DataSource) GetVoice(ctx context.Context, key string) (*model.Media, error) {
if key == "" {
return nil, errors.ErrKeyEmpty
@@ -56,7 +56,7 @@ var Groups = []*dbm.Group{
},
{
Name: Voice,
Pattern: `^MediaMSG([0-9])?\.db$`,
Pattern: `^MediaMSG([0-9]?[0-9])?\.db$`,
BlackList: []string{},
},
}
@@ -207,6 +207,10 @@ func (ds *DataSource) initMessageDbs() error {
infos[i].EndTime = infos[i+1].StartTime
}
}
if len(ds.messageInfos) > 0 && len(infos) < len(ds.messageInfos) {
log.Warn().Msgf("message db count decreased from %d to %d, skip init", len(ds.messageInfos), len(infos))
return nil
}
ds.messageInfos = infos
return nil
}
+9 -5
View File
@@ -5,17 +5,14 @@ import (
"sort"
"strings"
"github.com/rs/zerolog/log"
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/model"
)
// initChatRoomCache 初始化群聊缓存
func (r *Repository) initChatRoomCache(ctx context.Context) error {
// 加载所有群聊到缓存
chatRooms, err := r.ds.GetChatRooms(ctx, "", 0, 0)
if err != nil {
return err
}
chatRoomMap := make(map[string]*model.ChatRoom)
remarkToChatRoom := make(map[string][]*model.ChatRoom)
@@ -24,6 +21,13 @@ func (r *Repository) initChatRoomCache(ctx context.Context) error {
chatRoomRemark := make([]string, 0)
chatRoomNickName := make([]string, 0)
// 加载所有群聊到缓存
// 暂时忽略获取不到群聊的错误
chatRooms, err := r.ds.GetChatRooms(ctx, "", 0, 0)
if err != nil {
log.Error().Err(err).Msg("Failed to load chat rooms")
}
for _, chatRoom := range chatRooms {
// 补充群聊信息(从联系人中获取 Remark 和 NickName)
r.enrichChatRoom(chatRoom)
+9 -5
View File
@@ -5,17 +5,14 @@ import (
"sort"
"strings"
"github.com/rs/zerolog/log"
"github.com/sjzar/chatlog/internal/errors"
"github.com/sjzar/chatlog/internal/model"
)
// initContactCache 初始化联系人缓存
func (r *Repository) initContactCache(ctx context.Context) error {
// 加载所有联系人到缓存
contacts, err := r.ds.GetContacts(ctx, "", 0, 0)
if err != nil {
return err
}
contactMap := make(map[string]*model.Contact)
aliasMap := make(map[string][]*model.Contact)
@@ -28,6 +25,13 @@ func (r *Repository) initContactCache(ctx context.Context) error {
remarkList := make([]string, 0)
nickNameList := make([]string, 0)
// 加载所有联系人到缓存
// 暂时忽略获取不到联系人的错误
contacts, err := r.ds.GetContacts(ctx, "", 0, 0)
if err != nil {
log.Error().Err(err).Msg("Failed to load contacts")
}
for _, contact := range contacts {
contactMap[contact.UserName] = contact
contactList = append(contactList, contact.UserName)