今年の夏は各地で記録的な猛暑の日々が続いていますが、
データセンターなどが利用できる大手ディストリビューションとは異なり、
今年の暑さは特に厳しくて、
さて、
今回はその作業中に遭遇した、
TotemとYouTube plugin
この連載の3回目あたりでも紹介したように、
このTotemの各種機能の中で、
このプラグインは便利なものの、
ところが、

このエラーは前回も経験したので、
さっそくその修正コードを眺めてみましたが、
Totem-2.30用YouTubeプラグインパッチ
まずはTotem-2.
23 g_regex_match (self->regex, contents, 0, &match_info);
24 if (g_match_info_matches (match_info) == TRUE) {
25 - gchar *t_param, *s;
26 - const gchar *fmt_param;
27 - GString *video_uri_string;
28 + gchar *fmt_url_map_escaped, *fmt_url_map;
29 + gchar **mappings, **i;
30 /* We have a match */
31 - s = g_match_info_fetch (match_info, 1);
32 - t_param = g_uri_unescape_string (s, NULL);
33 - if (t_param == NULL)
34 - t_param = s;
35 - else
36 - g_free (s);
37 - fmt_param = get_fmt_param (self);
38 -
39 - video_uri_string = g_string_new ("http://www.youtube.com/get_video?video_id=");
40 - g_string_append_uri_escaped (video_uri_string, video_id, NULL, TRUE);
41 - g_string_append (video_uri_string, "&t=");
42 - g_string_append_uri_escaped (video_uri_string, t_param, NULL, TRUE);
43 - g_string_append (video_uri_string, fmt_param);
44 -
45 - video_uri = g_string_free (video_uri_string, FALSE);
46 + fmt_url_map_escaped = g_match_info_fetch (match_info, 1);
47 + fmt_url_map = g_uri_unescape_string (fmt_url_map_escaped, NULL);
48 + g_free (fmt_url_map_escaped);
49 +
50 + /* The fmt_url_map parameter is in the following format:
51 + * fmt1|uri1,fmt2|uri2,fmt3|uri3,...
52 + * where fmtN is an identifier for the audio and video encoding and resolution as described here:
53 + * (http://en.wikipedia.org/wiki/YouTube#Quality_and_codecs) and uriN is the playback URI for that format. */
54 + mappings = g_strsplit (fmt_url_map, ",", 0);
55 +
56 + for (i = mappings; *i != NULL; i++) {
57 + /* For the moment we just take the first format we get */
58 + gchar **mapping = g_strsplit (*i, "|", 2);
59 + video_uri = g_strdup (mapping[1]);
60 + g_strfreev (mapping);
61 + break;
62 + }
パッチファイルに詳しくない人でも、
このパッチファイルを見る限り、
具体的には、
YouTube用プラグインにどのような修正が必要なのかをざっと把握した上で、
Python版のYouTubeプラグイン
上述のように、
どうやってこれらのクラスを使うのだろう、
% grep -C2 youtube.com totem.py
128
129 """Set up the service"""
130 self.service = gdata.service.GDataService (account_type = "HOSTED_OR_GOOGLE", server = "gdata.youtube.com")
131
132 def deactivate (self, totem):
--
204 def resolve_t_param (self, youtube_id):
205 """We have to get the t parameter from the actual video page, since Google changed how their URLs work"""
206 stream = urllib.urlopen ("http://youtube.com/watch?v=" + urllib.quote (youtube_id))
207 regexp1 = re.compile ("swfArgs.*\"t\": \"([^\"]+)\"")
208 regexp2 = re.compile ("</head>")
--
242
243 """Open the video in the browser"""
244 os.spawnlp (os.P_NOWAIT, "xdg-open", "xdg-open", "http://www.youtube.com/watch?v=" + urllib.quote (youtube_id) + self.get_fmt_string ())
245
246 def on_button_press_event (self, widget, event):
--
335
336 if t_param != "":
337 mrl = "http://www.youtube.com/get_video?video_id=" + urllib.quote (youtube_id) + "&t=" + t_param + self.get_fmt_string ()
338
339 gobject.idle_add (self._append_to_liststore, treeview_name, pixbuf, entry.title.text, mrl, youtube_id, search_token)
grepに与えた-C2というオプションは、
ざっと見、
この部分のソースコードを調べてみると、
204 def resolve_t_param (self, youtube_id):
205 """We have to get the t parameter from the actual video page, since Google changed how their URLs work"""
206 stream = urllib.urlopen ("http://youtube.com/watch?v=" + urllib.quote (youtube_id))
207 regexp1 = re.compile ("swfArgs.*\"t\": \"([^\"]+)\"")
208 regexp2 = re.compile ("</head>")
209
210 contents = stream.read ()
211 if contents != "":
212 """Check for the t parameter, which is now in a JavaScript array on the video page"""
213 matches = regexp1.search (contents)
214 if (matches != None):
215 stream.close ()
216 return matches.group (1)
217
218 """Check to see if we've come to the end of the <head> tag; in which case, we should give up"""
219 if (regexp2.search (contents) != None):
220 stream.close ()
221 return ""
222
223 stream.close ()
224 return ""
225
オブジェクト指向のPythonの場合、
デバッグ、デバッグ、デバッグ
修正すべき箇所の手掛りが見えてきたので、
まず210行目の stream.
210 contents = stream.read ()
211 print "my_debug:",contents
212 if contents != "":
このプラグインはPythonで書かれているので、

この出力をファイルに保存してみると、
1 my_debug: <!DOCTYPE html>
2 <html lang="" dir="ltr">
3 <!-- machid: ta3Z2bmtLVURHRVBnejN3ODRJTWU3M0F6UTNfck9jWjFCRF9nVEstUllVXzE0aExvWnVLS1ln -->
4 <head>
5 <script>
6 var yt = yt || {};
7 yt.timing = yt.timing || {};
8 yt.timing.cookieName = 'VISITOR_INFO1_LIVE';
9 yt.timing.tick = function(label) %7B
....
つらつらとこのログファイルを眺めていると、
261 var swfHTML = (isIE) ? "<object height=\"38" + "5\" width=\"64" + "0\" classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" id=\"movie_player\" ><param name=\"movie\" value=\"http:\/\/s.ytimg.com\/yt\/swf\/watch_as3-vfl180154.swf\"><param name=\"flashvars\" ...
どうやらこの行が怪しそうだ、
var swfHTML = (isIE) ? "<object height=\"38" + "5\" width=\"64" + "0\" classid=\"..
rv.2.thumbnailUrl=http%3A%2F%2Fi3.ytimg.com%2Fvi%2F240Vq6tIxio%2Fdefault.jpg&
rv.0.url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DxBZOlipfjkQ&
rv.0.view_count=888291&
enablecsi=1&
rv.2.title=Bad+Apple%21%21+-+Stop+Motion+PV&
rv.6.author=tripflag&
rv.3.view_count=192392&
is_doubleclick_tracked=1&
rv.4.thumbnailUrl=http%3A%2F%2Fi3.ytimg.com%2Fvi%2F2GIMgD_gShU%2Fdefault.jpg&
fmt_url_map=34%7Chttp%3A%2F%2Fv4.lscache5.c.youtube.com%2Fvideoplayback%3Fip%3D210.0.0.0%26sparams%3Did%252Cexpire..
csi_page_type=watch&
keywords=%E6%9D%B1%E6%96%B9%2CTouhou%2CBad%2CApple%21%21%2CPV%2C%E9%9F%B3%E6%A5%BD%2C%E6%9D%B1%E6%96%B9%E6%89%8B%E6%..
cr=JP&
cc3_module=http%3A%2F%2Fs.ytimg.com%2Fyt%2Fswf%2Fsubtitles3_module-vfl180154.swf&
この結果を見るとfmt_
71 - self->regex = g_regex_new ("swfHTML = .*&t=([^&]+)&", G_REGEX_OPTIMIZE, 0, NULL);
72 + self->regex = g_regex_new ("swfHTML = .*&fmt_url_map=([^&]+)&", G_REGEX_OPTIMIZE, 0, NULL);
このパッチを見る限り、
改めて、
fmt_url_map=34|http://v4.lscache5.c.youtube.com/videoplayback?ip=210.0.0.0&sparams=id%2Cexpire.. csi_page_type=watch&..
改めて、
207 # regexp1 = re.compile ("swfHTML = .*&t=([^&]+)&")
208 regexp1 = re.compile ("fmt_stream_map=([^&]+)&")
209 regexp2 = re.compile ("</head>")
合わせて、
213 matches = regexp1.search (contents)
214 print "my_debug_2:", matches.group(1)
215 if (matches != None):
216 stream.close ()
217 return matches.group (1)
これらの修正を加えて再起動し再度ログを取って調べると、
my_debug_2: 34%7Chttp%3A%2F%2Fv4.lscache5.c.youtube.com%2Fvideoplayback%3Fip%3D210.0.0.0%26sparams%3Did%252Cexpire%252Cip%252Cipbits%252Citag%252Calgorithm%252Cburst%252Cfactor%26algorithm%3Dthrottle-factor%26itag%3D34%26ipbits%3D8%26burst%3D40%26sver%3D3%26expire%3D1280865600%26key%3Dyt1%26signature%3DA1BEA852CE9BCCEA89F4DEB5EE912081BDC79...
ここまで来れば、
336 """Get the video stream MRL"""
337 quoted_param = self.resolve_t_param(youtube_id)
338
339 fmt_stream_param = urllib.unquote(quoted_param)
340 param_list = fmt_stream_param.split(',')
341 tmp_param = param_list[0]
342 fmt_list = tmp_param.split('|')
343 t_param = fmt_list[1]
344 print "my_debug_3: quoted_param:", quoted_param
345 print "my_debug_3: fmt_stream_param:", fmt_stream_param
346 print "my_debug_3: param_list:", param_list
347 print "my_debug_3: tmp_param:", tmp_param
348 print "my_debug_3: fmt_list:",fmt_list
この処理は、
この変更を追加してログを取り直してみると fmt_
my_debug_3: quoted_param: 34%7Chttp%3A%2F%2Fv4.lscache5.c.youtube.com%2Fvideoplayback%3Fip%3D210.0.0.0%26sparams%3Did%2 52Cexpire%252Cip%252Cipbits%252Citag%252Calgorithm%252Cburst%252Cfactor%26fexp%3D900505%26algorithm%3Dthrottle-factor%2 ...
my_debug_3: fmt_stream_param: 34|http://v4.lscache5.c.youtube.com/videoplayback?ip=210.0.0.0&sparams=id%2Cexpire%2Cip%2 Cipbits%2Citag%2Calgorithm%2Cburst%2Cfactor&fexp=900505&algorithm=throttle-factor&itag=34&ipbits=8&burst=40&sver=3&expi ...
my_debug_3: param_list: ['34|http://v4.lscache5.c.youtube.com/videoplayback?ip=210.0.0.0&sparams=id%2Cexpire%2Cip%2Cipb its%2Citag%2Calgorithm%2Cburst%2Cfactor&fexp=900505&algorithm=throttle-factor&itag=34&ipbits=8&burst=40&sver=3&expire=1 ...
my_debug_3: fmt_list: ['34', 'http://v4.lscache5.c.youtube.com/videoplayback?ip=210.0.0.0&sparams=id%2Cexpire%2Cip%2Cip bits%2Citag%2Calgorithm%2Cburst%2Cfactor&fexp=900505&algorithm=throttle-factor&itag=34&ipbits=8&burst=40&sver=3&expire= ...
あとはこうして取り出した t_
350 if t_param != "":
351 # mrl = "http://www.youtube.com/get_video?video_id=" + urllib.quote (youtube_id) + "&t=" + t_param + self.get_fmt_string ()
352 mrl = t_param
YouTubeプラグインにここまでの修正を施してみると、

さて、

TotemではこれらのコーデックはバックエンドのGStreamerの機能で直接再生できるのに対し、
ただ、