summaryrefslogtreecommitdiff
path: root/totem
diff options
context:
space:
mode:
authorLuo Jinghua <sunmoon1997@gmail.com>2010-01-19 22:29:53 +0800
committerLuo Jinghua <sunmoon1997@gmail.com>2010-01-23 09:58:18 +0800
commit5dd9fd16cc29c4228a6357c6d1d0171b14a2112e (patch)
treec5b0f9950675f243ce2f0223cd85cddf4d0ca3ab /totem
Inital commit of totem-sohuHEADmaster
Diffstat (limited to 'totem')
-rw-r--r--totem/Makefile.am1
-rw-r--r--totem/plugin/Makefile.am23
-rw-r--r--totem/plugin/SohuVideoList.py143
-rw-r--r--totem/plugin/sohuvideo-config.ui238
-rw-r--r--totem/plugin/sohuvideo-ui.xml7
-rw-r--r--totem/plugin/sohuvideo.py1743
-rw-r--r--totem/plugin/sohuvideo.totem-plugin.in9
-rw-r--r--totem/plugin/sohuvideo.ui378
-rw-r--r--totem/plugin/sohuvideo_translation.py.in9
-rw-r--r--totem/plugin/sohuvideolist.py522
10 files changed, 3073 insertions, 0 deletions
diff --git a/totem/Makefile.am b/totem/Makefile.am
new file mode 100644
index 0000000..3f7beb2
--- /dev/null
+++ b/totem/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = plugin \ No newline at end of file
diff --git a/totem/plugin/Makefile.am b/totem/plugin/Makefile.am
new file mode 100644
index 0000000..2d9360b
--- /dev/null
+++ b/totem/plugin/Makefile.am
@@ -0,0 +1,23 @@
+plugindir = $(PLUGINDIR)/sohuvideo
+uidir = $(plugindir)
+
+plugin_PYTHON = \
+ sohuvideolist.py \
+ sohuvideo.py \
+ SohuVideoList.py
+
+sohuvideo_translation.py: sohuvideo_translation.py.in
+ sed -e "s,\@GETTEXT_PACKAGE\@,$(GETTEXT_PACKAGE),g" \
+ -e "s,\@LOCALEDIR\@,$(datadir)/locale,g" $< >$@
+
+plugin_in_files = sohuvideo.totem-plugin.in
+
+%.totem-plugin: %.totem-plugin.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+
+plugin_DATA = $(plugin_in_files:.totem-plugin.in=.totem-plugin) sohuvideo_translation.py
+ui_DATA = sohuvideo.ui sohuvideo-ui.xml sohuvideo-config.ui
+
+EXTRA_DIST = $(plugin_in_files) $(ui_DATA) sohuvideo.py sohuvideo_translation.py.in SohuVideoList.py
+
+CLEANFILES = $(plugin_DATA) sohuvideo_translation.py
+DISTCLEANFILES = $(plugin_DATA) sohuvideo_translation.py
diff --git a/totem/plugin/SohuVideoList.py b/totem/plugin/SohuVideoList.py
new file mode 100644
index 0000000..6c6bdfd
--- /dev/null
+++ b/totem/plugin/SohuVideoList.py
@@ -0,0 +1,143 @@
+# Copyright (C) 2009 Luo Jinghua <sunmoon1997@gmail.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import pygtk
+pygtk.require('2.0')
+
+import sys
+import gobject
+import gtk
+import totem
+
+class SohuVideoList (gtk.TreeView):
+ __gproperties__ = {
+ 'title-column' : (gobject.TYPE_INT, "title-column",
+ "title-column", -1, gobject.G_MAXINT, -1,
+ gobject.PARAM_READWRITE),
+ 'tooltip-column' : (gobject.TYPE_INT, 'tooltip-column',
+ 'tooltip-column', -1, gobject.G_MAXINT, -1,
+ gobject.PARAM_READWRITE),
+ 'mrl-column' : (gobject.TYPE_INT, 'mrl-column', 'mrl-column',
+ -1, gobject.G_MAXINT, -1, gobject.PARAM_READWRITE),
+ "totem": (gobject.TYPE_OBJECT, "Totem",
+ "Totem", gobject.PARAM_READWRITE),
+ "plugin": (gobject.TYPE_OBJECT, "plugin",
+ "plugin", gobject.PARAM_READWRITE),
+ }
+ __gsignals__ = { 'starting-video': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN,
+ (gobject.TYPE_OBJECT, gobject.TYPE_PYOBJECT )),
+ }
+
+ __gtype_name__ = 'SohuVideoList'
+
+ def __init__ (self):
+ self.plugin = None
+ self.totem = None
+ self.builder = None
+ gtk.TreeView.__init__ (self)
+
+ self.set_property ("has-tooltip", True)
+ self.connect ("row-activated", self.row_activated_cb)
+ self.connect ("query-tooltip", self.query_tooltip_cb)
+ self.connect ("button-press-event", self.button_pressed_cb)
+ self.connect ("popup-menu", self.popup_menu_cb)
+
+ selection = self.get_selection ()
+ selection.connect ("changed", self.selection_changed_cb)
+ selection.set_mode (gtk.SELECTION_MULTIPLE)
+
+ def do_set_property(self, pspec, value):
+ setattr(self, pspec.name, value)
+
+ def do_get_property(self, pspec):
+ return getattr(self, pspec.name)
+
+ def row_activated_cb (self, widget, path, column):
+ #mrl_column = widget.get_property ("mrl-column")
+ if self.get_property ("mrl-column") == -1:
+ return
+
+ play_video = self.emit ("starting-video", self, path)
+ if not play_video:
+ return
+
+ model = widget.get_model ()
+ iter = model.get_iter (path)
+
+ if type (model) is gtk.TreeModelFilter:
+ iter = model.convert_iter_to_child_iter (iter)
+ model = model.get_model ()
+
+ mrl = model.get (iter, self.get_property ("mrl-column"))[0]
+ title = model.get (iter, self.get_property ("title-column"))[0]
+ if not mrl:
+ return
+ if hasattr(self.totem, "add_to_playlist_and_play"):
+ self.totem.add_to_playlist_and_play (mrl, title, False)
+ else:
+ self.totem.action_remote (totem.REMOTE_COMMAND_REPLACE, mrl)
+
+ if not hasattr (gtk.TreeView, 'get_tooltip_context'):
+ def get_tooltip_context (self, x, y, keyboard_mode):
+ model = self.get_model ()
+ if keyboard_mode:
+ # Keyboard mode
+ ret = self.get_cursor ()
+ if not ret[0]:
+ return None
+ path = ret[0]
+ else:
+ coords = self.convert_widget_to_bin_window_coords (x, y)
+
+ # Mouse mode
+ path = self.get_path_at_pos (coords[0], coords[1])
+ if not path:
+ return None
+ path = path[0]
+ return model, path, model.get_iter (path)
+
+ def query_tooltip_cb (self, widget, x, y, keyboard_mode, tooltip):
+ if not widget.get_tooltip_context (x, y, keyboard_mode):
+ return False
+
+ model, path, iter = widget.get_tooltip_context (x, y, keyboard_mode)
+ if type (model) is gtk.TreeModelFilter:
+ iter = model.convert_iter_to_child_iter (iter)
+ model = model.get_model ()
+ mrl_column = self.get_property ("mrl-column")
+ if mrl_column == -1:
+ text = model.get (iter, self.get_property ("tooltip-column"))[0]
+ tooltip.set_text (text)
+ else:
+ text = model.get (iter, self.get_property ("tooltip-column"))[0]
+ text += '\n' + model.get (iter, self.get_property ("mrl-column"))[0]
+ tooltip.set_text (text)
+ widget.set_tooltip_row (tooltip, path)
+ return True
+
+ def button_pressed_cb (self, widget, event):
+ pass
+
+ def selection_changed_cb (self, widget):
+ self.trigger_tooltip_query ()
+
+ def popup_menu_cb (self, treeview, widget):
+ return False
+
+ def get_ui_manager (self):
+ return self.ui_manager
+
+gobject.type_register (SohuVideoList)
diff --git a/totem/plugin/sohuvideo-config.ui b/totem/plugin/sohuvideo-config.ui
new file mode 100644
index 0000000..6c417d9
--- /dev/null
+++ b/totem/plugin/sohuvideo-config.ui
@@ -0,0 +1,238 @@
+<?xml version="1.0"?>
+<interface>
+ <!-- interface-requires gtk+ 2.12 -->
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkDialog" id="sohuvideo_config_dialog">
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Configure Sohu video</property>
+ <property name="type_hint">normal</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkCheckButton" id="sohuvideo_config_show_posters">
+ <property name="label" translatable="yes">Show posters</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="sohuvideo_config_compatible">
+ <property name="label" translatable="yes">Be compatible with old totem</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">Enable a workaround for old totem such as totem 2.22 shiped with ubuntu 8.04. If the sohu video sidebar doesn't show anything in "categories" page, please enable this.</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Sohu video&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="sohuvideo_config_ok_button">
+ <property name="label">gtk-ok</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="sohuvideo_config_cancel_button">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">sohuvideo_config_ok_button</action-widget>
+ <action-widget response="1">sohuvideo_config_cancel_button</action-widget>
+ </action-widgets>
+ </object>
+</interface>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/totem/plugin/sohuvideo-ui.xml b/totem/plugin/sohuvideo-ui.xml
new file mode 100644
index 0000000..d0c6398
--- /dev/null
+++ b/totem/plugin/sohuvideo-ui.xml
@@ -0,0 +1,7 @@
+ <ui>
+ <popup name="sohuvideo-list-popup">
+ <menuitem name="add-to-playlist" action="add-to-playlist"/>
+ <menuitem name="copy-location" action="copy-location"/>
+ </popup>
+ </ui>
+
diff --git a/totem/plugin/sohuvideo.py b/totem/plugin/sohuvideo.py
new file mode 100644
index 0000000..bef9930
--- /dev/null
+++ b/totem/plugin/sohuvideo.py
@@ -0,0 +1,1743 @@
+import totem
+import gobject, gtk, gconf
+gobject.threads_init()
+import urllib
+import httplib
+import htmlentitydefs
+import threading
+import time
+import re
+import os
+import random
+import gettext
+import copy
+import pickle
+
+from xml.dom import minidom
+from traceback import print_exc
+
+from sohuvideo_translation import _
+
+import sohuvideolist
+import SohuVideoList
+
+try:
+ from hashlib import md5
+
+ def url_digest (url):
+ m = md5 ()
+ m.update (url)
+ return m.hexdigest ()
+except ImportError:
+ import md5
+
+ def url_digest (url):
+ m = md5.new ()
+ m.update (url)
+ return m.hexdigest ()
+
+def unescape_charref (ref):
+ name = ref[2:-1]
+ base = 10
+ if name[0] == 'x':
+ name = name[1:]
+ base = 16
+ return unichr (int(name, base))
+
+def replace_entities (match):
+ ent = match.group ()
+ if ent[1] == "#":
+ return unescape_charref (ent)
+
+ repl = htmlentitydefs.name2codepoint.get (ent[1:-1])
+ if repl is not None:
+ repl = unichr (repl)
+ else:
+ repl = ent
+ return repl
+
+def unescape (data):
+ return re.sub (r"&#?[A-Za-z0-9]+?;", replace_entities, data)
+
+def unescape_xml (data):
+ ustr = unicode(data, 'utf-8')
+ return unescape(ustr).encode ('utf-8')
+
+gconf_key = '/apps/totem/plugins/sohuvideo'
+
+sohuvideo_cache_dir = os.path.expanduser ('~/.local/share/totem/plugin/sohuvideo')
+
+
+sohuvideo_images_dir = os.path.join (sohuvideo_cache_dir, "images")
+if not os.path.exists (sohuvideo_images_dir):
+ os.makedirs (sohuvideo_images_dir)
+
+### special category ids
+CATEGORY_ID_FAVORITES = 0
+CATEGORY_ID_RECENT = -1
+CATEGORY_ID_SEARCH = -2
+
+### special category index
+CATEGORY_FAVORITES = 0
+CATEGORY_RECENT = 1
+CATEGORY_SEARCH = 2
+CATEGORY_NUM_SPECIAL = 3
+
+### maximium number of recents
+MAX_RECENT = 25
+
+### maximium cached time
+CATEGORY_MAX_CACHED_TIME = 2 * 24 * 60 * 60
+MOVIES_MAX_CACHED_TIME = 7 * 24 * 60 * 60
+MOVIE_MAX_CACHED_TIME = 1 * 24 * 60 * 60
+
+def xml_node_value (node):
+ return node.firstChild.nodeValue
+
+def xml_child_value (node, id):
+ child = node.getElementsByTagName(id)[0].firstChild;
+ if child:
+ return child.nodeValue
+ return None
+
+class ProcessThread (threading.Thread):
+ def __init__ (self, data, process, callback, *args, **kwargs):
+ self.data = data
+ self.process = process
+ self.callback = callback
+ self.args = args
+ self.kwargs = kwargs
+ threading.Thread.__init__ (self)
+
+ def run (self):
+ try:
+ res = self.process (self.data, *self.args, **self.kwargs)
+ except Exception, e:
+ print "Couldn't process data: ", e
+ print_exc()
+ res = None
+
+ gobject.idle_add (self.publish_results, res)
+
+ def publish_results(self, res):
+ self.callback (res, *self.args, **self.kwargs)
+ return False
+
+class DownloadThread (threading.Thread):
+ def __init__ (self, url, callback, *args, **kwargs):
+ self.url = url
+ self.callback = callback
+ self.args = args
+ self.kwargs = kwargs
+ threading.Thread.__init__ (self)
+
+ def run (self):
+ for i in range (3):
+ try:
+ res = urllib.urlopen (self.url).read ()
+ except Exception, e:
+ print "Coudn't open url: ", e
+ res = None
+ if res:
+ break
+ gobject.idle_add (self.publish_results, res)
+
+ def publish_results(self, res):
+ self.callback (res, *self.args, **self.kwargs)
+ return False
+
+class RetrieveImageTask:
+ pass
+
+class RetrieveImage (threading.Thread):
+ def __init__ (self):
+ self.queue = []
+ self._cache = {}
+ self._lock = threading.Lock ()
+ self._done = False
+ threading.Thread.__init__ (self)
+
+ def retrieve (self, url, callback, *args):
+ task = RetrieveImageTask ()
+ task.url = url
+ task.callback = callback
+ task.args = args
+ self._lock.acquire ()
+ self.queue.append (task)
+ self._lock.release ()
+
+ def cancel_tasks (self):
+ self._lock.acquire (True)
+ self.queue = []
+ self._cache = {}
+ self._lock.release ()
+
+ def cancel (self):
+ self._done = True
+ self._lock.acquire (True)
+ self.queue = []
+ self._cache = {}
+ self._lock.release ()
+
+ def is_busy (self):
+ self._lock.acquire (True)
+ busy = len (self.queue) != 0
+ self._lock.release ()
+
+ return busy
+
+ def get_remaning_tasks (self):
+ self._lock.acquire (True)
+ num = len (self.queue)
+ self._lock.release ()
+
+ return num
+
+ def image_name_from_url (self, url):
+ ext = url.rfind('.')
+ if ext >= 0:
+ ext = url[ext:]
+ else:
+ ext = ''
+ filename = url_digest (url) + ext
+ return os.path.join (sohuvideo_cache_dir, "images", filename)
+
+ def load_image_from_disk (self, url):
+ filename = self.image_name_from_url (url)
+ if not os.access (filename, os.R_OK):
+ return None
+
+ #print 'loading image', filename
+ try:
+ pixbuf = gtk.gdk.pixbuf_new_from_file (filename)
+ except gobject.GError:
+ return None
+ return pixbuf
+
+ def store_image_to_disk (self, url, origin):
+ filename = self.image_name_from_url (url)
+ df = None
+ sf = None
+ try:
+ df = file (filename, 'wb')
+ sf = file (origin, 'rb')
+ while True:
+ buf = sf.read (4096)
+ if not buf:
+ break
+ df.write (buf)
+ except Exception, e:
+ print 'Failed to copy ', origin, ' to ', filename
+ print e
+ finally:
+ if df:
+ df.close ()
+ if sf:
+ sf.close ()
+
+ def load_pixbuf (self, url):
+ pixbuf = self.load_image_from_disk (url)
+ if pixbuf:
+ return pixbuf
+
+ try:
+ filename, headers = urllib.urlretrieve (url)
+ except Exception, e:
+ print "Couldn't download ", url
+ print "Error: ", e
+ return None
+
+ try:
+ pixbuf = gtk.gdk.pixbuf_new_from_file (filename)
+ except gobject.GError:
+ return None
+
+ self.store_image_to_disk (url, filename)
+ os.unlink (filename)
+ return pixbuf
+
+ def load_from_cache (self, url):
+ pixbuf = None
+ self._lock.acquire (True)
+ if url in self._cache:
+ pixbuf = self._cache [url]
+ self._lock.release ()
+ return pixbuf
+
+ def put_in_cache (self, url, pixbuf):
+ self._lock.acquire (True)
+ self._cache[url] = pixbuf
+ self._lock.release ()
+
+ def run (self):
+ import time
+ while not self.done:
+ task = None
+ self._lock.acquire (True)
+ if len (self.queue):
+ #print 'pending tasks:', len(self.queue)
+ task = self.queue.pop (0)
+ self._lock.release ()
+
+ if not task:
+ time.sleep (0.1)
+ continue
+
+ pixbuf = self.load_from_cache (task.url)
+ if not pixbuf:
+ #print 'loading:', task.url
+ pixbuf = self.load_pixbuf (task.url)
+ if pixbuf:
+ self.put_in_cache (task.url, pixbuf)
+ time.sleep (0.10)
+
+ if not pixbuf:
+ continue
+
+ #print 'image loaded:', task.url
+ gobject.timeout_add (50, self.publish_result, pixbuf, task)
+
+ def publish_result (self, pixbuf, task):
+ task.callback (pixbuf, *task.args)
+ return False
+
+ @property
+ def done (self):
+ self._lock.acquire (True)
+ res = self._done
+ self._lock.release ()
+ return res
+
+class Sohuvideo (totem.Plugin):
+ def __init__ (self):
+ totem.Plugin.__init__ (self)
+ self.debug = False
+ self.gstreamer_plugins_present = True
+
+ self.gconf = gconf.client_get_default ()
+
+ self.max_results = 20
+ self.button_down = False
+
+ self.start_index = {}
+ self.entry = {}
+
+ self.current_treeview_name = ""
+ self.notebook_pages = []
+
+ self.vadjust = {}
+ self.liststore = {}
+ self.treeview = {}
+
+ self.subclass_index = 0
+ self.sohuvideolist = sohuvideolist.SohuVideoList()
+
+ def load_ui (self, filename, fatal, parent, user_data):
+ datadir = os.path.dirname (__file__)
+ filename = os.path.join (datadir, filename)
+ builder = gtk.Builder ()
+ builder.set_translation_domain ('totem-sohuvideo')
+ builder.add_from_file (filename)
+ builder.connect_signals (self, user_data)
+ return builder
+
+ def activate (self, totem_object):
+ bvw_name = totem_object.get_video_widget_backend_name ()
+ self.can_recode_mrl = False
+
+ """Continue loading the plugin as before"""
+ self.builder = self.load_ui ("sohuvideo.ui", True,
+ totem_object.get_main_window (), self)
+ self.config_builder = self.load_ui ("sohuvideo-config.ui", True,
+ totem_object.get_main_window (), self)
+
+ self.totem = totem_object
+
+ self.search_entry = self.builder.get_object ("sohuvideo_search_entry")
+ self.search_entry.connect ("activate", self.on_search_entry_activated)
+ self.search_button = self.builder.get_object ("sohuvideo_search_button")
+ self.search_mode_button = self.builder.get_object ('sohuvideo_search_checkbutton')
+ self.search_mode_button.set_label (_("filter:"))
+ self.search_mode_button.connect ('toggled', self.on_search_mode_entry_toggled)
+
+ #self.search_button.set_label ('')
+ self.search_button.connect ("clicked", self.on_search_button_clicked)
+ self.refresh_button = self.builder.get_object ("sohuvideo_refresh_button")
+ #self.refresh_button.set_label ('')
+ self.refresh_button.connect ("clicked", self.on_refresh_button_clicked)
+ self.progress_bar = self.builder.get_object ("sohuvideo_progress_bar")
+ self.progress_bar.hide ()
+
+ self.notebook = self.builder.get_object ("sohuvideo_notebook")
+ self.notebook.connect ("switch-page", self.on_notebook_page_changed)
+
+ self.notebook_pages = ["categories", "movies", "files"]
+ self.current_treeview_name = "categories"
+ self.load_config ()
+ self.setup_favorites ()
+ self.setup_recent ()
+ self.setup_search ()
+ self.setup_categories ()
+ self.setup_movies ()
+ self.setup_files ()
+ self.setup_page_ui ()
+ self.setup_config_dialog ()
+
+ self.vbox = self.builder.get_object ("sohuvideo_vbox")
+ self.vbox.show_all ()
+ self.page_hbox.hide()
+ self.favorites_hbox.hide()
+
+ totem_object.add_sidebar_page ("sohuvideo", _("Sohu video"), self.vbox)
+
+ self.retrieveimage = RetrieveImage ()
+ self.retrieveimage.start ()
+
+ self.search_terms = ''
+ self.search_web_terms = ''
+ self.search_web = False
+ self.classes = []
+ self.fetch_classes ()
+
+ self.start_idle_task ()
+
+ def deactivate (self, totem):
+ self.clear_ui ()
+ self.categories_count +=1
+ self.movies_count += 1
+ self.classes = []
+
+ self.retrieveimage.cancel ()
+ self.retrieveimage.join ()
+
+ self.stop_idle_task ()
+
+ self.sohuvideolist.reset()
+
+ totem.remove_sidebar_page ("sohuvideo")
+
+ def is_configurable (self):
+ return True
+
+ def create_configure_dialog (self):
+ dialog = self.config_builder.get_object ("sohuvideo_config_dialog")
+ button = self.config_builder.get_object ("sohuvideo_config_show_posters")
+ button.set_active (self.configs['show_posters'])
+ button = self.config_builder.get_object ("sohuvideo_config_compatible")
+ button.set_active (self.configs['compatible'])
+ return dialog
+
+ def start_idle_task (self):
+ if self.configs['compatible']:
+ self.source_id = gobject.idle_add (self.on_idle)
+ else:
+ self.source_id = 0
+
+ def stop_idle_task (self):
+ if self.source_id:
+ gobject.source_remove (self.source_id)
+ self.source_id = 0
+
+ def on_idle (self):
+ time.sleep (0.001)
+ return True
+
+ def load_special_classes (self):
+ ### show favorites
+ self.subclass_index += 1
+ self.sohuvideolist.updateClasses([self.favorites])
+
+ ### show recent
+ self.subclass_index += 1
+ self.sohuvideolist.updateClasses([self.recent])
+
+ ### search
+ self.subclass_index += 1
+ self.sohuvideolist.updateClasses([self.search])
+
+ ### fill loaded categories
+ self.fill_all_categories ()
+
+ def __fetch_classes (self, *args):
+ self.fetching_classes = True
+ return self.sohuvideolist.fetchClasses()
+
+ def fetch_classes (self, force = False):
+ if force:
+ self.sohuvideolist.reset()
+
+ if self.sohuvideolist.getClasses() == 0:
+ return
+
+ self.movie_class_path = ()
+ self.subclass_index = 0
+ self.categories_count += 1
+ self.movies_count += 1
+ self.fetching_classes = False
+ self.clear_ui ()
+ self.retrieveimage.cancel_tasks ()
+
+ ### load special classes first
+ self.load_special_classes ()
+
+ ### download classes from sohuvideo.tv
+ thread = ProcessThread (None,
+ self.__fetch_classes,
+ self.on_classes_fetched,
+ self.categories_count)
+ thread.start()
+
+ gobject.timeout_add (300,
+ self.update_categories_progress,
+ self.categories_count)
+
+ def on_classes_fetched (self, res, count):
+ self.fetching_classes = False
+ if not res:
+ print "Couldn't load sohuvideo playlist"
+ return
+
+ ### check if this is a canceled task
+ if self.categories_count == count:
+ self.parse_class (res)
+
+ def __parse_movies (self, res, class_path, page, count):
+ cls = self.get_movie_class (class_path)
+
+ if res:
+ try:
+ result = self.sohuvideolist.parseMovieList(cls, res, page)
+ except Exception, e:
+ print "Couldn't parse movie list:", class_path, e
+ result = None
+
+ cls.parsing_movies = False
+ return result
+
+ def on_movies_fetched (self, res, class_path, page, count, new = True):
+ if self.movies_count != count:
+ return
+
+ ### check if the server repeats the last page
+ cls = self.get_movie_class (class_path)
+
+ if res is None:
+ cls.fetching_movies = False
+ return
+
+ ### If the size of content is larger then 4k, parse it in another thread.
+ ### Blocking the main thread is bad.
+ if res and len(res) > 1024 * 2:
+ cls.parsing_movies = True
+ process = ProcessThread (res, self.__parse_movies,
+ self.add_parsed_movies,
+ class_path, page, count)
+ process.start ()
+ self.update_progress_bar ()
+ return
+
+ result = self.__parse_movies (res, class_path, page, count)
+ self.add_parsed_movies (result, class_path, page, count)
+
+ def add_parsed_movies (self, result, class_path, page, count):
+ if self.movies_count != count or page != self.movies_page:
+ return
+ cls = self.get_movie_class (class_path)
+
+ if result:
+ cls.addMovies(result, page)
+
+ cls.fetching_movies = False
+
+ if result:
+ ## for movie in result:
+ ## print movie
+ self.fill_movies (result, class_path)
+ self.set_page_max(cls.getMaxPage())
+
+ def parse_class (self, res):
+ ### parse the class list
+ classes = self.sohuvideolist.parseClasses(res)
+ for cls in classes:
+ cls.fetching_movies = False
+ self.sohuvideolist.updateClasses(classes)
+
+ ### fill category list
+ self.clear_pages ('categories')
+ for cls in self.sohuvideolist.getClasses():
+ self.fill_categories (cls.id)
+
+ def get_movie_class (self, class_path):
+ def find_class (classes, clsid):
+ for cls in classes:
+ if cls.id == clsid:
+ return cls
+ return None
+ if not class_path:
+ return None
+ classes = self.sohuvideolist.getClasses()
+ return find_class (classes, class_path[0])
+
+ def download_movies (self, callback, class_path, page, count):
+ cls = self.get_movie_class (class_path)
+ thread = ProcessThread (None, self.__fetch_movies,
+ callback, class_path,
+ page, count)
+ thread.start()
+
+ def __fetch_movies(self, dummy, class_path, page, *args):
+ cls = self.get_movie_class (class_path)
+ if cls.id != CATEGORY_ID_SEARCH:
+ return self.sohuvideolist.fetchMovieList(cls, page)
+ return self.sohuvideolist.searchMovieList(cls.keyword, page)
+
+ def fetch_movies (self, class_path):
+ cls = self.get_movie_class (class_path)
+ cls.fetching_movies = True
+ #print cls, cls.page
+ self.download_movies (self.on_movies_fetched, class_path,
+ self.movies_page, self.movies_count)
+
+ def on_category_row_activated(self, treeview, path, view_column,
+ data = None):
+ model, rows = treeview.get_selection ().get_selected_rows ()
+ iter = model.get_iter (rows[0])
+ title = model.get_value (iter, 3)
+ classid = model.get_value (iter, 0)
+
+ class_path = (classid,)
+ #print title, class_path
+
+ self.movies_page = 0
+ self.show_movies (class_path)
+ self.notebook.set_current_page (1)
+
+ def setup_categories (self):
+ treeview_name = 'categories'
+ treeview = self.builder.get_object ("sohuvideo_treeview_" + treeview_name)
+ renderer = gtk.CellRendererText()
+ renderer.set_property("xalign", 0.0)
+
+ column = gtk.TreeViewColumn(_("Name"), renderer, text = 3)
+ column.set_clickable(True)
+ treeview.append_column(column)
+
+ column = gtk.TreeViewColumn(_("Number"), renderer, text = 4)
+ treeview.append_column(column)
+
+ self.vadjust[treeview_name] = treeview.get_vadjustment ()
+ self.vadjust[treeview_name].connect ("value-changed", self.on_value_changed)
+ vscroll = self.builder.get_object ("sohuvideo_scrolled_window_" + treeview_name).get_vscrollbar ()
+ vscroll.connect ("button-press-event", self.on_button_press_event)
+ vscroll.connect ("button-release-event", self.on_button_release_event)
+
+ self.liststore[treeview_name] = self.builder.get_object ("sohuvideo_liststore_" + treeview_name)
+ self.treeview[treeview_name] = treeview
+ treeview.set_model (self.liststore[treeview_name])
+
+ treeview.connect("row_activated", self.on_category_row_activated)
+
+ self.categories_count = 0
+
+ def on_add_to_favorites (self, button):
+ selection = self.treeview['movies'].get_selection ()
+ model, rows = selection.get_selected_rows ()
+ if not rows:
+ return
+
+ iter = model.get_iter (rows[0])
+ class_path = eval(model.get_value (iter, 4))
+
+ for row in rows:
+ iter = model.get_iter (row)
+ index = rows
+ if type (model) is gtk.TreeModelFilter:
+ iter = model.convert_iter_to_child_iter (iter)
+ filtermodel = model.get_model ()
+ else:
+ filtermodel = model
+ class_path = eval(filtermodel.get_value (iter, 4))
+ self.add_favorites (class_path,
+ filtermodel.get_path (iter)[0], False)
+ self.save_favorites ()
+ self.refresh_favorites ()
+
+ def on_remove_from_favorites (self, button):
+ selection = self.treeview['movies'].get_selection ()
+ model, rows = selection.get_selected_rows ()
+ if not rows:
+ return
+ rows.reverse ()
+ for row in rows:
+ iter = model.get_iter (row)
+ index = rows
+ if type (model) is gtk.TreeModelFilter:
+ iter = model.convert_iter_to_child_iter (iter)
+ filtermodel = model.get_model ()
+ else:
+ filtermodel = model
+ class_path = eval(filtermodel.get_value (iter, 4))
+ self.remove_favorites (class_path,
+ filtermodel.get_path (iter)[0], False)
+ self.save_favorites ()
+ self.refresh_favorites ()
+
+ def setup_favorites (self):
+ button = self.builder.get_object ('sohuvideo_add_to_favorites_button')
+ self.add_to_favorites_button = button
+ button.set_sensitive (False)
+ button.connect ('clicked', self.on_add_to_favorites)
+ button = self.builder.get_object ('sohuvideo_remove_from_favorites_button')
+ button.set_sensitive (False)
+ button.connect ('clicked', self.on_remove_from_favorites)
+ self.remove_from_favorites_button = button
+ self.favorites_hbox = self.builder.get_object ('sohuvideo_favorites_hbox')
+ self.load_favorites ()
+
+ def load_favorites (self):
+ self.favorites = sohuvideolist.SohuClass ()
+ self.favorites.id = CATEGORY_ID_FAVORITES
+ self.favorites.title = _('Favorites')
+ self.favorites.fetching_movies = False
+
+ filename = os.path.join (sohuvideo_cache_dir, 'favorites.pickle')
+ if not os.path.exists (filename):
+ return
+ try:
+ dump = pickle.load (file(filename, 'rb'))
+ except Exception, e:
+ print "Couldn't load favorite list", e
+ dump = []
+
+ movies = []
+ for d in dump:
+ movie = sohuvideolist.SohuMovie()
+ movie.load(d)
+ movies.append(movie)
+ self.favorites.setMovies(movies)
+
+ def save_favorites (self):
+ treeview_name = 'categories'
+ filename = os.path.join (sohuvideo_cache_dir, 'favorites.pickle')
+ fp = None
+ try:
+ fp = file (filename, 'wb')
+ except:
+ return
+ d = [ movie.dictionary() for movie in self.favorites.getPage(0)]
+ try:
+ pickle.dump(d, fp)
+ except:
+ pass
+
+ def add_favorites (self, class_path, index, update = True):
+ cls = self.get_movie_class (class_path)
+ movies = cls.getPage(0)
+ movie = movies[index]
+ if movie in self.favorites.getPage(0):
+ return
+ movie = copy.copy(movie)
+ movie.model_path = ()
+ self.favorites.addMovies([movie])
+
+ if not update:
+ return
+ self.refresh_favorites ()
+ self.save_favorites ()
+
+ def remove_favorites (self, class_path, index, update = False):
+ cls = self.get_movie_class (class_path)
+ movies = cls.getPage(self.movies_page)
+ movie = movies[index]
+ #print self.favorites.movies, movie
+ if not movie in self.favorites.getPage(0):
+ print "Couldn't find ", movie, ' in favorites'
+ return
+ movies.remove(movie)
+
+ if not update:
+ return
+ self.refresh_favorites ()
+ self.save_favorites ()
+
+ def refresh_favorites (self):
+ treeview_name = 'categories'
+ treeview = self.builder.get_object ("sohuvideo_liststore_" + treeview_name)
+ if not len (treeview):
+ return
+
+ self.update_category (self.favorites)
+
+ if self.movie_class_path == ():
+ return
+
+ class_path = self.movie_class_path
+ if class_path != (CATEGORY_ID_FAVORITES,):
+ return
+
+ self.movie_class_path = ()
+ self.show_movies (class_path, False, True)
+
+ def setup_recent (self):
+ self.load_recent ()
+
+ def load_recent (self):
+ treeview_name = 'categories'
+
+ self.recent = sohuvideolist.SohuClass ()
+ self.recent.id = CATEGORY_ID_RECENT;
+ self.recent.title = _('Recent')
+ self.recent.fetching_movies = False
+
+ filename = os.path.join (sohuvideo_cache_dir, 'recent.pickle')
+ if not os.path.exists (filename):
+ return
+ try:
+ dump = pickle.load (file(filename, 'rb'))
+ except Exception, e:
+ print "Couldn't load recent list", e
+ dump = []
+
+ movies = []
+ for d in dump:
+ movie = sohuvideolist.SohuMovie()
+ movie.load(d)
+ movies.append(movie)
+ self.recent.setMovies(movies)
+
+ def save_recent (self):
+ treeview_name = 'categories'
+ filename = os.path.join (sohuvideo_cache_dir, 'recent.pickle')
+ fp = None
+ try:
+ fp = file (filename, 'wb')
+ except:
+ return
+ d = [ movie.dictionary() for movie in self.recent.getPage(0)]
+ try:
+ pickle.dump(d, fp)
+ except:
+ pass
+
+ def add_recent (self, class_path, index):
+ cls = self.get_movie_class (class_path)
+ movie = cls.getPage(self.movies_page)[index]
+
+ liststore_name = 'movies'
+ liststore = self.liststore[liststore_name]
+ old_index = -1
+ movies = self.recent.getPage(0)
+ if movie in movies:
+ ### it's alreay the last one
+ if movie == movies[0]:
+ return
+ old_index = movies.index (movie)
+ movies.remove (movie)
+ else:
+ while len (movies) > MAX_RECENT - 1:
+ if self.movie_class_path == (CATEGORY_ID_RECENT,):
+ del self.liststore[-1]
+ del movies[-1]
+ self.recent.resetMovies()
+ movie = copy.copy(movie)
+ self.recent.setMovies([movie] + movies)
+
+ if self.movie_class_path == (CATEGORY_ID_RECENT,):
+ if old_index < 0:
+ self.storelist_append_movie (liststore, movie,
+ self.movie_class_path, 0)
+ else:
+ iter = liststore.get_iter (old_index)
+ liststore.move_after (iter, None)
+ self.refresh_recent_num ()
+ self.save_recent ()
+
+ def refresh_recent_num (self):
+ liststore_name = 'categories'
+ liststore = self.liststore[liststore_name]
+ if not len (liststore):
+ return
+
+ self.update_category (self.recent)
+
+ def refresh_recent (self):
+ self.refresh_recent_num ()
+ if self.movie_class_path == ():
+ return
+
+ class_path = self.movie_class_path
+ if class_path != (CATEGORY_ID_RECENT,):
+ return
+
+ self.movie_class_path = ()
+ self.show_movies (class_path, False, True)
+
+ def setup_search (self):
+ self.search = sohuvideolist.SohuClass()
+ self.search.id = CATEGORY_ID_SEARCH
+ self.search.title = _('Search result')
+ self.search.keyword = ''
+ self.search.fetching_movies = False
+
+ def fill_all_categories (self):
+ treeview_name = 'categories'
+ treeview = self.liststore[treeview_name]
+ self.clear_pages (treeview_name)
+ for cls in self.sohuvideolist.getClasses():
+ iter = treeview.append(None)
+ s = '%d/%d' % (len(cls.getMovies()), cls.getMaxPage())
+ treeview.set(iter,
+ 0, cls.id,
+ 1, 0,
+ 2, 0,
+ 3, cls.title,
+ 4, s)
+ cls.model_path = treeview.get_path(iter)
+
+ def fill_categories (self, clsid):
+ treeview_name = 'categories'
+ treeview = self.liststore[treeview_name]
+
+ cls = None
+ for cls in self.sohuvideolist.getClasses():
+ if cls.id == clsid:
+ break
+ if not cls:
+ return
+
+ s = '%d/%d' % (len(cls.getMovies()), cls.getMaxPage())
+ iter = treeview.append(None)
+ treeview.set(iter,
+ 0, cls.id,
+ 1, 0,
+ 2, 0,
+ 3, cls.title,
+ 4, s)
+ cls.model_path = treeview.get_path(iter)
+ #print cls.id, cls.title
+
+ def update_category (self, cls):
+ treeview_name = 'categories'
+ liststore = self.liststore[treeview_name]
+ iter = liststore.get_iter (cls.model_path)
+ s = '%d/%d' % (len(cls.getMovies()), cls.getMaxPage())
+ liststore.set (iter, 4, s)
+
+ def update_current_category (self):
+ if not self.movie_class_path:
+ return
+ cls = self.get_movie_class(self.movie_class_path)
+ self.update_category (cls)
+
+ def get_movie_tooltip (self, movie):
+ tip = movie.title
+ if movie.director:
+ tip += '\n' + _("Director: ") + movie.director
+ if movie.actor:
+ tip += '\n' + _("Actor: ") + movie.actor
+ if movie.area:
+ tip += '\n' + _("Area: ") + movie.area
+ if movie.size:
+ tip += '\n' + _("Size: ") + str(movie.size) + _('MB')
+ if movie.pubtime:
+ tip += '\n' + _("Pubtime: ") + movie.pubtime
+ if movie.length:
+ tip += '\n' + _("Length: ") + str(movie.length) + _('Min')
+ tip += '\n' + _("Score: ") + str(movie.score)
+ tip += '\n' + _("Episodes: ") + str(movie.cn)
+ tip += '\n' + _("Description: ") + '\n' + movie.desc
+ if hasattr(file, 'longdesc'):
+ tip += '\n' + _("Long Description: ") + movie.longdesc
+
+ return unescape_xml (tip)
+
+ def on_image_retrieved (self, pixbuf, movie, iter, class_path, count):
+ treeview_name = 'movies'
+ liststore = self.liststore[treeview_name]
+ movie.pixbuf = pixbuf
+
+ try:
+ ### check whether the treeview has been cleared.
+ if self.movie_class_path == class_path and self.movies_count == count:
+ if self.configs['show_posters']:
+ liststore.set (iter, 0, pixbuf)
+ else:
+ print 'not showing posters'
+ except Exception, e:
+ print 'Failed to loading posters:', class_path, e
+ pass
+
+ def recode_mrl (self, url):
+ if not self.can_recode_mrl:
+ return url
+ path, qs = urllib.splitquery (url)
+ if '%' in path:
+ path = urllib.unquote (path)
+ try:
+ path = unicode (path, 'gbk').encode('utf-8')
+ except Exception, e:
+ print 'Failed to recode url:', e
+ return url
+ if qs is None:
+ qs = ''
+ return path + qs
+
+ def storelist_append_movie (self, treeview, movie, class_path, pos = -1):
+ files = movie.getFiles()
+ #if not movie.getFiles():
+ # return
+ f = None
+ if len(files):
+ f = files[0]
+ origurl = f.url
+ else:
+ origurl = ''
+ tip = self.get_movie_tooltip (movie)
+ pixbuf = movie.pixbuf
+ if not self.configs['show_posters']:
+ pixbuf = None
+ url = self.recode_mrl (origurl)
+ if pos == -1:
+ it = treeview.append ([pixbuf, movie.title, url, tip,
+ repr(class_path), origurl])
+ else:
+ it = treeview.insert (0)
+ treeview.set (it,
+ 0, pixbuf,
+ 1, movie.title,
+ 2, url,
+ 3, tip,
+ 4, repr(class_path),
+ 5, f.url)
+ if not movie.pixbuf and movie.smallimage and self.configs['show_posters']:
+ self.retrieveimage.retrieve (movie.smallimage, self.on_image_retrieved,
+ movie, it, class_path,
+ self.movies_count)
+ movie.model_path = treeview.get_path(it)
+
+ def fill_files (self, movie):
+ treeview_name = 'files'
+ self.clear_pages (treeview_name)
+ treeview = self.liststore[treeview_name]
+ for f in movie.getFiles():
+ no = str(f.ci + 1) + '/' + str(movie.cn)
+ it = treeview.append ([no, f.title, self.recode_mrl (f.url), f.url])
+
+ def show_files (self, movie):
+ self.current_movie = movie
+ if movie.getFiles():
+ self.fill_files(movie)
+ else:
+ self.clear_pages('files')
+ self.fetch_files(movie)
+
+ def download_files (self, callback, movie, show, count):
+ thread = ProcessThread (None, self.__fetch_files,
+ callback, movie, show, count)
+ thread.start()
+
+ def __fetch_files(self, dummy, movie, *args):
+ cls = movie.parent
+ assert (cls is not None)
+ return self.sohuvideolist.fetchMovie(cls, movie)
+
+ def fetch_files (self, movie, show = True):
+ self.download_files (self.on_files_fetched, movie, show,
+ self.movies_count)
+
+ def on_files_fetched(self, res, movie, show, movies_count):
+ if res:
+ files = self.sohuvideolist.parseMovie(movie.parent, movie, res)
+ movie.setFiles(files)
+ if show:
+ if self.movies_count == movies_count and \
+ movie == self.current_movie:
+ self.fill_files(movie)
+ self.movies_update_files_status()
+
+ def fill_all_movies (self, class_path):
+ cls = self.get_movie_class (class_path)
+
+ self.set_page_max(cls.getMaxPage())
+
+ treeview_name = 'movies'
+ liststore = self.liststore[treeview_name]
+ for movie in cls.getPage(self.movies_page):
+ self.storelist_append_movie (liststore, movie, class_path)
+
+ self.update_category (cls)
+
+ def fill_movies (self, movies, class_path):
+ if self.movie_class_path != class_path:
+ return
+ treeview_name = 'movies'
+ liststore = self.liststore[treeview_name]
+ for movie in movies:
+ self.storelist_append_movie (liststore, movie, class_path)
+
+ self.update_current_category ()
+
+ def refetch_movies (self, class_path = None):
+ if class_path is None:
+ class_path = self.movie_class_path
+
+ self.retrieveimage.cancel_tasks ()
+ self.movie_class_path = ()
+ self.show_movies (class_path, True)
+
+ def show_movies (self, class_path, force = False, cancel = True):
+ #print class_path, force, cancel
+ ### force -- refetch movie list from server
+ ### cancel -- discard the current movie list
+ if self.movie_class_path == class_path and not force:
+ return
+
+ ### cancel current fetching thread
+ if cancel:
+ self.movies_count += 1
+ if self.movie_class_path != ():
+ cls = self.get_movie_class (self.movie_class_path)
+ cls.fetching_movies = False
+
+ self.movie_class_path = class_path
+
+ if not self.configs['show_posters']:
+ self.retrieveimage.cancel_tasks ()
+
+ self.clear_pages (['movies', 'files'])
+
+ cls = self.get_movie_class (class_path)
+
+ ### reset movie list(except the favorites/recent list)
+ if force:
+ cls.setMovies([], self.movies_page)
+
+ if cancel or (not cls.fetching_movies and \
+ not self.retrieveimage.is_busy ()):
+ self.add_progress_callback (class_path,
+ self.movies_count)
+
+ ### fill all loaded movies
+ self.set_current_page(self.movies_page + 1)
+ self.fill_all_movies (class_path)
+ if not len(cls.getPage(self.movies_page)) and \
+ not cls.fetching_movies:
+ self.fetch_movies (class_path)
+
+ def on_movies_visible_func (self, model, iter):
+ terms = self.search_terms
+ if not terms:
+ return True
+ treeview = self.treeview['movies']
+ title = model.get (iter, treeview.get_property ("title-column"))[0] or ''
+ tooltip = model.get (iter, treeview.get_property ("tooltip-column"))[0] or ''
+ if terms in title or terms in tooltip:
+ return True
+ return False
+
+ def setup_movies (self):
+ treeview_name = 'movies'
+ self.movie_class_path = ()
+ self.movies_count = 0
+ self.movies_page = 0
+
+ """This is done here rather than in the UI file,
+ because UI files parsed in C and GObjects created in
+ Python apparently don't mix."""
+ renderer = totem.CellRendererVideo (use_placeholder = self.configs['show_posters'])
+ treeview = SohuVideoList.SohuVideoList()
+ treeview.set_property('headers-visible', False)
+ treeview.set_property('fixed-height-mode', False)
+ treeview.set_property('title-column', 1)
+ treeview.set_property('tooltip-column', 3)
+ treeview.set_property('mrl-column', 2)
+ treeview.set_property ("totem", self.totem)
+
+ window = self.builder.get_object('sohuvideo_scrolled_window_movies')
+ window.add(treeview)
+
+ treeview.connect ("row-activated", self.on_movies_row_activated)
+ treeview.connect_after ("starting-video", self.on_movies_starting_video)
+ treeview.insert_column_with_attributes (0, _("Videos"),
+ renderer, thumbnail=0, title=1)
+ self.cell_video_renderer = renderer
+
+ self.vadjust[treeview_name] = treeview.get_vadjustment ()
+ self.vadjust[treeview_name].connect ("value-changed", self.on_value_changed)
+ vscroll = self.builder.get_object ("sohuvideo_scrolled_window_" + treeview_name).get_vscrollbar ()
+ vscroll.connect ("button-press-event", self.on_button_press_event)
+ vscroll.connect ("button-release-event", self.on_button_release_event)
+
+ self.liststore[treeview_name] = self.builder.get_object ("sohuvideo_liststore_" + treeview_name)
+ self.treeview[treeview_name] = treeview
+ treefilter = self.liststore[treeview_name].filter_new ()
+ treefilter.set_visible_func (self.on_movies_visible_func)
+ treeview.set_model (treefilter)
+
+ selection = treeview.get_selection ()
+ selection.connect ("changed", self.on_movies_selection_changed)
+
+ def on_add_to_playlist (self, treeview):
+ ### add selected movie to recent list
+ treeview_name = 'movies'
+ treeview = self.treeview[treeview_name]
+ selection = treeview.get_selection ()
+ model = treeview.get_model ()
+ model, rows = selection.get_selected_rows ()
+ self.add_movie_to_recent (treeview, rows[0])
+
+ treeview_name = 'files'
+ treeview = self.treeview[treeview_name]
+ selection = treeview.get_selection ()
+ model = treeview.get_model ()
+ model, rows = selection.get_selected_rows ()
+
+ for row in rows:
+ iter = model.get_iter (row)
+ mrl = model.get_value (iter, 2)
+ self.totem.action_remote (totem.REMOTE_COMMAND_ENQUEUE, mrl)
+
+ def on_copy_location (self, action):
+ treeview_name = 'files'
+ treeview = self.treeview[treeview_name]
+ selection = treeview.get_selection ()
+ model = treeview.get_model ()
+ model, rows = selection.get_selected_rows ()
+ iter = model.get_iter (rows[0])
+ mrl = model.get_value (iter, 3)
+
+ clip = gtk.Clipboard ()
+ clip.set_text (mrl)
+ clip = gtk.Clipboard (display = gtk.gdk.display_get_default(),
+ selection = "PRIMARY")
+ clip.set_text (mrl)
+
+ def setup_files_action (self):
+ datadir = os.path.dirname (__file__)
+ filename = os.path.join (datadir, 'sohuvideo-ui.xml')
+
+ self.files_uimanager = self.builder.get_object ('sohuvideo-list-ui-manager')
+ self.files_uimanager.add_ui_from_file(filename)
+
+ action_group = gtk.ActionGroup(name = 'sohuvideo-list-action-group')
+ self.files_uimanager.insert_action_group(action_group, 0)
+
+ action = gtk.Action(name = 'add-to-playlist',
+ label = _('_Add to Playlist'),
+ tooltip = _('Add the video to the playlist'),
+ stock_id = 'gtk-add')
+ action.connect ('activate', self.on_add_to_playlist)
+ action_group.add_action(action)
+
+ action = gtk.Action(name = 'copy-location',
+ label = _('_Copy Location'),
+ tooltip = _('Copy the location to the clipboard'),
+ stock_id = 'gtk-copy')
+ action.connect ('activate', self.on_copy_location)
+ action_group.add_action(action)
+
+ def files_show_popup_menu (self, event = None):
+ treeview_name = 'files'
+ treeview = self.treeview [treeview_name]
+ selection = treeview.get_selection ()
+
+ if event:
+ button = event.button
+ time = event.time
+ path = treeview.get_path_at_pos (int(event.x), int(event.y))
+ if not path or not selection.path_is_selected (path[0]):
+ selection.unselect_all ()
+ else:
+ time = gtk.get_current_event_time ()
+ button = None
+
+ count = selection.count_selected_rows ()
+ if not count:
+ return False
+
+ action_group = self.files_uimanager.get_action_groups () [0]
+ action = action_group.get_action ("copy-location")
+ action.set_sensitive (count == 1)
+
+ menu = self.files_uimanager.get_widget ("/sohuvideo-list-popup")
+ menu.select_first (False)
+ menu.popup (None, None, None, button, time)
+
+ return True
+
+ def on_files_popup_menu (self):
+ self.files_show_popup_menu ()
+
+ def on_files_button_pressed (self, widget, event, user_data = None):
+ if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3:
+ return self.files_show_popup_menu (event)
+
+ return False
+
+ def setup_files (self):
+ treeview_name = 'files'
+ treeview = self.builder.get_object ("sohuvideo_treeview_" + treeview_name)
+ renderer = gtk.CellRendererText()
+ renderer.set_property("xalign", 0.0)
+
+ column = gtk.TreeViewColumn(_("No"), renderer, text = 0)
+ column.set_clickable(True)
+ treeview.append_column(column)
+
+ column = gtk.TreeViewColumn(_("Title"), renderer, text = 1)
+ treeview.append_column(column)
+
+ column = gtk.TreeViewColumn(_("Mrl"), renderer, text = 2)
+ treeview.append_column(column)
+
+ self.vadjust[treeview_name] = treeview.get_vadjustment ()
+ self.vadjust[treeview_name].connect ("value-changed", self.on_value_changed)
+ vscroll = self.builder.get_object ("sohuvideo_scrolled_window_" + treeview_name).get_vscrollbar ()
+ vscroll.connect ("button-press-event", self.on_button_press_event)
+ vscroll.connect ("button-release-event", self.on_button_release_event)
+
+ self.liststore[treeview_name] = self.builder.get_object ("sohuvideo_liststore_" + treeview_name)
+ self.treeview[treeview_name] = treeview
+ treeview.set_model (self.liststore[treeview_name])
+
+ treeview.connect("row_activated", self.on_files_treeview_row_activated)
+
+ self.setup_files_action ()
+ treeview.connect ("popup-menu", self.on_files_popup_menu)
+ treeview.connect ("button-press-event", self.on_files_button_pressed)
+
+ selection = treeview.get_selection ()
+ selection.set_mode (gtk.SELECTION_MULTIPLE)
+
+ self.files_count = 0
+
+ def on_files_treeview_row_activated(self, treeview, path, view_column, data=None):
+ model, rows = treeview.get_selection ().get_selected_rows ()
+ try:
+ iter = model.get_iter (rows[0])
+ except IndexError:
+ print 'activated invalid index:', rows[0]
+ return
+
+ ### play the movie
+ title = model.get_value (iter, 1)
+ mrl = model.get_value (iter, 2)
+ #print title, mrl
+ if hasattr(self.totem, "add_to_playlist_and_play"):
+ self.totem.add_to_playlist_and_play (mrl, title, False)
+ else:
+ self.totem.action_remote (totem.REMOTE_COMMAND_REPLACE, mrl)
+
+ ### add selected movie to recent list
+ movies_treeview = self.treeview['movies']
+ movies_model, movies_rows = movies_treeview.get_selection ().get_selected_rows ()
+ self.add_movie_to_recent (movies_treeview, movies_rows[0])
+
+ def on_movies_selection_changed (self, selection):
+ model, rows = selection.get_selected_rows ()
+ if not rows:
+ self.clear_pages ('files')
+ return
+
+ all_files_fetched = True
+ for row in rows:
+ iter = model.get_iter(row)
+ if type (model) is gtk.TreeModelFilter:
+ iter = model.convert_iter_to_child_iter (iter)
+ model = model.get_model ()
+
+ class_path = eval(model.get_value (iter, 4))
+ cls = self.get_movie_class (class_path)
+
+ index = model.get_path(iter)[0]
+ movie = cls.getPage(self.movies_page)[index]
+ if not movie.getFiles():
+ all_files_fetched = False
+ if row != rows[0]:
+ self.fetch_files(movie, False)
+ if row == rows[0]:
+ self.show_files(movie)
+ if self.movie_class_path == (CATEGORY_ID_FAVORITES,):
+ self.add_to_favorites_button.set_sensitive(False)
+ else:
+ self.add_to_favorites_button.set_sensitive(all_files_fetched)
+
+ def movies_update_files_status(self):
+ selection = self.treeview['movies'].get_selection ()
+ model, rows = selection.get_selected_rows ()
+ if not rows:
+ self.clear_pages ('files')
+ return
+
+ all_files_fetched = True
+ for row in rows:
+ iter = model.get_iter(row)
+ if type (model) is gtk.TreeModelFilter:
+ iter = model.convert_iter_to_child_iter (iter)
+ model = model.get_model ()
+
+ class_path = eval(model.get_value (iter, 4))
+ cls = self.get_movie_class (class_path)
+
+ index = model.get_path(iter)[0]
+ movie = cls.getPage(self.movies_page)[index]
+ if not movie.getFiles():
+ all_files_fetched = False
+ else:
+ url = model.get(iter, 2)[0]
+ if not url:
+ url = movie.getFiles()[0].url
+ model.set(iter, 2, self.recode_mrl(url))
+ model.set(iter, 5, url)
+
+ if self.movie_class_path == (CATEGORY_FAVORITES,):
+ self.add_to_favorites_button.set_sensitive(False)
+ else:
+ self.add_to_favorites_button.set_sensitive(all_files_fetched)
+
+ def on_first_page_clicked(self, *args):
+ if self.movie_class_path == ():
+ return
+
+ cls = self.get_movie_class(self.movie_class_path)
+
+ if self.movies_page == 0:
+ return
+ self.movies_page = 0
+
+ self.show_movies(self.movie_class_path, force = True)
+
+ def on_prev_page_clicked(self, *args):
+ if self.movie_class_path == ():
+ return
+ if self.movies_page < 1:
+ return
+ self.movies_page -= 1
+
+ self.show_movies(self.movie_class_path, force = True)
+
+ def on_next_page_clicked(self, *args):
+ if self.movie_class_path == ():
+ return
+ cls = self.get_movie_class(self.movie_class_path)
+
+ if self.movies_page >= cls.getMaxPage() - 1:
+ return
+ self.movies_page += 1
+
+ self.show_movies(self.movie_class_path, force = True)
+
+ def on_last_page_clicked(self, *args):
+ if self.movie_class_path == ():
+ return
+
+ cls = self.get_movie_class(self.movie_class_path)
+
+ if self.movies_page == cls.getMaxPage() - 1:
+ return
+ self.movies_page = cls.getMaxPage() - 1
+
+ self.show_movies(self.movie_class_path, force = True)
+
+ def on_goto_page_clicked(self, *args):
+ if self.movie_class_path == ():
+ return
+
+ cls = self.get_movie_class(self.movie_class_path)
+
+ page = self.which_page_button.get_value_as_int() - 1
+ if page >= cls.getMaxPage():
+ return
+ if page == self.movies_page:
+ return
+ self.movies_page = page
+ self.show_movies(self.movie_class_path, force = True)
+
+ def set_page_max(self, max_page):
+ adjustment = self.which_page_button.get_adjustment()
+ adjustment.upper = max_page
+ self.max_page_label.set_text (_('Total: %d') % max_page)
+
+ def set_current_page(self, current):
+ adjustment = self.which_page_button.get_adjustment()
+ adjustment.value = current
+
+ def setup_page_ui (self):
+ self.page_hbox = self.builder.get_object('sohuvideo_page_hbox')
+ self.first_page_button = self.builder.get_object('sohuvideo_first_page_button')
+ self.prev_page_button = self.builder.get_object('sohuvideo_prev_page_button')
+ self.next_page_button = self.builder.get_object('sohuvideo_next_page_button')
+ self.last_page_button = self.builder.get_object('sohuvideo_last_page_button')
+ self.goto_page_button = self.builder.get_object('sohuvideo_goto_page_button')
+ self.which_page_button = self.builder.get_object('sohuvideo_which_page_button')
+ self.max_page_label = self.builder.get_object('sohuvideo_max_page_label')
+
+ self.page_hbox.set_sensitive(False)
+ self.set_page_max (1)
+
+ adjustment = self.which_page_button.get_adjustment()
+ adjustment.lower = 1
+ adjustment.upper = 1
+ adjustment.value = 1
+
+ self.first_page_button.connect('clicked', self.on_first_page_clicked)
+ self.prev_page_button.connect('clicked', self.on_prev_page_clicked)
+ self.next_page_button.connect('clicked', self.on_next_page_clicked)
+ self.last_page_button.connect('clicked', self.on_last_page_clicked)
+ self.goto_page_button.connect('clicked', self.on_goto_page_clicked)
+
+ def on_notebook_page_changed (self, notebook, notebook_page, page_num):
+ self.current_treeview_name = self.notebook_pages[page_num]
+ self.add_to_favorites_button.set_sensitive (False)
+ self.remove_from_favorites_button.set_sensitive (False)
+ if self.current_treeview_name == 'files':
+ self.refresh_button.set_sensitive (False)
+ self.page_hbox.set_sensitive (False)
+ self.page_hbox.show()
+ self.favorites_hbox.hide()
+ self.update_progress_bar ()
+ elif self.current_treeview_name == 'movies':
+ if self.movie_class_path == ():
+ self.refresh_button.set_sensitive (False)
+ elif self.movie_class_path == (CATEGORY_ID_FAVORITES,):
+ self.remove_from_favorites_button.set_sensitive (True)
+ else:
+ self.refresh_button.set_sensitive (True)
+ self.page_hbox.set_sensitive (True)
+ self.page_hbox.show ()
+ self.favorites_hbox.show ()
+ self.movies_update_files_status()
+ self.update_progress_bar ()
+ else:
+ self.page_hbox.set_sensitive (False)
+ self.page_hbox.hide ()
+ self.favorites_hbox.hide ()
+ self.refresh_button.set_sensitive (True)
+ self.update_categories_progress (self.categories_count)
+
+ def update_categories_progress (self, count):
+ if count != self.categories_count:
+ return False
+ if self.current_treeview_name != 'categories':
+ return True
+ classes = self.sohuvideolist.getClasses()
+ if self.fetching_classes:
+ self.progress_bar.pulse ()
+ self.progress_bar.set_text (_("Refreshing category list..."))
+ self.progress_bar.show ()
+ return True
+ else:
+ self.progress_bar.set_fraction (0.0)
+ self.progress_bar.set_text ("")
+ self.progress_bar.hide ()
+ return False
+ return True
+
+ def update_progress_bar (self):
+ if self.current_treeview_name == 'categories':
+ return True
+ if self.movie_class_path == ():
+ self.progress_bar.set_fraction (0.0)
+ self.progress_bar.set_text ("")
+ self.progress_bar.hide ()
+ return True
+
+ cls = self.get_movie_class (self.movie_class_path)
+ if cls.fetching_movies:
+ self.progress_bar.pulse()
+ progressstr = '(%d/%d)' % (self.movies_page + 1, cls.max_page)
+ if hasattr(cls, 'parsing_movies') and cls.parsing_movies:
+ self.progress_bar.set_text (_("Parsing movie list%s...") % progressstr)
+ else:
+ self.progress_bar.set_text (_("Loading movie list%s...") % progressstr)
+ self.progress_bar.show ()
+ return True
+ elif self.retrieveimage.is_busy ():
+ remaining = self.retrieveimage.get_remaning_tasks ()
+ num_movies = len(cls.getMovies())
+ if remaining > num_movies:
+ remaining = num_movies
+ finished = num_movies - remaining
+ progress = float(finished) / num_movies
+ self.progress_bar.set_fraction (progress)
+ progressstr = str(finished) + '/' + str(num_movies)
+ self.progress_bar.set_text (_('loading posters(%s)...') % progressstr)
+ self.progress_bar.show ()
+ else:
+ self.progress_bar.set_fraction (0.0)
+ self.progress_bar.set_text ("")
+ self.progress_bar.hide ()
+
+ return False
+
+ return True
+
+ def on_update_progress_bar (self, class_path, count):
+ if self.movie_class_path == class_path and self.movies_count == count:
+ return self.update_progress_bar ()
+ return False
+
+ def add_progress_callback (self, class_path, count):
+ gobject.timeout_add (350, self.on_update_progress_bar,
+ class_path, count)
+
+ def on_movies_row_activated (self, treeview, path, column):
+ pass
+
+ def add_movie_to_recent (self, treeview, path):
+ model = treeview.get_model ()
+ iter = model.get_iter (path)
+
+ if type (model) is gtk.TreeModelFilter:
+ iter = model.convert_iter_to_child_iter (iter)
+ model = model.get_model ()
+
+ class_path = eval(model.get_value (iter, 4))
+ cls = self.get_movie_class(class_path)
+
+ movie = cls.getPage(self.movies_page)[path[0]]
+ files = movie.getFiles()
+ if not files:
+ return False
+
+ self.add_recent (class_path, model.get_path (iter)[0])
+ return True
+
+ def on_movies_starting_video (self, widget, treeview, path):
+ return self.add_movie_to_recent (treeview, path)
+
+ def on_button_press_event (self, widget, event):
+ self.button_down = True
+
+ def on_button_release_event (self, widget, event):
+ self.button_down = False
+ self.on_value_changed (self.vadjust[self.current_treeview_name])
+
+ def on_value_changed (self, adjustment):
+ """Load more results when we get near the bottom of the treeview"""
+ pass
+
+ def clear_ui (self, what = None):
+ #window = self.vbox.window
+ #window.set_cursor (None)
+ self.progress_bar.set_fraction (0.0)
+ self.progress_bar.set_text ("")
+ self.clear_pages (what)
+
+ def clear_pages (self, what = None):
+ if what is None:
+ what = self.notebook_pages
+ if not type(what) is list:
+ what = [what]
+
+ for treeview_name in what:
+ treeview = self.treeview[treeview_name]
+ treeview.get_selection ().unselect_all ()
+ liststore = self.liststore[treeview_name]
+ liststore.clear ()
+
+ def refresh_categories (self):
+ self.fetch_classes (True)
+
+ def refresh_movies (self):
+ if self.movie_class_path != ():
+ self.refetch_movies ()
+
+ def set_show_posters (self, show = True):
+ self.cell_video_renderer.set_property ('use_placeholder', show)
+ if self.movie_class_path == ():
+ return
+ class_path = self.movie_class_path
+ self.movie_class_path = ()
+ self.show_movies (class_path, False, False)
+
+ def sync_config (self):
+ for key in self.input_configs.keys ():
+ keyname = gconf_key + '/' + key
+ value = self.input_configs [key]
+ if type (value) is bool:
+ self.gconf.set_bool (keyname, value)
+ elif type (value) is int:
+ self.gconf.set_int (keyname, value)
+ elif type (value) is float:
+ self.gconf.set_float (keyname, value)
+ else:
+ self.gconf.set_string (keyname, value)
+ if self.input_configs['show_posters'] != self.configs['show_posters']:
+ self.configs['show_posters'] = self.input_configs['show_posters']
+ self.set_show_posters (self.configs['show_posters'])
+
+ if self.input_configs['compatible'] != self.configs['compatible']:
+ self.configs['comptiable'] = self.input_configs['compatible']
+ self.stop_idle_task ()
+ self.start_idle_task ()
+
+ def on_refresh_button_clicked (self, button):
+ if self.current_treeview_name == 'categories':
+ self.refresh_categories ()
+ elif self.current_treeview_name == 'movies':
+ self.refresh_movies ()
+
+ def on_search_mode_entry_toggled (self, button):
+ self.search_web = self.search_mode_button.get_active()
+ if self.search_web:
+ button.set_label (_('search:'))
+ else:
+ button.set_label (_("filter:"))
+
+ def on_search_entry_activated (self, entry):
+ if self.search_web:
+ self.search.keyword = self.search_entry.get_text ()
+ self.search_terms = ''
+ self.movies_page = 0
+ self.show_movies ((CATEGORY_ID_SEARCH,), True, True)
+ self.notebook.set_current_page (1)
+ else:
+ self.search_terms = self.search_entry.get_text ()
+ self.treeview['movies'].get_model ().refilter ()
+
+ def on_search_button_clicked (self, button):
+ self.on_search_entry_activated (self.search_entry)
+
+ def on_config_dialog_delete (self, dialog, event):
+ dialog.hide ()
+ return True
+
+ def on_config_dialog_close (self, dialog):
+ dialog.hide ()
+
+ def on_config_dialog_response (self, dialog, response_id):
+ dialog.hide ()
+ if response_id == gtk.RESPONSE_DELETE_EVENT:
+ return
+ elif response_id == 1:
+ return
+
+ self.sync_config ()
+
+ def on_config_checkbutton_toggle (self, button, which):
+ self.input_configs[which] = button.get_active ()
+
+ def load_config (self):
+ ### current configs
+ self.configs = {}
+ self.configs['show_posters'] = self.gconf.get_bool ('%s/show_posters' % gconf_key)
+ self.configs['compatible'] = self.gconf.get_bool ('%s/compatible' % gconf_key)
+
+ ### user input configs
+ self.input_configs = self.configs.copy ()
+
+ def setup_config_dialog (self):
+ dialog = self.config_builder.get_object ("sohuvideo_config_dialog")
+ dialog.connect ("response", self.on_config_dialog_response)
+ dialog.connect ("close", self.on_config_dialog_close)
+ dialog.connect ("delete-event", self.on_config_dialog_delete)
+
+ button = self.config_builder.get_object ("sohuvideo_config_show_posters")
+ button.set_active (self.configs['show_posters'])
+ button.connect ("toggled", self.on_config_checkbutton_toggle, 'show_posters')
+
+ button = self.config_builder.get_object ("sohuvideo_config_compatible")
+ button.set_active (self.configs['compatible'])
+ button.connect ("toggled", self.on_config_checkbutton_toggle, 'compatible')
+
diff --git a/totem/plugin/sohuvideo.totem-plugin.in b/totem/plugin/sohuvideo.totem-plugin.in
new file mode 100644
index 0000000..d978f5f
--- /dev/null
+++ b/totem/plugin/sohuvideo.totem-plugin.in
@@ -0,0 +1,9 @@
+[Totem Plugin]
+Loader=python
+Module=sohuvideo
+IAge=1
+_Name=Sohu video browser
+_Description=A plugin to let you browse sohu videos.
+Authors=Luo Jinghua <sunmoon1997@gmail.com>
+Copyright=Copyright © 2010 Luo Jinghua
+Website=http://code.google.com/p/totem-sohu/
diff --git a/totem/plugin/sohuvideo.ui b/totem/plugin/sohuvideo.ui
new file mode 100644
index 0000000..e6f9c12
--- /dev/null
+++ b/totem/plugin/sohuvideo.ui
@@ -0,0 +1,378 @@
+<?xml version="1.0"?>
+<interface>
+ <!-- interface-requires gtk+ 2.12 -->
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkTreeStore" id="sohuvideo_liststore_categories">
+ <columns>
+ <!-- column-name Class -->
+ <column type="gint"/>
+ <!-- column-name Type -->
+ <column type="gint"/>
+ <!-- column-name Subclass -->
+ <column type="gint"/>
+ <!-- column-name Title -->
+ <column type="gchararray"/>
+ <!-- column-name Content -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="sohuvideo_liststore_files">
+ <columns>
+ <!-- column-name No. -->
+ <column type="gchararray"/>
+ <!-- column-name Title -->
+ <column type="gchararray"/>
+ <!-- column-name MRL -->
+ <column type="gchararray"/>
+ <!-- column-name OrigMRL -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="sohuvideo_liststore_movies">
+ <columns>
+ <!-- column-name Poster -->
+ <column type="GdkPixbuf"/>
+ <!-- column-name Title -->
+ <column type="gchararray"/>
+ <!-- column-name MRL -->
+ <column type="gchararray"/>
+ <!-- column-name gchararray1 -->
+ <column type="gchararray"/>
+ <!-- column-name ClassPath -->
+ <column type="gchararray"/>
+ <!-- column-name OrigMRL -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkVBox" id="sohuvideo_vbox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">4</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkToggleButton" id="sohuvideo_search_checkbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">Filtering the movie list using the keyword or search the keyword on site tv.sohu.com</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="sohuvideo_search_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip_text" translatable="yes">The keyword for filtering the movie list or searching movies on site tv.sohu.com</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="sohuvideo_search_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Filter the movie list using the keyword or search movies by the keyword</property>
+ <child>
+ <object class="GtkImage" id="sohuvideo_find_image">
+ <property name="visible">True</property>
+ <property name="stock">gtk-find</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="sohuvideo_refresh_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <child>
+ <object class="GtkImage" id="sohuvideo_refresh_image">
+ <property name="visible">True</property>
+ <property name="tooltip_text" translatable="yes">Refresh the movie list</property>
+ <property name="stock">gtk-refresh</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="sohuvideo_notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkScrolledWindow" id="sohuvideo_scrolled_window_categories">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTreeView" id="sohuvideo_treeview_categories">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">sohuvideo_liststore_movies</property>
+ <property name="headers_visible">False</property>
+ <property name="rules_hint">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Categories</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="sohuvideo_scrolled_window_movies">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Movies</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="sohuvideo_scrolled_window_files">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTreeView" id="sohuvideo_treeview_files">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">sohuvideo_liststore_categories</property>
+ <property name="headers_visible">False</property>
+ <property name="rules_hint">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Files</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkProgressBar" id="sohuvideo_progress_bar">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="sohuvideo_page_hbox">
+ <child>
+ <object class="GtkButton" id="sohuvideo_first_page_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">Go to the first page</property>
+ <child>
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="stock">gtk-goto-first</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="sohuvideo_prev_page_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">Go to the previous page</property>
+ <child>
+ <object class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-back</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="sohuvideo_next_page_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Go to the next page</property>
+ <child>
+ <object class="GtkImage" id="image4">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-forward</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="sohuvideo_last_page_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Go to the last page</property>
+ <child>
+ <object class="GtkImage" id="image5">
+ <property name="visible">True</property>
+ <property name="tooltip_text" translatable="yes">Goto the last page</property>
+ <property name="stock">gtk-goto-last</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="sohuvideo_max_page_label">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip_text" translatable="yes">The number of pages in this category</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="sohuvideo_which_page_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip_text" translatable="yes">The current page</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="adjustment">sohuvideo_page_adjustment</property>
+ <property name="climb_rate">1</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="sohuvideo_goto_page_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Go to specified page</property>
+ <child>
+ <object class="GtkImage" id="image6">
+ <property name="visible">True</property>
+ <property name="stock">gtk-jump-to</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">6</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="sohuvideo_favorites_hbox">
+ <child>
+ <object class="GtkButton" id="sohuvideo_add_to_favorites_button">
+ <property name="label" translatable="yes">Add to favorites</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="sohuvideo_remove_from_favorites_button">
+ <property name="label" translatable="yes">Remove from favorites</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkUIManager" id="sohuvideo-list-ui-manager"/>
+ <object class="GtkAdjustment" id="sohuvideo_page_adjustment">
+ <property name="upper">1</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+</interface>
diff --git a/totem/plugin/sohuvideo_translation.py.in b/totem/plugin/sohuvideo_translation.py.in
new file mode 100644
index 0000000..efc4c62
--- /dev/null
+++ b/totem/plugin/sohuvideo_translation.py.in
@@ -0,0 +1,9 @@
+import gettext
+
+GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@'
+SOHU_LOCALEDIR = '@LOCALEDIR@'
+gettext.bindtextdomain (GETTEXT_PACKAGE, SOHU_LOCALEDIR);
+gettext.bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+def _(message):
+ return gettext.ldgettext(GETTEXT_PACKAGE, message)
+
diff --git a/totem/plugin/sohuvideolist.py b/totem/plugin/sohuvideolist.py
new file mode 100644
index 0000000..81b26a1
--- /dev/null
+++ b/totem/plugin/sohuvideolist.py
@@ -0,0 +1,522 @@
+#!/bin/env python
+# -*- coding: utf-8 -*-
+# -*- python -*-
+# Author: Luo Jinghua
+
+import urllib
+import httplib
+import htmlentitydefs
+import time
+import re
+import os
+import StringIO
+import copy
+import sys
+import time
+
+from xml.dom import minidom
+import BeautifulSoup
+
+KANPPS = 'http://kan.pps.tv'
+MOVIE_LIST_PATH = '/movie_index.html'
+MOVIE_LIST_URL = KANPPS + MOVIE_LIST_PATH
+
+class SohuClass:
+ def __init__ (self):
+ self.id = 0
+ self.title = ''
+ self.url = ''
+ self.pages = {}
+ self.max_page = 1
+ self.parent = None
+
+ def __str__(self):
+ return 'SohuClass<%d %s %s>' % (self.id, self.title, self.url)
+
+ def __repr__(self):
+ return repr(self.dictionary())
+
+ def dictionary(self):
+ d = {}
+ d['id'] = self.id
+ d['url'] = self.url
+ d['title'] = self.title
+ return d
+
+ def parseid (self, url):
+ return int(re.sub('.*/(\d+)/.*', r'\1', url))
+
+ def parse (self, node):
+ self.title = unicode(node.contents[0]).encode('utf-8')
+ self.url = KANPPS + node['href'].encode('utf-8')
+ self.id = self.parseid(self.url)
+ return self
+
+ def load(self, d):
+ self.__dict__.update(d)
+
+ def resetMovies(self):
+ self.pages = {}
+ self.max_page = 1
+
+ def addMovies(self, movies, page_id = 0):
+ movies = copy.copy(movies)
+ for movie in movies:
+ movie.parent = self
+
+ if not self.pages.has_key(page_id):
+ self.pages[page_id] = []
+ self.pages[page_id] += movies
+
+ def setMovies(self, movies, page_id = 0, clone = True):
+ if clone:
+ movies = copy.copy(movies)
+ for movie in movies:
+ movie.parent = self
+
+ self.pages[page_id] = movies
+
+ def getMovies(self):
+ pages = self.getPages()
+ movies = []
+ for page in pages:
+ movies += page
+ return movies
+
+ def getPage(self, page_id):
+ assert (page_id >= 0 and page_id < self.max_page)
+ if self.pages.has_key(page_id):
+ return self.pages[page_id]
+ return []
+
+ def getPages(self):
+ keys = self.pages.keys()
+ keys.sort()
+ pages = []
+ for key in keys:
+ pages.append(self.pages[key])
+ return pages
+
+ def getMaxPage(self):
+ return self.max_page
+
+class SohuFile:
+ elements = [ 'id', 'ci', 'size', 'url' ]
+
+ def __str__ (self):
+ return 'SohuFile<%d %s %s>' % (self.id, self.title, self.url)
+
+ def __repr__(self):
+ return repr(self.dictionary())
+
+ def __eq__ (self, other):
+ for attr in SohuFile.elements:
+ if getattr (self, attr) != getattr (other, attr):
+ #print attr, getattr (self, attr), getattr (other, attr)
+ return False
+ return True
+
+ def dictionary(self):
+ d = {}
+ d['id'] = self.id
+ d['url'] = self.url
+ d['title'] = self.title
+ return d
+
+ def __init__ (self):
+ self.id = 0
+ self.ci = 0
+ self.size = 0
+ self.title = ''
+ self.url = ''
+ self.parent = None
+
+ def parse (self, s, ci):
+ idendpos = s.find(']')
+ valuestartpos = s.find('=') + 2
+ valueendpos = len(s) - 1
+ values = s[valuestartpos:valueendpos].split('|||')
+ self.id = int(s[6:idendpos])
+ self.title = values[0]
+ self.url = values[2]
+ self.ci = ci
+ return self
+
+ def load(self, d):
+ self.__dict__.update(d)
+
+class SohuMovie:
+ elements = [ 'id', 'title', 'director', 'actor',
+ 'area', 'size', 'pubtime', 'length',
+ 'lang', 'score', 'desc', 'image',
+ 'cn', 'playerurl']
+ def __str__(self):
+ return 'SohuMovie<%d %s %s %d %s>' % (self.id, self.title,
+ self.score, self.cn,
+ self.actor)
+
+ def __eq__ (self, other):
+ for i in SohuMovie.elements:
+ if getattr (self, i) != getattr (other, i):
+ return False
+ return self.files == other.files
+
+ def __repr__(self):
+ return repr(self.dictionary())
+
+ def dictionary(self):
+ d = {}
+ for key in SohuMovie.elements:
+ d[key] = getattr(self, key)
+ return d
+
+ def __init__ (self):
+ self.id = 0
+ self.title = ''
+ self.director = ''
+ self.actor = ''
+ self.area = ''
+ self.size = 0
+ self.pubtime = ''
+ self.length = ''
+ self.lang = ''
+ self.score = ''
+ self.desc = ''
+ self.image = ''
+ self.cn = 1
+ self.playerurl = ''
+ self.files = []
+ self.pixbuf = None
+ self.parent = None
+
+ def parseid (self, url):
+ s = url[url.rfind('/') + 1:]
+ if s.find('_') < 0:
+ ids = s.split('.')
+ else:
+ ids = s.split('_')
+ return int(ids[0])
+
+ def parse_actors(self, node):
+ actors = node.findAll('a')
+ result = []
+ for actor in actors:
+ if actor.contents:
+ result.append(extractString(actor.contents[-1]))
+ result = {}.fromkeys(result).keys()
+ return result
+
+ def parse_area (self, node):
+ area = unicode(extractString(node.contents[-1]), 'utf-8')
+ if area.find (u':') >= 0:
+ area = area[area.find(u':') + 1:]
+ return area.encode('utf-8')
+
+ def parse (self, node):
+ self.files = []
+ self.pixbuf = None
+ self.image = extractString(node.find('img')['src'])
+ tr = node.find('dl', { "class" : "tr" })
+ dt = node.find('dt')
+ self.score = extractNumberString(dt.find('span').contents[0])
+ self.title = extractString(dt.find('a').contents[0])
+ self.cn = extractNumber(dt.contents[-1], 1)
+ self.playerurl = KANPPS + extractString(dt.find('a')['href'])
+ li = node.findAll('li')
+ self.area = self.parse_area(li[0].find('span'))
+ self.pubtime = extractString(li[0].contents[-1])
+ self.actor = ', '.join(self.parse_actors(li[1]))
+ self.desc = extractString(li[2].contents[-1])
+ self.id = self.parseid(self.playerurl)
+ self.smallimage = self.image
+ return self
+
+ def load(self, d):
+ self.__dict__.update(d)
+ self.smallimage = self.image
+
+ def getFiles(self):
+ return self.files
+
+ def setFiles(self, files):
+ self.files = copy.copy(files)
+ for f in self.files:
+ f.parent = self
+
+def download(url, max_retry = 3, interval = 5):
+ if not url:
+ return ''
+ for i in range (max_retry):
+ try:
+ res = urllib.urlopen (url).read ()
+ except Exception, e:
+ print "Coudn't open url: ", e
+ res = None
+ time.sleep (interval)
+ if res:
+ break
+ return res
+
+def gbk2utf8(s):
+ return unicode(s, 'gb18030', 'ignore').encode('utf-8')
+
+def extractString(s):
+ return unicode(s).encode('utf-8')
+
+def extractStrings(ss):
+ result = []
+ for s in ss:
+ result.append(extractString(s))
+ return result
+
+def extractNumberString(s, default = ''):
+ s = extractString(s)
+ n = re.search(r'[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?', s)
+ if n:
+ return n.group()
+ return default
+
+def extractNumber(s, default = 0):
+ s = extractString(s)
+ n = re.search(r'[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?', s)
+ if n:
+ if '.' in n.group() or 'e' in n.group() or 'E' in n.group():
+ return float(n.group())
+ return int(n.group())
+ return default
+
+def extractNavigableStrings(node):
+ result = []
+ for content in node.contents:
+ if type(content) is BeautifulSoup.NavigableString:
+ result.append(extractString(content))
+ return result
+
+def parseMovieClassList(res):
+ ppslist = gbk2utf8(res)
+ client_list = '<dt>客户端列表</dt>'
+ startpos = ppslist.find(client_list)
+ if startpos < 0:
+ return []
+ endpos = ppslist[startpos:].find('</dd>')
+ if endpos < 0:
+ return []
+ soup = BeautifulSoup.BeautifulSoup(ppslist[startpos:startpos + endpos + 5])
+ result = []
+ for l in soup.findAll('li'):
+ cls = SohuClass()
+ result.append(cls.parse(l.next))
+ return result
+
+def getMovieClassList():
+ movie_index_fname = 'movie_index.html'
+ if os.path.exists(movie_index_fname):
+ ppslist = file(movie_index_fname).read()
+ else:
+ ppslist = download(MOVIE_LIST_URL)
+ file(movie_index_fname, 'wb').write(ppslist)
+
+ return parseMovieClassList(ppslist)
+
+def parseMovieList(movie_list, s):
+ s = gbk2utf8(s)
+ soup = BeautifulSoup.BeautifulSoup(s)
+ result = []
+ for l in soup.findAll('div', { "class" : "pltr" }):
+ movie = SohuMovie()
+ result.append(movie.parse(l))
+ return result
+
+def parseMovieListPage(movie_list, s):
+ tag = '<div class="pageNav">'
+ tagpos = s.find(tag)
+ if tagpos < 0:
+ return (1, 1)
+ startpos = s[tagpos:].find('<span>')
+ endpos = s[tagpos:].find('</span>')
+ pagestr = s[tagpos + startpos:tagpos + endpos]
+ m = re.search('(\d+)/(\d+)', pagestr)
+ result = m.groups()
+ return (int(result[0]), int(result[1]))
+
+def getMovieList(movie_list, retrieve_all = False):
+ s = download(movie_list.url)
+ page = parseMovieListPage(movie_list, s)
+ result = parseMovieList(movie_list, s)
+ if retrieve_all:
+ url = movie_list.url
+ pos = url.rfind('/')
+ baseurl = url[:pos + 1]
+ pos = url.rfind('.')
+ suffix = url[pos:]
+ for i in range(page[1] - 1):
+ print 'retrieving %d of %d' % (i + 2, page[1])
+ url = '%s%d%s' % (baseurl, i + 2, suffix)
+ s = download(url)
+ print url
+ result += parseMovieList(movie_list, s)
+ return result
+
+def parseMovieFileList(movie, s):
+ s = gbk2utf8(s)
+ result = []
+ ci = 0
+ for f in re.findall('plist\[\d+\][^;]*', s):
+ ppsfile = SohuFile()
+ result.append(ppsfile.parse(f, ci))
+ ci += 1
+ return result
+
+def getMovieFileList(movie):
+ s = download(movie.playerurl)
+ return parseMovieFileList(movie, s)
+
+class SohuVideoList:
+ def __init__(self):
+ self.classes = []
+
+ def reset(self):
+ self.classes = []
+
+ def getClasses(self):
+ return self.classes
+
+ def fetchClasses(self):
+ s = download(MOVIE_LIST_URL)
+ return s
+
+ def parseClasses(self, s):
+ if s:
+ result = parseMovieClassList(s)
+ else:
+ result = []
+ return result
+
+ def updateClasses(self, classes):
+ self.classes += copy.copy(classes)
+
+ def fetchMovieList(self, movie_class, page_id = 0):
+ url = movie_class.url
+ if page_id != 0:
+ assert (page_id < movie_class.max_page)
+ pos = url.rfind('/')
+ baseurl = url[:pos + 1]
+ pos = url.rfind('.')
+ suffix = url[pos:]
+ url = '%s%d%s' % (baseurl, page_id + 2, suffix)
+ s = download(url)
+ return s
+
+ def searchMovieList(self, keyword, page_id = 0):
+ baseurl = 'http://kan.pps.tv/search/'
+ keyword = unicode(keyword, 'utf-8').encode('gb18030')
+ url = baseurl + urllib.quote(keyword) + '/'
+ if page_id > 0:
+ url += "%d.html" % (page_id + 1)
+ s = download(url, interval = 10)
+ #print keyword, url, gbk2utf8(s)
+ return s
+
+ def parseMovieList(self, movie_class, s, page_id = 0):
+ page = parseMovieListPage(movie_class, s)
+ result = parseMovieList(movie_class, s)
+ for movie in result:
+ movie.page_id = page_id
+ if page_id == 0:
+ movie_class.max_page = page[1]
+ return result
+
+ def updateMovieList(self, movie_class, movie_list, page_id = 0):
+ if page_id == 0:
+ movie_class.movies = []
+ movie_class.movies += movie_list
+ movie_class.pages[page_id] = copy.copy(movie_list)
+
+ def fetchMovie(self, cls, movie):
+ s = download(movie.playerurl)
+ return s
+
+ def parseMovie(self, cls, movie, s):
+ result = parseMovieFileList(movie, s)
+ return result
+
+ def updateMovie(self, cls, movie, file_list):
+ movie.files = copy.copy(file_list)
+
+if __name__ == '__main__':
+ ## movie_class_list = getMovieClassList()
+
+ ## for movie_class in movie_class_list:
+ ## print movie_class
+
+ ## movie_list = getMovieList(movie_class_list[1])
+
+ ## for movie in movie_list:
+ ## print movie
+
+ ## file_list = getMovieFileList(movie_list[0])
+
+ ## for f in file_list:
+ ## print f
+
+ def test_get_file_list():
+ s = download('http://kan.pps.tv/play/281874.html')
+ file_list = parseMovieFileList(SohuMovie(), s)
+ for f in file_list:
+ print f
+
+ sys.exit(0)
+
+ def test_search ():
+ ppslist = SohuVideoList()
+ s = ppslist.searchMovieList('周星驰')
+ movie_list = ppslist.parseMovieList(SohuClass(), s)
+ for m in movie_list:
+ print m
+ sys.exit(0)
+
+ def test_ppslist():
+ ppslist = SohuVideoList()
+ s = ppslist.fetchClasses()
+ ppslist.updateClasses(ppslist.parseClasses(s))
+ classes = ppslist.getClasses()
+ for cls in classes:
+ print cls
+
+ cls = classes[0]
+ s = ppslist.fetchMovieList(cls)
+ ppslist.updateMovieList(cls, ppslist.parseMovieList(cls, s))
+ page_id = 1
+ while page_id < cls.max_page:
+ s = ppslist.fetchMovieList(cls, page_id)
+ ppslist.updateMovieList(cls, ppslist.parseMovieList(cls, s, page_id),
+ page_id)
+ page_id += 1
+
+ movies = cls.getMovies()
+ for movie in movies:
+ print movie
+
+ page = cls.getPage(0)
+ for movie in page:
+ s = ppslist.fetchMovie(cls, movie)
+ ppslist.updateMovie(cls, movie, ppslist.parseMovie(cls, movie, s))
+
+ for movie in page:
+ print movie
+ for f in movie.files:
+ print f
+ print
+
+ movie = page[0]
+ ppsfile = movie.files[0]
+ d = ppsfile.dictionary()
+
+ print ppsfile, d
+ ppsfile1 = SohuFile()
+ ppsfile1.load(d)
+ print ppsfile1, ppsfile == ppsfile1
+
+ #test_ppslist()
+ #test_search()
+ #test_get_file_list()