mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-07 05:46:38 +08:00
refactor: split modules and harden scraping workflows
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/ShukeBta/MediaStationGo/internal/model"
|
||||
)
|
||||
|
||||
// History returns completed/archived subscription rules.
|
||||
func (s *SubscriptionService) History(ctx context.Context) ([]model.Subscription, error) {
|
||||
return s.repo.Subscription.History(ctx)
|
||||
}
|
||||
|
||||
// Restore moves an archived subscription back to the active management list.
|
||||
// It also clears the per-subscription seen state so an unfinished historical
|
||||
// rule can match resources again when it is run next.
|
||||
func (s *SubscriptionService) Restore(ctx context.Context, id string) (*model.Subscription, error) {
|
||||
var sub model.Subscription
|
||||
if err := s.repo.DB.WithContext(ctx).Where("id = ?", id).First(&sub).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.repo.DB.WithContext(ctx).Model(&model.Subscription{}).
|
||||
Where("id = ?", id).
|
||||
Updates(map[string]any{
|
||||
"enabled": true,
|
||||
"archive_reason": "",
|
||||
// 重置为 0:此前可能被 feed 低估并锁死(updateSubscriptionTotalEpisodes
|
||||
// 只增不减,resolveSubscriptionTotalEpisodes 见 >0 即不再回查元数据)。
|
||||
// 归零后下次 run 会从 TMDb/豆瓣等权威源重算真实总集数,避免恢复后
|
||||
// 因"误判已无缺集"而不再搜索资源。
|
||||
"total_episodes": 0,
|
||||
}).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.repo.DB.WithContext(ctx).
|
||||
Exec("UPDATE subscriptions SET archived_at = NULL WHERE id = ?", id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s.repo.Setting != nil {
|
||||
_ = s.repo.Setting.Delete(ctx, fmt.Sprintf("subscription.%s.seen", id))
|
||||
}
|
||||
var restored model.Subscription
|
||||
if err := s.repo.DB.WithContext(ctx).Where("id = ?", id).First(&restored).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &restored, nil
|
||||
}
|
||||
|
||||
func (s *SubscriptionService) archiveCompletedSubscription(ctx context.Context, sub *model.Subscription, availability LocalAvailability) error {
|
||||
if s == nil || s.repo == nil || s.repo.Subscription == nil || sub == nil {
|
||||
return nil
|
||||
}
|
||||
if !subscriptionShouldArchive(sub, availability) {
|
||||
return nil
|
||||
}
|
||||
now := time.Now()
|
||||
reason := subscriptionArchiveReason(sub, availability)
|
||||
if err := s.repo.Subscription.Archive(ctx, sub.ID, reason, now); err != nil {
|
||||
return err
|
||||
}
|
||||
sub.Enabled = false
|
||||
sub.ArchivedAt = &now
|
||||
sub.ArchiveReason = reason
|
||||
if s.log != nil {
|
||||
s.log.Info("subscription completed, moved to history",
|
||||
zap.String("id", sub.ID),
|
||||
zap.String("name", sub.Name),
|
||||
zap.String("reason", reason))
|
||||
}
|
||||
if s.hub != nil {
|
||||
s.hub.Publish("subscription", map[string]any{
|
||||
"id": sub.ID,
|
||||
"name": sub.Name,
|
||||
"archived": true,
|
||||
"reason": reason,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func subscriptionShouldArchive(sub *model.Subscription, availability LocalAvailability) bool {
|
||||
if sub == nil || sub.WashEnabled || sub.ArchivedAt != nil {
|
||||
return false
|
||||
}
|
||||
mediaType := strings.ToLower(strings.TrimSpace(sub.MediaType))
|
||||
if !isSubscriptionSeriesType(mediaType) {
|
||||
return availability.InLibrary || availability.LocalMediaCount > 0 || availability.DownloadedEpisodes > 0
|
||||
}
|
||||
if availability.HasSeriesPack {
|
||||
return true
|
||||
}
|
||||
total := sub.TotalEpisodes
|
||||
if total <= 0 {
|
||||
total = availability.TotalEpisodes
|
||||
}
|
||||
if total > 0 {
|
||||
return availability.DownloadedEpisodes >= total && len(availability.MissingEpisodes) == 0
|
||||
}
|
||||
return subscriptionLooksSingleEpisode(sub) && availability.DownloadedEpisodes > 0
|
||||
}
|
||||
|
||||
func subscriptionArchiveReason(sub *model.Subscription, availability LocalAvailability) string {
|
||||
if sub != nil && sub.WashEnabled {
|
||||
return ""
|
||||
}
|
||||
if availability.HasSeriesPack {
|
||||
return "整季资源已加入下载/入库"
|
||||
}
|
||||
if availability.TotalEpisodes > 0 {
|
||||
return fmt.Sprintf("订阅完成:%d/%d", availability.DownloadedEpisodes, availability.TotalEpisodes)
|
||||
}
|
||||
if availability.DownloadedEpisodes > 0 {
|
||||
return "单集订阅已加入下载/入库"
|
||||
}
|
||||
return "订阅媒体已加入下载/入库"
|
||||
}
|
||||
|
||||
func subscriptionLooksSingleEpisode(sub *model.Subscription) bool {
|
||||
if sub == nil {
|
||||
return false
|
||||
}
|
||||
for _, value := range []string{sub.Name, sub.Filter} {
|
||||
_, episode := ParseEpisode(value)
|
||||
if episode > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user