xref: /MusicPlayer2/MusicPlayer2/InternetCommon.cpp (revision 26cefa3ce191eaff4754c23aa55c7bab36bd518b)
1 #include "stdafx.h"
2 #include "InternetCommon.h"
3 #include "Resource.h"
4 #include "MusicPlayer2.h"
5 
6 
CInternetCommon()7 CInternetCommon::CInternetCommon()
8 {
9 }
10 
11 
~CInternetCommon()12 CInternetCommon::~CInternetCommon()
13 {
14 }
15 
URLEncode(const wstring & wstr)16 wstring CInternetCommon::URLEncode(const wstring & wstr)
17 {
18 	string str_utf8;
19 	wstring result{};
20 	wchar_t buff[4];
21 	str_utf8 = CCommon::UnicodeToStr(wstr, CodeType::UTF8_NO_BOM);
22 	for (const auto& ch : str_utf8)
23 	{
24 		if (ch == ' ')
25 			result.push_back(L'+');
26 		else if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'))
27 			result.push_back(static_cast<wchar_t>(ch));
28 		else if (ch == '-' || ch == '_' || ch == '.' || ch == '!' || ch == '~' || ch == '*'/* || ch == '\''*/ || ch == '(' || ch == ')')
29 			result.push_back(static_cast<wchar_t>(ch));
30 		else
31 		{
32 			swprintf_s(buff, L"%%%x", static_cast<unsigned char>(ch));
33 			result += buff;
34 		}
35 	}
36 	return result;
37 }
38 
GetURL(const wstring & str_url,wstring & result,bool custom_ua,bool allow_other_codes)39 bool CInternetCommon::GetURL(const wstring & str_url, wstring & result, bool custom_ua, bool allow_other_codes)
40 {
41     wstring log_info;
42     log_info = L"http get: " + str_url;
43     theApp.WriteLog(log_info, NonCategorizedSettingData::LT_NORMAL);
44 
45 	bool sucessed{ false };
46 	CInternetSession session{};
47     if (custom_ua) {
48         session.SetOption(INTERNET_OPTION_USER_AGENT, (LPVOID)L"MuiscPlayer2" APP_VERSION, wcslen(L"MuiscPlayer2" APP_VERSION) * sizeof(wchar_t));
49     }
50 	CHttpFile* pfile{};
51 	try
52 	{
53 		pfile = (CHttpFile *)session.OpenURL(str_url.c_str());
54 		DWORD dwStatusCode;
55 		pfile->QueryInfoStatusCode(dwStatusCode);
56 		if (allow_other_codes || dwStatusCode == HTTP_STATUS_OK)
57 		{
58 			CString content;
59 			CString data;
60 			while (pfile->ReadString(data))
61 			{
62 				content += data;
63 			}
64 			result = CCommon::StrToUnicode(string{ (const char*)content.GetString() }, CodeType::UTF8);	//获取网页内容,并转换成Unicode编码
65 			sucessed = true;
66 		}
67 		pfile->Close();
68 		delete pfile;
69 		session.Close();
70 	}
71 	catch (CInternetException* e)
72 	{
73         wstring log_info = L"http get " + str_url + L"error. Error code: ";
74         log_info += std::to_wstring(e->m_dwError);
75         theApp.WriteLog(log_info, NonCategorizedSettingData::LT_ERROR);
76 
77 		if (pfile != nullptr)
78 		{
79 			pfile->Close();
80 			delete pfile;
81 		}
82 		session.Close();
83 		sucessed = false;
84 		e->Delete();
85 	}
86 	return sucessed;
87 }
88 
HttpPost(const wstring & str_url,wstring & result)89 int CInternetCommon::HttpPost(const wstring& str_url, wstring& result) {
90     string body;
91     wstring headers;
92     return HttpPost(str_url, result, body, headers);
93 }
94 
HttpPost(const wstring & str_url,wstring & result,const wstring & body,wstring & headers,bool custom_ua)95 int CInternetCommon::HttpPost(const wstring& str_url, wstring& result, const wstring& body, wstring& headers, bool custom_ua) {
96     const auto& tmp = CCommon::UnicodeToStr(body, CodeType::UTF8_NO_BOM);
97     return HttpPost(str_url, result, tmp, headers, custom_ua);
98 }
99 
HttpPost(const wstring & str_url,wstring & result,const string & body,wstring & headers,bool custom_ua)100 int CInternetCommon::HttpPost(const wstring & str_url, wstring & result, const string& body, wstring& headers, bool custom_ua)
101 {
102     wstring log_info;
103     log_info = L"http post: " + str_url;
104     theApp.WriteLog(log_info, NonCategorizedSettingData::LT_NORMAL);
105 
106 	CInternetSession session;
107 	CHttpConnection* pConnection{};
108 	CHttpFile* pFile{};
109 	CString strServer;
110 	CString strObject;
111 	DWORD dwServiceType;
112 	INTERNET_PORT nPort;
113 
114     if (custom_ua) {
115         session.SetOption(INTERNET_OPTION_USER_AGENT, (LPVOID)L"MuiscPlayer2" APP_VERSION, wcslen(L"MuiscPlayer2" APP_VERSION) * sizeof(wchar_t));
116     }
117 
118 	AfxParseURL(str_url.c_str(), dwServiceType, strServer, strObject, nPort);
119 
120 	if (AFX_INET_SERVICE_HTTP != dwServiceType && AFX_INET_SERVICE_HTTPS != dwServiceType)
121 		return FAILURE;
122 
123 	try
124 	{
125 		pConnection = session.GetHttpConnection(strServer,
126 			dwServiceType == AFX_INET_SERVICE_HTTP ? NORMAL_CONNECT : SECURE_CONNECT,
127 			nPort);
128 		pFile = pConnection->OpenRequest(_T("POST"), strObject,
129 			NULL, 1, NULL, NULL,
130 			(dwServiceType == AFX_INET_SERVICE_HTTP ? NORMAL_REQUEST : SECURE_REQUEST));
131 
132 		pFile->SendRequest(headers.empty() ? NULL : headers.c_str(), headers.size(), body.empty() ? NULL : (LPVOID)body.c_str(), body.size());
133 
134 		CString content;
135 		CString data;
136 		while (pFile->ReadString(data))
137 		{
138 			content += data;
139 		}
140 		result = CCommon::StrToUnicode(string{ (const char*)content.GetString() }, CodeType::UTF8);
141 
142 
143 		pFile->Close();
144 		delete pFile;
145 		pConnection->Close();
146 		delete pConnection;
147 		session.Close();
148 	}
149 	catch (CInternetException* e)
150 	{
151 		pFile->Close();
152 		delete pFile;
153 		pConnection->Close();
154 		delete pConnection;
155 		session.Close();
156 		DWORD dwErrorCode = e->m_dwError;
157 		e->Delete();
158 		//DWORD dwError = GetLastError();
159 		//PRINT_LOG("dwError = %d", dwError, 0);
160 
161         wstring log_info = L"http post " + str_url + L"error. Error code: ";
162         log_info += std::to_wstring(dwErrorCode);
163         theApp.WriteLog(log_info, NonCategorizedSettingData::LT_ERROR);
164 
165 		if (ERROR_INTERNET_TIMEOUT == dwErrorCode)
166 			return OUTTIME;
167 		else
168 			return FAILURE;
169 	}
170 	return SUCCESS;
171 }
172 
173 
DeleteStrSlash(wstring & str)174 void CInternetCommon::DeleteStrSlash(wstring & str)
175 {
176 	for (int i{}; i < static_cast<int>(str.size() - 1); i++)
177 	{
178 		if (str[i] == '\\' && str[i + 1] == '\"')
179 		{
180 			str.erase(i, 1);
181 		}
182 	}
183 }
184 
DisposeSearchResult(vector<ItemInfo> & down_list,const wstring & search_result,int result_count)185 void CInternetCommon::DisposeSearchResult(vector<ItemInfo>& down_list, const wstring& search_result, int result_count)
186 {
187 	down_list.clear();
188 	ItemInfo item;
189 	int index1{}, index2{}, index3{}, index4{};
190 	//while (true)
191 	for (int i{}; i < result_count; i++)
192 	{
193 		//获取歌曲的ID
194 		if (i == 0)
195 		{
196 			index1 = search_result.find(L"\"songs\":[{\"id\":", index1 + 1);
197 			if (index1 == string::npos) break;
198 			index2 = search_result.find(L',', index1);
199 			item.id = search_result.substr(index1 + 15, index2 - index1 - 15);
200 		}
201 		else
202 		{
203 			index1 = search_result.find(L",{\"id\":", index1 + 1);
204 			if (index1 == string::npos) break;
205 			index2 = search_result.find(L',', index1 + 1);
206 			item.id = search_result.substr(index1 + 7, index2 - index1 - 7);
207 		}
208 
209 		//获取歌曲标题
210 		index2 = search_result.find(L"name", index1);
211 		if (index2 == string::npos) continue;
212 		index3 = search_result.find(L"\",\"", index2);
213 		wstring title = search_result.substr(index2 + 7, index3 - index2 - 7);
214 		if (search_result.substr(index3 + 3, 6) == L"picUrl")	//如果找到的“name”后面的字符串是“picUrl”,说明这项name的值不是
215 		{														//另一首歌的标题,而是上一首歌的艺术家,上一首歌有多个艺术家
216 			if (!down_list.empty())
217 			{
218 				down_list.back().artist += L'/';
219 				down_list.back().artist += title;
220 			}
221 			continue;
222 		}
223 		else
224 		{
225 			item.title = title;
226 		}
227 
228 		//获取歌曲的艺术家
229 		index2 = search_result.find(L"artists", index1);
230 		if (index2 == string::npos) continue;
231 		index3 = search_result.find(L"name", index2);
232 		index4 = search_result.find(L"\",\"", index3);
233 		item.artist = search_result.substr(index3 + 7, index4 - index3 - 7);
234 
235 		//获取歌曲的唱片集
236 		index2 = search_result.find(L"\"album\"", index1);
237 		if (index2 == string::npos) continue;
238 		index3 = search_result.find(L"name", index2);
239 		index4 = search_result.find(L"\",\"", index3);
240 		item.album = search_result.substr(index3 + 7, index4 - index3 - 7);
241 
242         //获取时长
243         index2 = search_result.find(L"\"duration\"", index1);
244         if (index2 != string::npos)
245         {
246             index3 = search_result.find(L',', index2);
247             wstring str_duration = search_result.substr(index2 + 11, index3 - index2 - 11);
248             item.duration = _wtoi(str_duration.c_str());
249         }
250 
251 		DeleteStrSlash(item.title);
252 		DeleteStrSlash(item.artist);
253 		DeleteStrSlash(item.album);
254 		down_list.push_back(item);
255 	}
256 }
257 
CharacterSimilarDegree(wchar_t ch1,wchar_t ch2)258 double CInternetCommon::CharacterSimilarDegree(wchar_t ch1, wchar_t ch2)
259 {
260 	if (ch1 == ch2)
261 		return 1;
262 	else if ((ch1 >= 'A' && ch1 <= 'Z' && ch2 == ch1 + 32) || (ch1 >= 'a' && ch1 <= 'z' && ch2 == ch1 - 32))
263 		return 0.8;
264 	else if ((ch1 == L'1' && ch2 == L'一') || (ch1 == L'一' && ch2 == L'1')
265 		|| (ch1 == L'2' && ch2 == L'二') || (ch1 == L'二' && ch2 == L'2')
266 		|| (ch1 == L'3' && ch2 == L'三') || (ch1 == L'三' && ch2 == L'3')
267 		|| (ch1 == L'4' && ch2 == L'四') || (ch1 == L'四' && ch2 == L'4')
268 		|| (ch1 == L'5' && ch2 == L'五') || (ch1 == L'五' && ch2 == L'5')
269 		|| (ch1 == L'6' && ch2 == L'六') || (ch1 == L'六' && ch2 == L'6')
270 		|| (ch1 == L'7' && ch2 == L'七') || (ch1 == L'七' && ch2 == L'7')
271 		|| (ch1 == L'8' && ch2 == L'八') || (ch1 == L'八' && ch2 == L'8')
272 		|| (ch1 == L'9' && ch2 == L'九') || (ch1 == L'九' && ch2 == L'9')
273 		|| (ch1 == L'0' && ch2 == L'零') || (ch1 == L'零' && ch2 == L'0')
274 		)
275 		return 0.7;
276 	else
277 		return 0.0;
278 }
279 
StringSimilarDegree_LD(const wstring & srcString,const wstring & matchString)280 double CInternetCommon::StringSimilarDegree_LD(const wstring & srcString, const wstring & matchString)
281 {
282 	/*
283 	编辑距离算法,来自“编辑距离——百度百科”(https://baike.baidu.com/history/编辑距离/8010193/131513486)
284 	比如要计算cafe和coffee的编辑距离。cafe→caffe→coffe→coffee
285 	先创建一个6×8的表(cafe长度为4,coffee长度为6,各加2)
286 	表1:
287 	|   |   | c | o | f | f | e | e |
288 	|   |   |   |   |   |   |   |   |
289 	| c |   |   |   |   |   |   |   |
290 	| a |   |   |   |   |   |   |   |
291 	| f |   |   |   |   |   |   |   |
292 	| e |   |   |   |   |   |   |   |
293 
294 	接着,在如下位置填入数字(表2):
295 	表2:
296 	|   |   | c | o | f | f | e | e |
297 	|   | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
298 	| c | 1 |   |   |   |   |   |   |
299 	| a | 2 |   |   |   |   |   |   |
300 	| f | 3 |   |   |   |   |   |   |
301 	| e | 4 |   |   |   |   |   |   |
302 
303 	从3,3格开始,开始计算。取以下三个值的最小值:
304 	如果最上方的字符等于最左方的字符,则为左上方的数字。否则为左上方的数字+1。(对于3,3来说为0)
305 	左方数字+1(对于3,3格来说为2)
306 	上方数字+1(对于3,3格来说为2)
307 
308 	因此为格3,3为0(表3)
309 	表3:
310 	|   |   | c | o | f | f | e | e |
311 	|   | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
312 	| c | 1 | 0 |   |   |   |   |   |
313 	| a | 2 |   |   |   |   |   |   |
314 	| f | 3 |   |   |   |   |   |   |
315 	| e | 4 |   |   |   |   |   |   |
316 
317 	从3,4(三行四列)格开始,开始计算。取以下三个值的最小值:
318 	* 如果最上方的字符等于最左方的字符,则为左上方的数字。否则为左上方的数字+1。(对于3,4来说为2)
319 	* 左方数字+1(对于3,4格来说为1)
320 	* 上方数字+1(对于3,4格来说为3)
321 	因此为格3,3为0(表4)
322 	表4:
323 	|   |   | c | o | f | f | e | e |
324 	|   | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
325 	| c | 1 | 0 | 1 |   |   |   |   |
326 	| a | 2 |   |   |   |   |   |   |
327 	| f | 3 |   |   |   |   |   |   |
328 	| e | 4 |   |   |   |   |   |   |
329 
330 	循环操作,推出下表
331 	|   |   | c | o | f | f | e | e |
332 	|   | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
333 	| c | 1 | 0 | 1 | 2 | 3 | 4 | 5 |
334 	| a | 2 | 1 | 1 | 2 | 3 | 4 | 5 |
335 	| f | 3 | 2 | 2 | 1 | 2 | 3 | 4 |
336 	| e | 4 | 3 | 3 | 2 | 2 | 2 | 3 |
337 	取右下角,得编辑距离为3。
338 	*/
339 
340 	int n = srcString.size();
341 	int m = matchString.size();
342 
343     const int MAX_LENGTH = 256;
344     if (n <= 0 || n > MAX_LENGTH || m <= 0 || m > MAX_LENGTH || std::abs(n - m) > MAX_LENGTH)      //如果要比较的字符串过长,则不计算
345         return 0;
346 
347 	//创建表
348 	vector<vector<double>> d(n + 1, vector<double>(m + 1));
349 	double cost; // cost
350 
351 	//// Step 1(如果其中一个字符串长度为0,则相似度为1)?
352 	//if (n == 0 || m == 0) return 0.0;	//如果其中一个字符串长度为0,则相似度为0
353 
354 	// Step 2,给表的第1行和第1列填入数字
355 	for (int i{}; i <= n; ++i) d[i][0] = i;
356 	for (int j{}; j <= m; ++j) d[0][j] = j;
357 	// Step 3
358 	for (int i = 1; i <= n; i++)
359 	{
360 		//Step 4
361 		for (int j = 1; j <= m; j++)
362 		{
363 			// Step 5,遍历表格剩下的格子计算每个格子的值
364 			wchar_t ch1 = matchString[j - 1];
365 			wchar_t ch2 = srcString[i - 1];
366 
367 			//比较最上方的字符和最左方的字符
368 			cost = 1 - CharacterSimilarDegree(ch1, ch2);
369 
370 			// Step 6,取3个值中的最小值
371 			d[i][j] = CCommon::Min3(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
372 		}
373 	}
374 
375 	// Step 7
376 	double ds = 1 - (double)d[n][m] / max(srcString.size(), matchString.size());
377 
378 	return ds;
379 }
380 
381 
SelectMatchedItem(const vector<ItemInfo> & down_list,const wstring & title,const wstring & artist,const wstring & album,const wstring & filename,bool write_log)382 int CInternetCommon::SelectMatchedItem(const vector<ItemInfo>& down_list, const wstring & title, const wstring & artist, const wstring & album, const wstring & filename, bool write_log)
383 {
384 	/*
385 	匹配度计算:
386 	通过计算以下的匹配度,并设置权值,将每一项的匹配度乘以权值再相加,得到的值最大的就是最匹配的一项
387 	项目			 权值
388 	标题——标题         0.4
389 	艺术家——艺术家     0.4
390 	唱片集——唱片集     0.3
391 	文件名——标题       0.3
392 	文件名——艺术家     0.2
393 	列表中的排序       0.05
394     时长              0.6
395 	*/
396 	if (down_list.empty()) return -1;
397 	vector<double> weights;		//储存列表中每一项的权值
398 
399 								//计算每一项的权值
400 	for (size_t i{}; i<down_list.size(); i++)
401 	{
402 		double weight;
403 		weight = 0;
404 		weight += (StringSimilarDegree_LD(title, down_list[i].title) * 0.4);
405 		weight += (StringSimilarDegree_LD(artist, down_list[i].artist) * 0.4);
406 		weight += (StringSimilarDegree_LD(album, down_list[i].album) * 0.3);
407 		weight += (StringSimilarDegree_LD(filename, down_list[i].title) * 0.3);
408 		weight += (StringSimilarDegree_LD(filename, down_list[i].artist) * 0.3);
409 
410 		weight += ((1 - i * 0.02) * 0.05);			//列表中顺序的权值,一般来说,网易云音乐的搜索结果的返回结果中
411 													//排在越前面的关联度就越高,这里取第一项为1,之后每一项减0.02,最后再乘以0.05
412 		weights.push_back(weight);
413 	}
414 
415 	//查找权值最大的项
416 	double max_weight = weights[0];
417 	int max_index{};
418 	for (size_t i{ 1 }; i < weights.size(); i++)
419 	{
420 		if (weights[i] > max_weight)
421 		{
422 			max_weight = weights[i];
423 			max_index = i;
424 		}
425 	}
426 
427 	//如果权值最大项的权值小于0.3,则判定没有匹配的项,返回-1
428 	if (max_weight < 0.3)
429 		max_index = -1;
430 
431 #ifdef DEBUG
432 	if (write_log)
433 	{
434 		CString out_info{ _T("\n==============================================================================\n") };
435 		CString tmp;
436 		out_info += _T("\n歌曲信息:\n");
437 		out_info += _T("文件名:");
438 		out_info += filename.c_str();
439 		out_info += _T("\n标题:");
440 		out_info += title.c_str();
441 		out_info += _T("\n艺术家:");
442 		out_info += artist.c_str();
443 		out_info += _T("\n唱片集:");
444 		out_info += album.c_str();
445 
446 		out_info += _T("\n搜索结果:\n");
447 		out_info += _T("序号\t歌曲ID\t标题\t艺术家\t唱片集\n");
448 		for (size_t i{}; i<down_list.size(); i++)
449 		{
450 			tmp.Format(_T("%d\t%s\t%s\t%s\t%s\n"), i + 1, down_list[i].id.c_str(), down_list[i].title.c_str(), down_list[i].artist.c_str(), down_list[i].album.c_str());
451 			out_info += tmp;
452 		}
453 
454 		out_info += _T("各项权值:\n");
455 		for (size_t i{}; i < weights.size(); i++)
456 		{
457 			tmp.Format(_T("%d\t%f\n"), i + 1, weights[i]);
458 			out_info += tmp;
459 		}
460 
461 		tmp.Format(_T("最佳匹配项:%d\n\n"), max_index + 1);
462 		out_info += tmp;
463 
464 		CCommon::WriteLog(L".\\search.log", wstring{ out_info });
465 	}
466 #endif // DEBUG
467 
468 	return max_index;
469 }
470 
SearchSongAndGetMatched(const wstring & title,const wstring & artist,const wstring & album,const wstring & file_name,bool message,DownloadResult * result)471 CInternetCommon::ItemInfo CInternetCommon::SearchSongAndGetMatched(const wstring & title, const wstring & artist, const wstring & album, const wstring & file_name, bool message, DownloadResult* result)
472 {
473 	//设置搜索关键字
474 	wstring search_result;		//查找歌曲返回的结果
475 	wstring keyword;		//查找的关键字
476     if (title.empty() || theApp.m_str_table.LoadText(L"TXT_EMPTY_TITLE") == title)            // 如果没有标题信息,就把文件名设为搜索关键字
477 	{
478 		keyword = file_name;
479 		size_t index = keyword.rfind(L'.');		//查找最后一个点
480 		keyword = keyword.substr(0, index);		//去掉扩展名
481 	}
482     else if (artist.empty() || theApp.m_str_table.LoadText(L"TXT_EMPTY_ARTIST") == artist)    //如果有标题信息但是没有艺术家信息,就把标题设为搜索关键字
483 	{
484 		keyword = title;
485 	}
486 	else		//否则将“艺术家 标题”设为搜索关键字
487 	{
488 		keyword = artist + L' ' + title;
489 	}
490 
491 	//搜索歌曲
492 	wstring keyword_url = CInternetCommon::URLEncode(keyword);		//将搜索关键字转换成URL编码
493 	CString url;
494 	url.Format(L"http://music.163.com/api/search/get/?s=%s&limit=20&type=1&offset=0", keyword_url.c_str());
495 	int rtn = CInternetCommon::HttpPost(wstring(url), search_result);		//向网易云音乐的歌曲搜索API发送http的POST请求
496 	if (rtn != 0)
497 	{
498 		if(message)
499 		{
500 			const wstring& info = theApp.m_str_table.LoadText(L"MSG_NETWORK_CONNECTION_FAILED");
501 			AfxMessageBox(info.c_str(), NULL, MB_ICONWARNING);
502 		}
503         if (result != nullptr)
504             *result = DR_NETWORK_ERROR;
505 
506 		return CInternetCommon::ItemInfo();
507 	}
508 
509 	//处理返回结果
510 	vector<CInternetCommon::ItemInfo> down_list;
511 	CInternetCommon::DisposeSearchResult(down_list, search_result);		//处理返回的查找结果,并将结果保存在down_list容器里
512 	if (down_list.empty())
513 	{
514 		if (message)
515 		{
516 			const wstring& info = theApp.m_str_table.LoadText(L"MSG_NETWORK_CANNOT_FIND_THIS_SONG");
517 			AfxMessageBox(info.c_str(), NULL, MB_ICONWARNING);
518 		}
519         if (result != nullptr)
520             *result = DR_DOWNLOAD_ERROR;
521         return CInternetCommon::ItemInfo();
522 	}
523 
524 	//计算最佳选择项
525 	wstring _title = title;
526 	wstring _artist = artist;
527 	wstring _album = album;
528     if (theApp.m_str_table.LoadText(L"TXT_EMPTY_TITLE") == title) _title.clear();
529     if (theApp.m_str_table.LoadText(L"TXT_EMPTY_ARTIST") == artist) _artist.clear();
530     if (theApp.m_str_table.LoadText(L"TXT_EMPTY_ALBUM") == album) _album.clear();
531 	if (_title.empty())
532 		_title = keyword;
533 	int best_matched = CInternetCommon::SelectMatchedItem(down_list, _title, _artist, _album, file_name, true);
534 	if (best_matched < 0)
535 	{
536 		if (message)
537 		{
538 			const wstring& info = theApp.m_str_table.LoadText(L"MSG_NETWORK_CANNOT_FIND_THIS_SONG");
539 			AfxMessageBox(info.c_str(), NULL, MB_ICONWARNING);
540 		}
541         if (result != nullptr)
542             *result = DR_DOWNLOAD_ERROR;
543         return CInternetCommon::ItemInfo();
544 	}
545 
546 	//获返回最佳匹配项
547     if (result != nullptr)
548         *result = DR_SUCCESS;
549     return down_list[best_matched];
550 }
551