Commit 7fdc8f0d 7fdc8f0dc9797b188983f533a05dd14477b1f573 by 沈秋雨

修复歌手为[]时误导入情况

1 parent 1bc953d1
...@@ -153,15 +153,16 @@ def _select_qq_import_record( ...@@ -153,15 +153,16 @@ def _select_qq_import_record(
153 current_record: dict, 153 current_record: dict,
154 candidate_records: list[dict], 154 candidate_records: list[dict],
155 songs_by_mid: dict[str, dict], 155 songs_by_mid: dict[str, dict],
156 ) -> dict | None: 156 ) -> tuple[dict | None, str]:
157 """为导入选择 QQ 录音;当前录音的歌词或封面不可用时,改选同源的合格候选。""" 157 """为导入选择 QQ 录音;当前录音的歌词或封面不可用时,改选同源的合格候选。
158 返回 (record, reason),record 为 None 时 reason 说明失败原因。"""
158 current_song = songs_by_mid.get(current_record['platform_unique_key']) 159 current_song = songs_by_mid.get(current_record['platform_unique_key'])
159 if not current_song: 160 if not current_song:
160 return None 161 return None, 'spider data missing'
161 needs_lyric = not _has_usable_qq_lyric(current_song) 162 needs_lyric = not _has_usable_qq_lyric(current_song)
162 needs_cover = not _is_usable_qq_cover(_qq_cover_source(hk_row, current_song)) or not current_song.get('album_id') 163 needs_cover = not _is_usable_qq_cover(_qq_cover_source(hk_row, current_song)) or not current_song.get('album_id')
163 if not needs_lyric and not needs_cover: 164 if not needs_lyric and not needs_cover:
164 return current_record 165 return current_record, 'ok'
165 166
166 for candidate in candidate_records: 167 for candidate in candidate_records:
167 song = songs_by_mid.get(candidate['platform_unique_key']) 168 song = songs_by_mid.get(candidate['platform_unique_key'])
...@@ -171,8 +172,13 @@ def _select_qq_import_record( ...@@ -171,8 +172,13 @@ def _select_qq_import_record(
171 continue 172 continue
172 if needs_cover and (not song.get('album_id') or not _is_usable_qq_cover(song.get('cover'))): 173 if needs_cover and (not song.get('album_id') or not _is_usable_qq_cover(song.get('cover'))):
173 continue 174 continue
174 return candidate 175 return candidate, 'ok'
175 return None 176 missing = []
177 if needs_lyric:
178 missing.append('lyric')
179 if needs_cover:
180 missing.append('cover')
181 return None, f"no candidate with usable {' and '.join(missing)}"
176 182
177 183
178 def _select_kugou_import_record( 184 def _select_kugou_import_record(
...@@ -180,16 +186,17 @@ def _select_kugou_import_record( ...@@ -180,16 +186,17 @@ def _select_kugou_import_record(
180 current_record: dict, 186 current_record: dict,
181 candidate_records: list[dict], 187 candidate_records: list[dict],
182 songs_by_id: dict[int, dict], 188 songs_by_id: dict[int, dict],
183 ) -> dict | None: 189 ) -> tuple[dict | None, str]:
184 """为导入选择酷狗录音;当前录音的歌词或封面不可用时,改选同源的合格候选。""" 190 """为导入选择酷狗录音;当前录音的歌词或封面不可用时,改选同源的合格候选。
191 返回 (record, reason),record 为 None 时 reason 说明失败原因。"""
185 song_id = int(current_record['platform_unique_key']) 192 song_id = int(current_record['platform_unique_key'])
186 current_song = songs_by_id.get(song_id) 193 current_song = songs_by_id.get(song_id)
187 if not current_song: 194 if not current_song:
188 return None 195 return None, 'spider data missing'
189 needs_lyric = not _has_kugou_lyric(current_song) 196 needs_lyric = not _has_kugou_lyric(current_song)
190 needs_cover = not _has_kugou_cover(hk_row, current_song) 197 needs_cover = not _has_kugou_cover(hk_row, current_song)
191 if not needs_lyric and not needs_cover: 198 if not needs_lyric and not needs_cover:
192 return current_record 199 return current_record, 'ok'
193 200
194 for candidate in candidate_records: 201 for candidate in candidate_records:
195 cid = int(candidate['platform_unique_key']) 202 cid = int(candidate['platform_unique_key'])
...@@ -200,8 +207,13 @@ def _select_kugou_import_record( ...@@ -200,8 +207,13 @@ def _select_kugou_import_record(
200 continue 207 continue
201 if needs_cover and not _has_kugou_cover(hk_row, song): 208 if needs_cover and not _has_kugou_cover(hk_row, song):
202 continue 209 continue
203 return candidate 210 return candidate, 'ok'
204 return None 211 missing = []
212 if needs_lyric:
213 missing.append('lyric')
214 if needs_cover:
215 missing.append('cover')
216 return None, f"no candidate with usable {' and '.join(missing)}"
205 217
206 218
207 def _select_netease_import_record( 219 def _select_netease_import_record(
...@@ -209,16 +221,17 @@ def _select_netease_import_record( ...@@ -209,16 +221,17 @@ def _select_netease_import_record(
209 current_record: dict, 221 current_record: dict,
210 candidate_records: list[dict], 222 candidate_records: list[dict],
211 songs_by_id: dict[int, dict], 223 songs_by_id: dict[int, dict],
212 ) -> dict | None: 224 ) -> tuple[dict | None, str]:
213 """为导入选择网易录音;当前录音的歌词或封面不可用时,改选同源的合格候选。""" 225 """为导入选择网易录音;当前录音的歌词或封面不可用时,改选同源的合格候选。
226 返回 (record, reason),record 为 None 时 reason 说明失败原因。"""
214 song_id = int(current_record['platform_unique_key']) 227 song_id = int(current_record['platform_unique_key'])
215 current_song = songs_by_id.get(song_id) 228 current_song = songs_by_id.get(song_id)
216 if not current_song: 229 if not current_song:
217 return None 230 return None, 'spider data missing'
218 needs_lyric = not _has_netease_lyric(current_song) 231 needs_lyric = not _has_netease_lyric(current_song)
219 needs_cover = not _has_netease_cover(hk_row, current_song) 232 needs_cover = not _has_netease_cover(hk_row, current_song)
220 if not needs_lyric and not needs_cover: 233 if not needs_lyric and not needs_cover:
221 return current_record 234 return current_record, 'ok'
222 235
223 for candidate in candidate_records: 236 for candidate in candidate_records:
224 cid = int(candidate['platform_unique_key']) 237 cid = int(candidate['platform_unique_key'])
...@@ -229,8 +242,13 @@ def _select_netease_import_record( ...@@ -229,8 +242,13 @@ def _select_netease_import_record(
229 continue 242 continue
230 if needs_cover and not _has_netease_cover(hk_row, song): 243 if needs_cover and not _has_netease_cover(hk_row, song):
231 continue 244 continue
232 return candidate 245 return candidate, 'ok'
233 return None 246 missing = []
247 if needs_lyric:
248 missing.append('lyric')
249 if needs_cover:
250 missing.append('cover')
251 return None, f"no candidate with usable {' and '.join(missing)}"
234 252
235 253
236 def _prepare_qq_payload(hk_row: dict, pr: dict, bucket, base_url, sp: dict, singer_list: list[dict]) -> dict: 254 def _prepare_qq_payload(hk_row: dict, pr: dict, bucket, base_url, sp: dict, singer_list: list[dict]) -> dict:
...@@ -626,6 +644,8 @@ def _prepare_import_payload( ...@@ -626,6 +644,8 @@ def _prepare_import_payload(
626 return None 644 return None
627 singer_key = int(song_data['id']) if platform == PLATFORM_QQ else int(platform_unique_id) 645 singer_key = int(song_data['id']) if platform == PLATFORM_QQ else int(platform_unique_id)
628 singer_list = singers_maps.get(platform, {}).get(singer_key, []) 646 singer_list = singers_maps.get(platform, {}).get(singer_key, [])
647 if not singer_list:
648 return None
629 payload = preparer(hk_row, pr, bucket, base_url, song_data, singer_list) 649 payload = preparer(hk_row, pr, bucket, base_url, song_data, singer_list)
630 payload['yinyan_record'] = { 650 payload['yinyan_record'] = {
631 'song_id': int(pending['song_id']), 651 'song_id': int(pending['song_id']),
...@@ -1708,23 +1728,23 @@ def run( ...@@ -1708,23 +1728,23 @@ def run(
1708 ) 1728 )
1709 continue 1729 continue
1710 if platform == PLATFORM_QQ: 1730 if platform == PLATFORM_QQ:
1711 selected = _select_qq_import_record( 1731 selected, reason = _select_qq_import_record(
1712 hk_row, pr, qq_records_by_song.get(src_id, []), qq_songs_map, 1732 hk_row, pr, qq_records_by_song.get(src_id, []), qq_songs_map,
1713 ) 1733 )
1714 elif platform == PLATFORM_KUGOU: 1734 elif platform == PLATFORM_KUGOU:
1715 selected = _select_kugou_import_record( 1735 selected, reason = _select_kugou_import_record(
1716 hk_row, pr, kugou_records_by_song.get(src_id, []), kugou_songs_map, 1736 hk_row, pr, kugou_records_by_song.get(src_id, []), kugou_songs_map,
1717 ) 1737 )
1718 elif platform == PLATFORM_NETEASE: 1738 elif platform == PLATFORM_NETEASE:
1719 selected = _select_netease_import_record( 1739 selected, reason = _select_netease_import_record(
1720 hk_row, pr, netease_records_by_song.get(src_id, []), netease_songs_map, 1740 hk_row, pr, netease_records_by_song.get(src_id, []), netease_songs_map,
1721 ) 1741 )
1722 else: 1742 else:
1723 selected = pr 1743 selected, reason = pr, 'ok'
1724 if selected is None: 1744 if selected is None:
1725 log.warning( 1745 log.warning(
1726 'Skip import without usable lyric/cover candidate: platform=%s source_song_id=%s record_id=%s', 1746 'Skip import (%s): platform=%s source_song_id=%s record_id=%s',
1727 platform, src_id, pending['record_id'], 1747 reason, platform, src_id, pending['record_id'],
1728 ) 1748 )
1729 rejected_pending.append(pending) 1749 rejected_pending.append(pending)
1730 continue 1750 continue
......
...@@ -102,7 +102,7 @@ def test_qq_cover_source_keeps_valid_hk_cover(): ...@@ -102,7 +102,7 @@ def test_qq_cover_source_keeps_valid_hk_cover():
102 def test_select_qq_import_record_replaces_missing_album_cover_with_same_source_candidate(): 102 def test_select_qq_import_record_replaces_missing_album_cover_with_same_source_candidate():
103 current = {'record_id': 10, 'platform_unique_key': 'bad-mid'} 103 current = {'record_id': 10, 'platform_unique_key': 'bad-mid'}
104 candidate = {'record_id': 20, 'platform_unique_key': 'good-mid'} 104 candidate = {'record_id': 20, 'platform_unique_key': 'good-mid'}
105 selected = runner._select_qq_import_record( 105 selected, reason = runner._select_qq_import_record(
106 {'cover_url': runner.QQ_MISSING_ALBUM_COVER}, 106 {'cover_url': runner.QQ_MISSING_ALBUM_COVER},
107 current, 107 current,
108 [current, candidate], 108 [current, candidate],
...@@ -112,12 +112,13 @@ def test_select_qq_import_record_replaces_missing_album_cover_with_same_source_c ...@@ -112,12 +112,13 @@ def test_select_qq_import_record_replaces_missing_album_cover_with_same_source_c
112 }, 112 },
113 ) 113 )
114 assert selected == candidate 114 assert selected == candidate
115 assert reason == 'ok'
115 116
116 117
117 def test_select_qq_import_record_replaces_missing_lyric_with_same_source_candidate(): 118 def test_select_qq_import_record_replaces_missing_lyric_with_same_source_candidate():
118 current = {'record_id': 10, 'platform_unique_key': 'without-lyric'} 119 current = {'record_id': 10, 'platform_unique_key': 'without-lyric'}
119 candidate = {'record_id': 20, 'platform_unique_key': 'with-lyric'} 120 candidate = {'record_id': 20, 'platform_unique_key': 'with-lyric'}
120 selected = runner._select_qq_import_record( 121 selected, reason = runner._select_qq_import_record(
121 {'cover_url': 'https://example.com/hk-cover.jpg'}, 122 {'cover_url': 'https://example.com/hk-cover.jpg'},
122 current, 123 current,
123 [current, candidate], 124 [current, candidate],
...@@ -127,16 +128,19 @@ def test_select_qq_import_record_replaces_missing_lyric_with_same_source_candida ...@@ -127,16 +128,19 @@ def test_select_qq_import_record_replaces_missing_lyric_with_same_source_candida
127 }, 128 },
128 ) 129 )
129 assert selected == candidate 130 assert selected == candidate
131 assert reason == 'ok'
130 132
131 133
132 def test_select_qq_import_record_returns_none_when_no_candidate_satisfies_missing_fields(): 134 def test_select_qq_import_record_returns_none_when_no_candidate_satisfies_missing_fields():
133 current = {'record_id': 10, 'platform_unique_key': 'bad-mid'} 135 current = {'record_id': 10, 'platform_unique_key': 'bad-mid'}
134 assert runner._select_qq_import_record( 136 result, reason = runner._select_qq_import_record(
135 {'cover_url': runner.QQ_MISSING_ALBUM_COVER}, 137 {'cover_url': runner.QQ_MISSING_ALBUM_COVER},
136 current, 138 current,
137 [current], 139 [current],
138 {'bad-mid': {'album_id': 0, 'cover': runner.QQ_MISSING_ALBUM_COVER, 'lyric': ''}}, 140 {'bad-mid': {'album_id': 0, 'cover': runner.QQ_MISSING_ALBUM_COVER, 'lyric': ''}},
139 ) is None 141 )
142 assert result is None
143 assert 'lyric' in reason and 'cover' in reason
140 144
141 145
142 def test_run_imports_only_pending_yinyan_platform_record(monkeypatch): 146 def test_run_imports_only_pending_yinyan_platform_record(monkeypatch):
......