1 /* -*- mode: C; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
2 /*
3 * Copyright (C) 2009-2011 Tiger Soldier <tigersoldi@gmail.com>
4 *
5 * This file is part of OSD Lyrics.
6 * OSD Lyrics is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * OSD Lyrics is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with OSD Lyrics. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #include <string.h>
20 #include <glib.h>
21 #include "ol_intl.h"
22 #include "ol_lrc_fetch_ttplayer.h"
23 #include "ol_lrc_fetch_utils.h"
24 #include "ol_utils.h"
25 #include "ol_debug.h"
26
27 #define MAX_CANDIDATE 5
28
29 const char *PREFIX_SEARCH_URL = "http://ttlrcct.qianqian.com/dll/lyricsvr.dll?sh?";
30 const char *DOWNLOAD_URL = "http://ttlrcct.qianqian.com/dll/lyricsvr.dll?dl?Id=%d&Code=%d&uid=01&mac=%012x";
31 const char *FIELD_DELIMIT = "\'";
32
33 struct CandidateParserData
34 {
35 const OlMusicInfo *info;
36 OlLrcCandidate *candidate_list;
37 int count;
38 };
39
40 static OlLrcCandidate* _search(const OlMusicInfo *info,
41 int *size,
42 const char* charset);
43 static int _download (OlLrcCandidate *candidate,
44 const char *pathname,
45 const char *charset);
46 static char* _encode_request_field (const char *value);
47
48 static void _parse_candidate (GMarkupParseContext *context,
49 const gchar *element_name,
50 const gchar **attribute_names,
51 const gchar **attribute_values,
52 gpointer user_data,
53 GError **error);
54 static size_t _remove_whitespace (char *str);
55 static gint32 _calc_download_code (int id, const char *data);
56
57 static gint32
_calc_download_code(int id,const char * data)58 _calc_download_code (int id, const char *data)
59 {
60 guint32 id1 = id & 0xff;
61 guint32 id2 = (id >> 8) & 0xff;
62 guint32 id3 = (id >> 16) & 0xff;
63 guint32 id4 = (id >> 24) & 0xff;
64
65 if (id3 == 0) id3 = ~id2 & 0xff;
66 if (id4 == 0) id4 = ~id1 & 0xff;
67 gint32 encoded_id = (id1 << 24) | (id3 << 16) | (id2 << 8) | id4;
68 gint32 encoded_data = 0;
69 size_t len = strlen(data);
70 int i;
71 for (i = len - 1; i >= 0; i--)
72 encoded_data = encoded_data + data[i] + (encoded_data << (i % 2 + 4));
73 gint32 encoded_data2 = 0;
74 for (i = 0; i < len; i++)
75 encoded_data2 = encoded_data2 + data[i] + (encoded_data2 << (i % 2 + 3));
76 gint32 ret = ((encoded_data ^ encoded_id) + (encoded_data2 | id)) * (encoded_data2 | encoded_id);
77 ret = ret * (encoded_data ^ id);
78 return ret;
79 }
80
81 static void
_parse_candidate(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,gpointer user_data,GError ** error)82 _parse_candidate (GMarkupParseContext *context,
83 const gchar *element_name,
84 const gchar **attribute_names,
85 const gchar **attribute_values,
86 gpointer user_data,
87 GError **error)
88 {
89 ol_debugf ("Got element: %s\n", element_name);
90 if (strcmp (element_name, "lrc") == 0)
91 {
92 const char **attr;
93 const char **value;
94 const char *url = NULL;
95 const char *title = NULL;
96 const char *artist = NULL;
97 struct CandidateParserData *data = (struct CandidateParserData*) user_data;
98 for (attr = attribute_names, value = attribute_values;
99 *attr != NULL && *value != NULL;
100 attr++, value++)
101 {
102 if (strcmp (*attr, "id") == 0)
103 url = *value;
104 else if (strcmp (*attr, "title") == 0)
105 title = *value;
106 else if (strcmp (*attr, "artist") == 0)
107 artist = *value;
108 else
109 ol_debugf ("Unknown attribute: %s=%s\n", *attr, *value);
110 }
111 if (url != NULL && (title != NULL || artist != NULL))
112 {
113 OlLrcCandidate *candidate = ol_lrc_candidate_new ();
114 ol_lrc_candidate_set_url (candidate, url);
115 if (title != NULL)
116 ol_lrc_candidate_set_title (candidate, title);
117 if (artist != NULL)
118 ol_lrc_candidate_set_artist (candidate, artist);
119 data->count = ol_lrc_fetch_add_candidate (data->info,
120 data->candidate_list,
121 data->count,
122 MAX_CANDIDATE,
123 candidate);
124 ol_lrc_candidate_free (candidate);
125 }
126 }
127 }
128
129 static size_t
_remove_whitespace(char * str)130 _remove_whitespace (char *str)
131 {
132 size_t len = 0;
133 size_t tail = 0;
134 for (; str[len] != '\0'; len++)
135 {
136 if (str[len] != ' ')
137 {
138 if (tail != len)
139 str[tail] = str[len];
140 tail++;
141 }
142 }
143 str[tail] = '\0';
144 return tail;
145 }
146
147 static char*
_encode_request_field(const char * value)148 _encode_request_field (const char *value)
149 {
150 size_t len = strlen (value);
151 size_t utf16_len = len * 2 + 1;
152 char *utf16 = g_new0 (char, utf16_len);
153 char *hex = NULL;
154 char *lower_str = g_utf8_strdown (value, -1);
155 g_strdelimit (lower_str, FIELD_DELIMIT, ' ');
156 len = _remove_whitespace (lower_str);
157 utf16_len = convert ("UTF-8", "UTF16LE",
158 (char*)lower_str, len,
159 utf16, utf16_len);
160 if ((int)utf16_len >= 0)
161 hex = ol_encode_hex (utf16, utf16_len);
162 g_free (lower_str);
163 g_free (utf16);
164 return hex;
165 }
166
167 static OlLrcCandidate *
_search(const OlMusicInfo * info,int * size,const char * charset)168 _search(const OlMusicInfo *info,
169 int *size,
170 const char* charset)
171 {
172 ol_assert_ret (info != NULL, NULL);
173 ol_assert_ret (size != NULL, NULL);
174 if (ol_music_info_get_title (info) == NULL ||
175 ol_music_info_get_artist (info) == NULL)
176 return NULL;
177 static OlLrcCandidate candidate_list[MAX_CANDIDATE];
178 OlLrcCandidate *retval = NULL;
179 GString *surl = g_string_new (PREFIX_SEARCH_URL);
180 char *encoded_title = _encode_request_field (ol_music_info_get_title (info));
181 char *encoded_artist = _encode_request_field (ol_music_info_get_artist (info));
182 g_string_append_printf (surl,
183 "Artist=%s&Title=%s&Flags=0",
184 encoded_artist, encoded_title);
185 g_free (encoded_title);
186 g_free (encoded_artist);
187 char *url = g_string_free (surl, FALSE);
188 ol_debugf ("url: %s\n", url);
189 struct memo content = {.mem_base = NULL, .mem_len = 0};
190 if (fetch_into_memory (url, NULL, NULL, NULL, 0, &content) != 0)
191 {
192 ol_debugf ("Search lyrics failed\n");
193 if (content.mem_base != NULL)
194 free (content.mem_base);
195 }
196 else
197 {
198 GMarkupParser parser = {.start_element = _parse_candidate};
199 GError *error = NULL;
200 struct CandidateParserData data = {
201 .info = info,
202 .candidate_list = candidate_list,
203 .count = 0,
204 };
205 GMarkupParseContext *context = g_markup_parse_context_new (&parser,
206 0,
207 &data,
208 NULL);
209 ol_debugf ("Search result: %s\n", content.mem_base);
210 if (!g_markup_parse_context_parse (context,
211 (const char*)content.mem_base,
212 content.mem_len,
213 &error))
214 {
215 ol_errorf ("failed to parse: %s\n", error->message);
216 g_error_free (error);
217 }
218 else if (!g_markup_parse_context_end_parse (context, &error))
219 {
220 ol_errorf ("failed to parse: %s\n", error->message);
221 g_error_free (error);
222 }
223 g_markup_parse_context_free (context);
224 retval = candidate_list;
225 *size = data.count;
226 free (content.mem_base);
227 }
228 g_free (url);
229 return retval;
230 }
231
232 static int
_download(OlLrcCandidate * candidate,const char * pathname,const char * charset)233 _download(OlLrcCandidate *candidate,
234 const char *pathname,
235 const char *charset)
236 {
237 ol_assert_ret (candidate != NULL, -1);
238 ol_assert_ret (pathname != NULL, -1);
239 int ret = 0;
240 int id = atoi (candidate->url);
241 char *data = g_strdup_printf ("%s%s", candidate->artist, candidate->title);
242 int code = _calc_download_code (id, data);
243 int mac = random ();
244 char *url = g_strdup_printf (DOWNLOAD_URL, id, code, mac);
245 ol_debugf ("Download url: %s\n", url);
246 struct memo content = {.mem_base = NULL, .mem_len = 0};
247 if (fetch_into_memory (url, NULL, NULL, NULL, 0, &content) < 0 ||
248 content.mem_len == 0)
249 {
250 ol_errorf ("Download lyrics (%s, %s, %s) failed\n",
251 candidate->url, candidate->title, candidate->artist);
252 ret = -1;
253 }
254 else
255 {
256 GError *error = NULL;
257 if (!g_file_set_contents (pathname,
258 content.mem_base,
259 content.mem_len,
260 &error))
261 {
262 ol_errorf ("Cannot save lyric file %s: %s\n",
263 error->message);
264 g_error_free (error);
265 ret = -1;
266 }
267 }
268 if (content.mem_base != NULL)
269 free (content.mem_base);
270 g_free (data);
271 g_free (url);
272 return ret;
273 }
274
275 static OlLrcFetchEngine ttplayer = {
276 N_("ttPlayer"),
277 _search,
278 _download,
279 };
280
281 OlLrcFetchEngine *
ol_lrc_fetch_ttplayer_engine()282 ol_lrc_fetch_ttplayer_engine ()
283 {
284 return &ttplayer;
285 }
286