diff --git a/README.md b/README.md index 116fb7a..8c7ba6b 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,20 @@ services: - 8080:8080 ```` +## 同步逻辑 +- 收藏夹 + - 视频稿件失效:已下载的视频稿件文件夹会被打上`[已失效]`标识 + - 视频稿件取消收藏:已下载视频保留。未下载视频不下载。 + - 删除收藏夹:已同步的整个收藏夹移动到回收站中 + - 收藏夹改名:如当前收藏夹视频正在下载,等下载完毕后更改收藏夹名称 +- 收藏和订阅 + - 视频稿件失效:已下载的视频稿件文件夹会被打上`[已失效]`标识 + - 取消订阅:已同步的整个订阅移动到回收站中 + - 订阅合集中视频稿件被移除合集:保留已下载视频稿件 +- 稍后再看 + - 手动删除稍后再看的视频稿件:已下载的视频稿件文件夹移动到回收站中 + - 视频稿件失效:已下载的视频稿件文件夹会被打上`[已失效]`标识 + ## 预览图 ![账号列表](./.assets/1.png) ![设置收藏夹同步](./.assets/2.png) diff --git a/bobo/client/client.go b/bobo/client/client.go index f5a76fb..b7a177e 100644 --- a/bobo/client/client.go +++ b/bobo/client/client.go @@ -78,6 +78,9 @@ func (c *Client) RefreshWbiKey(nav *Navigation) error { if len(c.cookies) < 1 { return errors.New("未登录") } + if nav == nil { + return errors.New("未获取到导航信息") + } imgUrl := strings.Split(nav.WbiImg.ImgUrl, "/") subUrl := strings.Split(nav.WbiImg.SubUrl, "/") c.imgKey = strings.Split(imgUrl[len(imgUrl)-1], ".")[0] @@ -161,3 +164,8 @@ func (c *Client) resty() *resty.Client { func (c *Client) GetResty() *resty.Client { return c.resty() } + +func (c *Client) CheckVideo(bvid string) bool { + _, code, _ := c.GetVideoInfoByBvidCode(bvid) + return code == 0 +} diff --git a/bobo/client/fav_type.go b/bobo/client/fav_type.go index 81b530f..9591b40 100644 --- a/bobo/client/fav_type.go +++ b/bobo/client/fav_type.go @@ -119,7 +119,7 @@ type FavourList struct { Name string `json:"name"` // UP主昵称 Face string `json:"face"` // UP主头像url } `json:"upper"` - Attr int `json:"attr"` // 属性位(?) + Attr int `json:"attr"` // 属性位(?) 0:正常 9:失效-UP主删除 CntInfo struct { // 状态数 Collect int `json:"collect"` // 收藏数 Play int `json:"play"` // 播放数 diff --git a/bobo/client/video.go b/bobo/client/video.go index 1037c65..0d271ff 100644 --- a/bobo/client/video.go +++ b/bobo/client/video.go @@ -55,18 +55,35 @@ func (c *Client) GetVideoInfoByAvid(avid int) (*VideoInfo, error) { // GetVideoInfoByBvid 通过Bvid获取视频信息 func (c *Client) GetVideoInfoByBvid(bvid string) (*VideoInfo, error) { + // resp, err := c.resty().R().SetHeader("Content-Type", "application/x-www-form-urlencoded"). + // SetQueryParam("bvid", bvid).Get("https://api.bilibili.com/x/web-interface/view") + // if err != nil { + // return nil, errors.WithStack(err) + // } + // data, err := getRespData(resp, "获取视频详细信息") + // if err != nil { + // return nil, err + // } + // var ret *VideoInfo + // err = json.Unmarshal(data, &ret) + // return ret, errors.WithStack(err) + ret, _, err := c.GetVideoInfoByBvidCode(bvid) + return ret, err +} + +func (c *Client) GetVideoInfoByBvidCode(bvid string) (*VideoInfo, int64, error) { resp, err := c.resty().R().SetHeader("Content-Type", "application/x-www-form-urlencoded"). SetQueryParam("bvid", bvid).Get("https://api.bilibili.com/x/web-interface/view") if err != nil { - return nil, errors.WithStack(err) + return nil, 0, errors.WithStack(err) } - data, err := getRespData(resp, "获取视频详细信息") + data, code, err := getRespDataWithCode(resp, "获取视频详细信息") if err != nil { - return nil, err + return nil, code, err } var ret *VideoInfo err = json.Unmarshal(data, &ret) - return ret, errors.WithStack(err) + return ret, 0, errors.WithStack(err) } // GetVideoInfoByShortUrl 通过短链接获取视频信息 diff --git a/bobo/downloader.go b/bobo/downloader.go index 91ff217..a86665f 100644 --- a/bobo/downloader.go +++ b/bobo/downloader.go @@ -25,6 +25,10 @@ func downloadHandler(c *client.Client, video *models.Videos, basePath, path stri services.SetVideoStatus(video.ID, videoStatus) return } + if !c.CheckVideo(video.Bvid) { + services.SetVideoStatus(video.ID, videoStatus) + return + } tmpFilePath := filepath.Join(basePath, ".tmp") fileName := fmt.Sprintf("%d_%d_%s_%d", mid, video.SourceId, video.Bvid, video.Cid) diff --git a/bobo/refreshCollected.go b/bobo/refreshCollected.go index 614bd6b..9a0d69e 100644 --- a/bobo/refreshCollected.go +++ b/bobo/refreshCollected.go @@ -6,6 +6,7 @@ import ( "bilibo/log" "bilibo/services" "fmt" + "slices" ) func (b *BoBo) RefreshCollected(mid int) *client.CollectedInfo { @@ -39,13 +40,15 @@ func (b *BoBo) RefreshCollectedVideo(mid int, data *client.CollectedInfo) map[st logger := log.GetLogger() logger.Infof("user: %d collected video list", mid) videosInfoMap := make(map[string]*services.VideoInfo) + if client, err := b.GetClient(mid); err == nil { if data != nil { for _, collected := range data.List { videosMap := make(map[string]*services.Video) + invalidVideosBvidList := make([]string, 0) if fret, err := client.GetCollectedVideoList(collected.Id); err == nil { for _, media := range fret.Medias { - if vret, err := client.GetVideoInfoByBvid(media.BvId); err == nil { + if vret, code, err := client.GetVideoInfoByBvidCode(media.BvId); err == nil { for _, page := range vret.Pages { videosMapKey := fmt.Sprintf("%d_%s_%d", collected.Id, media.BvId, page.Cid) videosMap[videosMapKey] = &services.Video{ @@ -68,9 +71,18 @@ func (b *BoBo) RefreshCollectedVideo(mid int, data *client.CollectedInfo) map[st Rotate: vret.Dimension.Rotate, } } + } else if code == 62002 { + logger.Infof("用户: %d 收藏和订阅: %s 无效视频bvid: %s", mid, collected.Title, media.BvId) + if !slices.Contains(invalidVideosBvidList, media.BvId) { + invalidVideosBvidList = append(invalidVideosBvidList, media.BvId) + } } } } + if len(invalidVideosBvidList) > 0 { + logger.Infof("用户: %d 收藏和订阅: %s 有 %d 个无效视频", mid, collected.Title, len(invalidVideosBvidList)) + services.SetInvalidVideos(mid, collected.Id, invalidVideosBvidList, consts.VIDEO_TYPE_COLLECTED) + } if len(videosMap) > 0 { services.SetVideos(mid, collected.Id, videosMap, consts.VIDEO_TYPE_COLLECTED) } diff --git a/bobo/refreshFav.go b/bobo/refreshFav.go index cf04355..3eab55a 100644 --- a/bobo/refreshFav.go +++ b/bobo/refreshFav.go @@ -6,6 +6,7 @@ import ( "bilibo/log" "bilibo/services" "fmt" + "slices" ) func (b *BoBo) RefreshFav(mid int) *client.AllFavourFolderInfo { @@ -45,35 +46,56 @@ func (b *BoBo) RefreshFavVideo(mid int, data *client.AllFavourFolderInfo) map[st if data != nil { for _, fav := range data.List { videosMap := make(map[string]*services.Video) + invalidVideosBvidList := make([]string, 0) mlid := fav.Id - if fret, err := client.GetFavourList(mlid, 0, "", "", 0, 20, 1, "web"); err == nil { - for _, media := range fret.Medias { - if vret, err := client.GetVideoInfoByBvid(media.BvId); err == nil { - for _, page := range vret.Pages { - videosMapKey := fmt.Sprintf("%d_%s_%d", mlid, media.BvId, page.Cid) - videosMap[videosMapKey] = &services.Video{ - Bvid: media.BvId, - SourceId: mlid, - Mid: mid, - Cid: page.Cid, - Type: consts.VIDEO_TYPE_FAVOUR, - } + pn := 1 + for { + if fret, err := client.GetFavourList(mlid, 0, "", "", 0, 20, pn, "web"); err == nil { + for _, media := range fret.Medias { + if vret, code, err := client.GetVideoInfoByBvidCode(media.BvId); err == nil { + for _, page := range vret.Pages { + videosMapKey := fmt.Sprintf("%d_%s_%d", mlid, media.BvId, page.Cid) + videosMap[videosMapKey] = &services.Video{ + Bvid: media.BvId, + SourceId: mlid, + Mid: mid, + Cid: page.Cid, + Type: consts.VIDEO_TYPE_FAVOUR, + } - videosInfoMapKey := fmt.Sprintf("%s_%d", media.BvId, page.Cid) - videosInfoMap[videosInfoMapKey] = &services.VideoInfo{ - Bvid: media.BvId, - Cid: page.Cid, - Page: page.Page, - Title: vret.Title, - Part: page.Part, - Width: vret.Dimension.Width, - Height: vret.Dimension.Height, - Rotate: vret.Dimension.Rotate, + videosInfoMapKey := fmt.Sprintf("%s_%d", media.BvId, page.Cid) + videosInfoMap[videosInfoMapKey] = &services.VideoInfo{ + Bvid: media.BvId, + Cid: page.Cid, + Page: page.Page, + Title: vret.Title, + Part: page.Part, + Width: vret.Dimension.Width, + Height: vret.Dimension.Height, + Rotate: vret.Dimension.Rotate, + } + } + } else if code == 62002 { + logger.Infof("用户: %d 收藏夹: %s 无效视频bvid: %s", mid, fav.Title, media.BvId) + if !slices.Contains(invalidVideosBvidList, media.BvId) { + invalidVideosBvidList = append(invalidVideosBvidList, media.BvId) } } } + logger.Infof("已获取 用户: %d 收藏夹: %s 第 %d 页数据", mid, fav.Title, pn) + if fret.HasMore { + pn++ + } else { + break + } + } else { + break } } + if len(invalidVideosBvidList) > 0 { + logger.Infof("用户: %d 收藏夹: %s 有 %d 个无效视频", mid, fav.Title, len(invalidVideosBvidList)) + services.SetInvalidVideos(mid, mlid, invalidVideosBvidList, consts.VIDEO_TYPE_FAVOUR) + } if len(videosMap) > 0 { services.SetVideos(mid, mlid, videosMap, consts.VIDEO_TYPE_FAVOUR) } diff --git a/bobo/refreshToView.go b/bobo/refreshToView.go index b858c21..0f04008 100644 --- a/bobo/refreshToView.go +++ b/bobo/refreshToView.go @@ -2,17 +2,21 @@ package bobo import ( "bilibo/consts" + "bilibo/log" "bilibo/services" "fmt" + "slices" ) func (b *BoBo) RefreshToView(mid int) map[string]*services.VideoInfo { + logger := log.GetLogger() videosMap := make(map[string]*services.Video) videosInfoMap := make(map[string]*services.VideoInfo) + invalidVideosBvidList := make([]string, 0) if client, err := b.GetClient(mid); err == nil { if toViewData, err := client.GetToView(); err == nil { for _, data := range toViewData.List { - if vret, err := client.GetVideoInfoByBvid(data.Bvid); err == nil { + if vret, code, err := client.GetVideoInfoByBvidCode(data.Bvid); err == nil { for _, page := range vret.Pages { videosMapKey := fmt.Sprintf("%d_%s_%d", 0, data.Bvid, page.Cid) videosMap[videosMapKey] = &services.Video{ @@ -34,10 +38,19 @@ func (b *BoBo) RefreshToView(mid int) map[string]*services.VideoInfo { Rotate: vret.Dimension.Rotate, } } + } else if code == 62002 { + logger.Infof("用户: %d 稍后再看 无效视频: %s", mid, data.Bvid) + if !slices.Contains(invalidVideosBvidList, data.Bvid) { + invalidVideosBvidList = append(invalidVideosBvidList, data.Bvid) + } } } } } + if len(invalidVideosBvidList) > 0 { + logger.Infof("用户: %d 稍后再看 有 %d 个无效视频", mid, len(invalidVideosBvidList)) + services.SetInvalidVideos(mid, 0, invalidVideosBvidList, consts.VIDEO_TYPE_WATCH_LATER) + } if len(videosMap) > 0 { services.SetVideos(mid, 0, videosMap, consts.VIDEO_TYPE_WATCH_LATER) } diff --git a/consts/common.go b/consts/common.go index bdab244..f81ae2b 100644 --- a/consts/common.go +++ b/consts/common.go @@ -4,6 +4,11 @@ import "errors" var ERROR_DOWNLOAD_403 = errors.New("download failed,status code: 403") +const ( + VIDEO_ATTR_NORMAL = 0 + VIDEO_ATTR_INVALID = 9 +) + const ( WATCH_LATER_NOT_SYNC = 0 WATCH_LATER_NEED_SYNC = 1 diff --git a/services/video.go b/services/video.go index b777f5e..97eddc0 100644 --- a/services/video.go +++ b/services/video.go @@ -1,12 +1,15 @@ package services import ( + "bilibo/config" "bilibo/consts" "bilibo/log" "bilibo/models" "bilibo/utils" "fmt" "math" + "os" + "path/filepath" "time" "golang.org/x/exp/maps" @@ -99,10 +102,10 @@ func SetVideos(mid, source_id int, videos map[string]*Video, vType int) { &models.Videos{Mid: mid, Type: vType, SourceId: source_id}, ).Find(&videosList) - existsVideoMap := make(map[string]*models.Videos) + existsVideoMap := make(map[string]models.Videos) for _, v := range videosList { mapKey := fmt.Sprintf("%d_%s_%d", v.SourceId, v.Bvid, v.Cid) - existsVideoMap[mapKey] = &v + existsVideoMap[mapKey] = v } existsKeys := maps.Keys(existsVideoMap) @@ -132,10 +135,10 @@ func SetVideos(mid, source_id int, videos map[string]*Video, vType int) { } if len(insertKeys) > 0 { - createList := []*models.Videos{} + createList := []models.Videos{} for _, key := range insertKeys { if video, ok := videos[key]; ok { - createList = append(createList, &models.Videos{ + createList = append(createList, models.Videos{ SourceId: video.SourceId, Mid: mid, Bvid: video.Bvid, @@ -151,6 +154,120 @@ func SetVideos(mid, source_id int, videos map[string]*Video, vType int) { } } +type GroupVideo struct { + SourceId int + Mid int + Bvid string + Status int + Type int +} + +type GroupVideoInfo struct { + Bvid string + Title string +} + +func SetInvalidVideos(mid, source_id int, bvids []string, vType int) { + logger := log.GetLogger() + db := models.GetDB() + videosList := []GroupVideo{} + + if len(bvids) > 0 { + times := int(math.Ceil(float64(len(bvids)) / 100)) + for i := 0; i < times; i++ { + start := i * 100 + end := i*100 + 100 + if end > len(bvids) { + end = len(bvids) + } + bvidsSlice := bvids[start:end] + + rangeVideos := []GroupVideo{} + + db.Model(&models.Videos{}).Select( + "mid", "type", "source_id", "status", "bvid", + ).Where(&models.Videos{ + Mid: mid, Type: vType, SourceId: source_id, Status: consts.VIDEO_STATUS_DOWNLOAD_DONE, + }).Where("bvid IN (?)", bvidsSlice). + Group("mid"). + Group("type"). + Group("source_id"). + Group("status"). + Group("bvid"). + Find(&rangeVideos) + + if len(rangeVideos) > 0 { + videosList = append(videosList, rangeVideos...) + } + } + } + + if len(videosList) < 1 { + return + } + + videoInfoList := []GroupVideoInfo{} + if len(bvids) > 0 { + times := int(math.Ceil(float64(len(bvids)) / 100)) + for i := 0; i < times; i++ { + start := i * 100 + end := i*100 + 100 + if end > len(bvids) { + end = len(bvids) + } + bvidsSlice := bvids[start:end] + rangeVideosInfo := []GroupVideoInfo{} + db.Model(&models.VideosInfo{}).Select( + "bvid", "title", + ).Where( + "bvid IN (?)", bvidsSlice, + ).Group("bvid").Group("title").Find(&rangeVideosInfo) + if len(rangeVideosInfo) > 0 { + videoInfoList = append(videoInfoList, rangeVideosInfo...) + } + } + } + + videoInfoMap := make(map[string]string) + for _, v := range videoInfoList { + videoInfoMap[v.Bvid] = v.Title + } + + path := "" + basePath := config.GetConfig().Download.Path + + if vType == consts.VIDEO_TYPE_COLLECTED { + collect := models.CollectedInfo{} + db.Model(&models.CollectedInfo{}).Where(&models.CollectedInfo{ + Mid: mid, CollId: source_id, + }).First(&collect) + if collect.ID > 0 { + path = filepath.Join(utils.GetCollectedPath(mid, basePath), collect.Title) + } + } else if vType == consts.VIDEO_TYPE_WATCH_LATER { + path = utils.GetWatchLaterPath(mid, basePath) + } else if vType == consts.VIDEO_TYPE_FAVOUR { + folder := models.FavourFoldersInfo{} + db.Model(&models.FavourFoldersInfo{}).Where(&models.FavourFoldersInfo{ + Mid: mid, Mlid: source_id, + }).First(&folder) + if folder.ID > 0 { + path = filepath.Join(utils.GetFavourPath(mid, basePath), folder.Title) + } + } + if path == "" { + return + } + for _, v := range videosList { + if videoInfoTitle, ok := videoInfoMap[v.Bvid]; ok { + beforePath := filepath.Join(path, utils.Name(videoInfoTitle)) + distPath := filepath.Join(path, utils.Name(videoInfoTitle)+"[已失效]") + os.Rename(beforePath, distPath) + logger.Infof("%s -> %s", beforePath, distPath) + } + } +} + func SetVideosInfo(videosInfo map[string]*VideoInfo) { db := models.GetDB() diff --git a/tests/invalid_videos/invalid_videos_test.go b/tests/invalid_videos/invalid_videos_test.go new file mode 100644 index 0000000..714660e --- /dev/null +++ b/tests/invalid_videos/invalid_videos_test.go @@ -0,0 +1,121 @@ +package tests + +import ( + "bilibo/config" + "bilibo/consts" + "bilibo/models" + "bilibo/services" + "bilibo/tests" + "bilibo/utils" + "os" + "path/filepath" + "testing" +) + +func setup() { + os.RemoveAll(config.GetConfig().Download.Path) + favPath := utils.GetFavourPath(1, config.GetConfig().Download.Path) + recyclePath := utils.GetRecyclePath(1, config.GetConfig().Download.Path) + os.MkdirAll(filepath.Join(favPath, "test", "abc1"), os.ModePerm) + os.MkdirAll(recyclePath, os.ModePerm) + + db := models.GetDB() + db.Migrator().DropTable( + &models.BiliAccounts{}, + &models.FavourFoldersInfo{}, + &models.Videos{}, + &models.VideosInfo{}, + ) + db.AutoMigrate( + &models.BiliAccounts{}, + &models.FavourFoldersInfo{}, + &models.Videos{}, + &models.VideosInfo{}, + ) + + account := models.BiliAccounts{ + Mid: 1, + UName: "test", + Face: "test", + ImgKey: "test", + SubKey: "test", + Cookies: "test", + Status: consts.ACCOUNT_STATUS_NORMAL, + } + db.Save(&account) + + fav := models.FavourFoldersInfo{ + Mid: 1, + Fid: 1, + MediaCount: 1, + Attr: 1, + Title: "test", + Mlid: 1, + FavState: 1, + Sync: 1, + } + db.Save(&fav) + + video := models.Videos{ + SourceId: 1, + Mid: 1, + Bvid: "abc1", + Cid: 1, + Status: consts.VIDEO_STATUS_DOWNLOAD_DONE, + LastDownloadAt: nil, + Type: consts.VIDEO_TYPE_FAVOUR, + } + db.Save(&video) + + videoInfo := models.VideosInfo{ + Bvid: "abc1", + Cid: 1, + Page: 1, + Title: "abc1", + Part: "testvideo", + Width: 1080, + Height: 720, + Rotate: 0, + } + db.Save(&videoInfo) +} + +func SetInvalidVideos(t *testing.T) { + services.SetInvalidVideos(1, 1, []string{"abc1"}, consts.VIDEO_TYPE_FAVOUR) + favPath := utils.GetFavourPath(1, config.GetConfig().Download.Path) + distPath := filepath.Join(favPath, "test", "abc1[已失效]") + + if _, err := os.Stat(distPath); err != nil { + t.Fatal(err) + } + db := models.GetDB() + var videosCount int64 = 0 + db.Model(&models.Videos{}).Where("bvid = ?", "abc1").Count(&videosCount) + if videosCount == 0 { + t.Fatal("videosCount == 0") + } + + var videoInfoCount int64 = 0 + db.Model(&models.VideosInfo{}).Where("bvid = ?", "abc1").Count(&videoInfoCount) + if videoInfoCount == 0 { + t.Fatal("videoInfoCount == 0") + } +} + +func teardown() { + db := models.GetDB() + db.Migrator().DropTable( + &models.BiliAccounts{}, + &models.FavourFoldersInfo{}, + &models.Videos{}, + &models.VideosInfo{}, + ) + os.RemoveAll(config.GetConfig().Download.Path) +} + +func TestInvalidVideos(t *testing.T) { + tests.Init() + setup() + defer teardown() + SetInvalidVideos(t) +} diff --git a/tests/realtime_job_test.go b/tests/realtime_job/realtime_job_test.go similarity index 99% rename from tests/realtime_job_test.go rename to tests/realtime_job/realtime_job_test.go index 85aaa54..ca5589e 100644 --- a/tests/realtime_job_test.go +++ b/tests/realtime_job/realtime_job_test.go @@ -5,6 +5,7 @@ import ( "bilibo/consts" "bilibo/models" "bilibo/services" + "bilibo/tests" "bilibo/utils" "os" "path/filepath" @@ -184,7 +185,7 @@ func DeleteFavour(t *testing.T) { } func TestFileUtils(t *testing.T) { - Init() + tests.Init() ChangeFavourName(t) DeleteFavour(t) } diff --git a/web/services/account.go b/web/services/account.go index a49d728..bf77268 100644 --- a/web/services/account.go +++ b/web/services/account.go @@ -89,19 +89,16 @@ type Collected struct { } type AccountInfo struct { - Mid int `json:"mid"` - Uname string `json:"uname"` - Status int `json:"status"` - Face string `json:"face"` - FoldersCount int `json:"folders_count"` - Folders []*FavourFolders `json:"folders"` - WatchLaterCount int64 `json:"watch_later_count"` - WatchLaterSync int `json:"watch_later_sync"` - Collected []*Collected `json:"collected"` - CollectedCount int `json:"collected_count"` + Mid int `json:"mid"` + Uname string `json:"uname"` + Status int `json:"status"` + Face string `json:"face"` + FoldersCount int64 `json:"folders_count"` + WatchLaterCount int64 `json:"watch_later_count"` + CollectedCount int64 `json:"collected_count"` } -type AccountWatchLaterCount struct { +type AccountCounts struct { Mid int `json:"mid"` Count int64 `json:"count"` } @@ -129,60 +126,38 @@ func AccountList(page, pageSize int) (*[]*AccountInfo, int64) { url.QueryEscape(data.Face), ), Uname: data.UName, - Folders: make([]*FavourFolders, 0), FoldersCount: 0, WatchLaterCount: 0, - WatchLaterSync: 0, } accountMap[data.Mid] = &item accountMids = append(accountMids, data.Mid) } - var favourFolderInfos []models.FavourFoldersInfo - db.Model(&models.FavourFoldersInfo{}).Where("mid IN (?)", accountMids).Find(&favourFolderInfos) - for _, v := range favourFolderInfos { - folders := FavourFolders{ - Mlid: v.Mlid, - Fid: v.Fid, - Title: v.Title, - MediaCount: v.MediaCount, - Sync: v.Sync, - } - accountMap[v.Mid].Folders = append(accountMap[v.Mid].Folders, &folders) - accountMap[v.Mid].FoldersCount++ + var favourFolderCount []AccountCounts + db.Model(&models.FavourFoldersInfo{}).Select( + "COUNT(mid) AS count", "mid", + ).Where("mid IN (?)", accountMids).Group("mid").Find(&favourFolderCount) + for _, v := range favourFolderCount { + accountMap[v.Mid].FoldersCount = v.Count } - var collectedInfos []models.CollectedInfo - db.Model(&models.CollectedInfo{}).Where("mid IN (?)", accountMids).Find(&collectedInfos) + var collectedInfos []AccountCounts + db.Model(&models.CollectedInfo{}).Select( + "COUNT(mid) AS count", "mid", + ).Where("mid IN (?)", accountMids).Group("mid").Find(&collectedInfos) for _, v := range collectedInfos { - collected := Collected{ - CollId: v.CollId, - Attr: v.Attr, - Title: v.Title, - MediaCount: v.MediaCount, - Sync: v.Sync, - } - accountMap[v.Mid].Collected = append(accountMap[v.Mid].Collected, &collected) - accountMap[v.Mid].CollectedCount++ + accountMap[v.Mid].CollectedCount = v.Count } - watchLaterCount := make([]AccountWatchLaterCount, 0) - db.Model(&models.Videos{}).Select("COUNT(mid) AS count", "mid").Where( - "mid IN (?) AND type = ?", accountMids, + var watchLaterCount []AccountCounts + db.Model(&models.Videos{}).Select( + "COUNT(mid) AS count", "mid", + ).Where("mid IN (?) AND type = ?", accountMids, consts.VIDEO_TYPE_WATCH_LATER, ).Group("mid").Find(&watchLaterCount) for _, v := range watchLaterCount { accountMap[v.Mid].WatchLaterCount = v.Count } - - watchLaterSync := make([]AccountWatchLaterSync, 0) - db.Model(&models.WatchLater{}).Select("sync", "mid").Where( - "mid IN (?)", accountMids, - ).Find(&watchLaterSync) - for _, v := range watchLaterSync { - accountMap[v.Mid].WatchLaterSync = v.Sync - } - } items := maps.Values(accountMap) return &items, total @@ -195,6 +170,34 @@ func AccountTotal() int64 { return total } +type AccountSettings struct { + Mid int `json:"mid"` + Folders []FavourFolders `json:"folders"` + WatchLaterSync int `json:"watch_later_sync"` + Collected []Collected `json:"collected"` +} + +func GetAccountSettings(mid int) *AccountSettings { + db := models.GetDB() + watchLaterSync := AccountWatchLaterSync{} + db.Model(&models.WatchLater{}).Select("sync", "mid").Where( + "mid = ?", mid, + ).First(&watchLaterSync) + + var favourFolderInfos []FavourFolders + db.Model(&models.FavourFoldersInfo{}).Where("mid = ?", mid).Find(&favourFolderInfos) + + var collectedInfos []Collected + db.Model(&models.CollectedInfo{}).Where("mid = ?", mid).Find(&collectedInfos) + + return &AccountSettings{ + Mid: mid, + Folders: favourFolderInfos, + WatchLaterSync: watchLaterSync.Sync, + Collected: collectedInfos, + } +} + type AccountFile struct { BaseName string `json:"basename"` Extension string `json:"extension"` diff --git a/web/views/AccountViews.go b/web/views/AccountViews.go index 0475127..227628d 100644 --- a/web/views/AccountViews.go +++ b/web/views/AccountViews.go @@ -20,6 +20,7 @@ import ( func RegAccount(rg *gin.RouterGroup) { account := rg.Group("account") account.GET("list", accountList) + account.GET("settings/:mid", accountSettings) account.POST("delete", accountDelete) account.GET("save", accountSave) account.GET("proxy/:mid", accountProxy) @@ -66,6 +67,28 @@ func accountList(c *gin.Context) { c.JSON(http.StatusOK, rsp) } +func accountSettings(c *gin.Context) { + rsp := gin.H{ + "data": nil, + "message": "account not found", + "result": 999, + } + if midStr := c.Param("mid"); midStr == "" { + c.Status(404) + } else { + if mid, err := strconv.Atoi(midStr); err != nil { + rsp["message"] = "mid error:" + err.Error() + } else { + if data := services.GetAccountSettings(mid); data != nil { + rsp["data"] = data + rsp["message"] = "account settings" + rsp["result"] = 0 + } + } + c.JSON(http.StatusOK, rsp) + } +} + type accountDeleteReq struct { Mid int `json:"mid" binding:"required"` }