@@ -139,17 +139,42 @@ def _get_pending_best_version_episodes(cls, subscribe: Subscribe) -> List[int]:
139139 return cls .__get_pending_best_version_episodes_with_priority (subscribe )
140140
141141 @classmethod
142- def get_best_version_lack_episode (
143- cls ,
144- subscribe : Subscribe ,
145- episode_priority : Optional [dict ] = None ,
146- ) -> int :
142+ def compute_completed_episode (cls , subscribe : Subscribe ) -> Optional [int ]:
147143 """
148- 获取洗版订阅当前剩余待洗剧集数。
144+ 计算订阅"已完成"集数派生值,仅用于响应填充,不入库。
145+
146+ 语义:
147+ - 普通订阅 (best_version=0):``max(total_episode - lack_episode, 0)``,即媒体库已入库集数。
148+ - 洗版订阅 (best_version=1,含分集与全集洗版):
149+ ``(start_episode - 1) + (episode_priority 中 priority==100 且 ep ∈ [start, total] 的命中数)``。
150+ start_episode 之前的集不在订阅范围内,视为"逻辑上已完成",与主文案分母 total_episode 对齐。
151+
152+ - 入参:完整 Subscribe ORM/Schema 对象,需至少包含 best_version、type、start_episode、
153+ total_episode、lack_episode、episode_priority 字段。
154+ - 返回:完成集数;电影或缺少 total_episode 时返回 None。
149155 """
150- if not subscribe .best_version or subscribe .type != MediaType .TV .value :
151- return subscribe .lack_episode or 0
152- return len (cls .__get_pending_best_version_episodes_with_priority (subscribe , episode_priority ))
156+ total_episode = subscribe .total_episode or 0
157+ if subscribe .type != MediaType .TV .value or not total_episode :
158+ return None
159+
160+ start_episode = subscribe .start_episode or 1
161+
162+ if not subscribe .best_version :
163+ lack = subscribe .lack_episode or 0
164+ return max (total_episode - lack , 0 )
165+
166+ # 洗版口径:start 之前的集视为已完成 + 范围内 priority==100 命中。
167+ # ``start_episode > total_episode`` 是异常配置,需把"起始集前"偏移截断到 total,
168+ # 避免 completed 越过分母 total_episode。
169+ episode_priority = subscribe .episode_priority or {}
170+ priority_completed = sum (
171+ 1
172+ for ep_key , priority in episode_priority .items ()
173+ if str (ep_key ).isdigit ()
174+ and start_episode <= int (ep_key ) <= total_episode
175+ and priority == 100
176+ )
177+ return min (max (start_episode - 1 , 0 ), total_episode ) + priority_completed
153178
154179 @classmethod
155180 def get_best_version_current_priority (
@@ -1141,18 +1166,16 @@ def update_subscribe_priority(self, subscribe: Subscribe, meta: MetaBase,
11411166 return
11421167
11431168 current_priority = self .get_best_version_current_priority (subscribe , episode_priority )
1144- lack_episode = self . get_best_version_lack_episode ( subscribe , episode_priority )
1169+ # lack_episode 由 finish_subscribe_or_not -> __update_lack_episodes 按媒体库实况维护,本处不写
11451170 update_data : Dict [str , Any ] = {
11461171 "episode_priority" : episode_priority ,
11471172 "last_update" : now ,
11481173 "current_priority" : current_priority ,
1149- "lack_episode" : lack_episode ,
11501174 }
11511175
11521176 SubscribeOper ().update (subscribe .id , update_data )
11531177 subscribe .episode_priority = episode_priority
11541178 subscribe .current_priority = current_priority
1155- subscribe .lack_episode = lack_episode
11561179 subscribe .last_update = now
11571180
11581181 completed_episodes = self .__get_best_version_completed_episodes (subscribe )
@@ -1197,26 +1220,28 @@ def finish_subscribe_or_not(self, subscribe: Subscribe, meta: MetaBase, mediainf
11971220 self .__update_subscribe_note (subscribe = subscribe , downloads = downloads )
11981221 # 是否完成订阅
11991222 if not subscribe .best_version :
1200- # 更新订阅剩余集数和时间
1223+ # 普通订阅:先按 lefts 写 lack,再判断完成
12011224 self .__update_lack_episodes (lefts = lefts , subscribe = subscribe , mediainfo = mediainfo ,
12021225 update_date = bool (downloads ))
1203- # 判断是否需要完成订阅
12041226 if ((no_lefts and meta .type == MediaType .TV )
12051227 or (downloads and meta .type == MediaType .MOVIE )
12061228 or force ):
12071229 self .__finish_subscribe (subscribe = subscribe , meta = meta , mediainfo = mediainfo )
12081230 else :
1209- # 未下载到内容且不完整
12101231 logger .info (f'{ mediainfo .title_year } 未下载完整,继续订阅 ...' )
1211- elif downloads :
1212- # 洗版下载到了内容,更新资源优先级
1232+ return
1233+
1234+ # 洗版订阅:本轮若有下载先更新 episode_priority / current_priority,让 __update_lack_episodes
1235+ # 读取到包含本轮新下载的集;否则 lack 会慢一个搜索周期才反映新下载。
1236+ if downloads :
12131237 self .update_subscribe_priority (subscribe = subscribe , meta = meta ,
12141238 mediainfo = mediainfo , downloads = downloads )
1215- elif self .__is_best_version_complete (subscribe ):
1239+ self .__update_lack_episodes (lefts = lefts , subscribe = subscribe , mediainfo = mediainfo ,
1240+ update_date = bool (downloads ))
1241+ if self .__is_best_version_complete (subscribe ):
12161242 # 洗版完成
12171243 self .__finish_subscribe (subscribe = subscribe , meta = meta , mediainfo = mediainfo )
1218- else :
1219- # 洗版,未下载到内容
1244+ elif not downloads :
12201245 logger .info (f'{ mediainfo .title_year } 继续洗版 ...' )
12211246
12221247 def refresh (self ):
@@ -1662,26 +1687,24 @@ def check(self):
16621687 current_priority = None
16631688 if not subscribe .manual_total_episode and len (episodes ):
16641689 total_episode = len (episodes )
1690+ # 总集数增长按 delta 同步抬升 lack
1691+ lack_episode = (subscribe .lack_episode or 0 ) + (total_episode - (subscribe .total_episode or 0 ))
16651692 if subscribe .best_version and subscribe .type == MediaType .TV .value :
1693+ # 为新增集补齐 episode_priority 初始项(priority=0)
16661694 old_total_episode = subscribe .total_episode or 0
16671695 episode_priority = self .__get_episode_priority (subscribe )
16681696 for episode in range (old_total_episode + 1 , total_episode + 1 ):
16691697 episode_priority .setdefault (str (episode ), 0 )
16701698 subscribe .total_episode = total_episode
16711699 subscribe .episode_priority = episode_priority
1672- lack_episode = self .get_best_version_lack_episode (subscribe , episode_priority )
16731700 current_priority = self .get_best_version_current_priority (subscribe , episode_priority )
1674- else :
1675- lack_episode = subscribe .lack_episode + (total_episode - subscribe .total_episode )
16761701 logger .info (
16771702 f'订阅 { subscribe .name } 总集数变化,更新总集数为{ total_episode } ,缺失集数为{ lack_episode } ...' )
16781703 else :
16791704 total_episode = subscribe .total_episode
1705+ lack_episode = subscribe .lack_episode
16801706 if subscribe .best_version and subscribe .type == MediaType .TV .value :
1681- lack_episode = self .get_best_version_lack_episode (subscribe )
16821707 current_priority = self .get_best_version_current_priority (subscribe )
1683- else :
1684- lack_episode = subscribe .lack_episode
16851708 # 更新TMDB信息
16861709 update_data = {
16871710 "name" : mediainfo .title ,
@@ -1891,21 +1914,23 @@ def __update_lack_episodes(lefts: Dict[Union[int, str], Dict[int, schemas.NotExi
18911914 mediainfo : MediaInfo ,
18921915 update_date : Optional [bool ] = False ):
18931916 """
1894- 更新订阅剩余集数及时间
1917+ 写入订阅 lack_episode,可选同时刷新 last_update。
1918+
1919+ lack 统一语义为"订阅范围内尚未下载到任何版本的集数"。
1920+ - 普通订阅:lack 从 ``lefts`` 提取(lefts 已在 ``__get_subscribe_no_exits`` 里扣过 note)
1921+ - 洗版订阅:lack = ``[start, total]`` 范围内既不在 note 也不在 episode_priority(>0) 命中的集数。
1922+ 洗版的 lefts 由 ``check_and_handle_existing_media`` 按 priority<100 构造,承担"搜索目标"职责,
1923+ 与"未下载"维度并不同义——若复用会把"已下载但待升级"的集错算成 lack。
18951924 """
18961925 update_data = {}
18971926 if update_date :
18981927 update_data ["last_update" ] = datetime .now ().strftime ('%Y-%m-%d %H:%M:%S' )
1899- if subscribe .best_version and subscribe .type == MediaType .TV .value :
1900- lack_episode = len (SubscribeChain ._get_pending_best_version_episodes (subscribe ))
1901- logger .info (f"{ mediainfo .title_year } 季 { subscribe .season } 剩余待洗剧集数为{ lack_episode } ..." )
1902- update_data ["lack_episode" ] = lack_episode
1903- if update_data :
1904- SubscribeOper ().update (subscribe .id , update_data )
1905- return
19061928 if subscribe .type == MediaType .TV .value :
1907- if not lefts :
1908- # 如果 lefts 为空,表示没有缺失集数,直接设置 lack_episode 为 0
1929+ if subscribe .best_version :
1930+ lack_episode = SubscribeChain .__compute_best_version_lack_episode (subscribe )
1931+ logger .info (f"{ mediainfo .title_year } 季 { subscribe .season } 剩余未下载剧集数为{ lack_episode } ..." )
1932+ elif not lefts :
1933+ # lefts 为空:媒体库实缺为 0
19091934 lack_episode = 0
19101935 logger .info (f'{ mediainfo .title_year } 没有缺失集数,直接更新为 0 ...' )
19111936 else :
@@ -1928,6 +1953,36 @@ def __update_lack_episodes(lefts: Dict[Union[int, str], Dict[int, schemas.NotExi
19281953 if update_data :
19291954 SubscribeOper ().update (subscribe .id , update_data )
19301955
1956+ @staticmethod
1957+ def __compute_best_version_lack_episode (subscribe : Subscribe ) -> int :
1958+ """
1959+ 计算洗版订阅"未下载集数":在 ``[start, total]`` 范围内排除已在 ``note`` 或
1960+ ``episode_priority`` (>0) 中记账的集。priority<100 但 >0 的集视为"已下载、待升级",
1961+ 不计入 lack——与 UI 上"已下载 = total - lack"展示口径一致。
1962+ """
1963+ total_episode = subscribe .total_episode or 0
1964+ if not total_episode :
1965+ return 0
1966+ start_episode = subscribe .start_episode or 1
1967+ if total_episode < start_episode :
1968+ return 0
1969+ target_episodes = set (range (start_episode , total_episode + 1 ))
1970+ downloaded : set = set ()
1971+ for ep in (subscribe .note or []):
1972+ try :
1973+ downloaded .add (int (ep ))
1974+ except (TypeError , ValueError ):
1975+ continue
1976+ for ep_str , priority in (subscribe .episode_priority or {}).items ():
1977+ if not str (ep_str ).isdigit ():
1978+ continue
1979+ try :
1980+ if float (priority ) > 0 :
1981+ downloaded .add (int (ep_str ))
1982+ except (TypeError , ValueError ):
1983+ continue
1984+ return len (target_episodes - downloaded )
1985+
19311986 def __finish_subscribe (self , subscribe : Subscribe , mediainfo : MediaInfo , meta : MetaBase ):
19321987 """
19331988 完成订阅
0 commit comments