Author: duncan
Date: Mon Nov  5 03:06:24 2007
New Revision: 10077

Log:
[ 1825640 ] Video podcast
New plug-in from Krasimir Atanasov added


Added:
   branches/rel-1-7/freevo/src/video/plugins/vpodcast.py   (contents, props 
changed)
   branches/rel-1/freevo/src/video/plugins/vpodcast.py   (contents, props 
changed)
Modified:
   branches/rel-1-7/freevo/ChangeLog
   branches/rel-1/freevo/ChangeLog

Modified: branches/rel-1-7/freevo/ChangeLog
==============================================================================
--- branches/rel-1-7/freevo/ChangeLog   (original)
+++ branches/rel-1-7/freevo/ChangeLog   Mon Nov  5 03:06:24 2007
@@ -19,13 +19,14 @@
  * New Afrikaans translation (F#1790781)
  * New Romanian translation (F#1809393)
  * Updated Dutch translation (B#1766337)
- * Updated German translation (F#1770195,F#1824931)
- * Updated Spanish translation (F#1821771)
+ * Updated German translation (F#1770195)
+ * Updated Spanish translation (F#1821771,F#1824931)
  * New DVB streamer plug-in, allows pausing and recording DVB/ATSC streams 
(F#1720288)
  * New eject CD-ROM plug-in, adding a menu item to the drive menu (F#1773418)
  * New internet TV plug-in (F#1811634)
  * New lastfm menu plug-in (F#1792494)
  * New RSS audio podcast plug-in (F#1807634)
+ * New RSS video podcast plug-in (F#1825640)
  * New text entry and program search (F#1768790)
  * New youtube plug-in for the webserver (F#1783643,F#1792819)
  * Updated ivtv_xine_tv plug-in for stop confirmation, progressive seek, video 
group selection and more (F#1814720)

Added: branches/rel-1-7/freevo/src/video/plugins/vpodcast.py
==============================================================================
--- (empty file)
+++ branches/rel-1-7/freevo/src/video/plugins/vpodcast.py       Mon Nov  5 
03:06:24 2007
@@ -0,0 +1,509 @@
+# -*- coding: iso-8859-1 -*-
+# -----------------------------------------------------------------------
+# Video Podcast Player
+# -----------------------------------------------------------------------
+# $Id$
+#
+# -----------------------------------------------------------------------
+# Freevo - A Home Theater PC framework
+# Copyright (C) 2002 Krister Lagerstrom, et al.
+# Please see the file freevo/Docs/CREDITS for a complete list of authors.
+#
+# 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 MER-
+# CHANTABILITY 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.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# -----------------------------------------------------------------------
+
+# __author__ = 'Krasimir Atanasov'
+# __author_email__ = '[EMAIL PROTECTED]'
+
+import urllib2, os, threading, urllib, time, re, string
+import config, menu, rc, plugin, util, skin
+from item import Item
+from audio.player import PlayerGUI
+from video.videoitem import VideoItem
+from menu import MenuItem
+from gui import AlertBox, PopupBox, GUIObject
+from event import *
+#import util.feedparser
+#import youtube-dl
+
+MAX_AGE = 3600 * 10
+
+_player_ = None
+
+
+
+class PluginInterface(plugin.MainMenuPlugin):
+
+    """
+    Video podcast plugin
+
+    Add to local_conf.py
+    | plugin.activate('video.vpodcast')
+    | VPODCAST_LOCATIONS = [
+    |     ('You Tube - Top 
Viewed','http://youtube.com/rss/global/top_viewed.rss'),
+    |     ('You Tube - Norah Jones', 
'http://www.referd.info/tag/norah_jones/rss.php'),
+    |     ('You Tube - Top 
Rated','http://youtube.com/rss/global/top_rated.rss'),
+    |     ('Metacafe - Top 
Videos','http://www.metacafe.com/tags/top_videos/rss.xml'),
+    |     ('Metacafe - Music','http://www.metacafe.com/tags/music/rss.xml'),
+    |     ('Metacafe - Today Videos 
','http://www.metacafe.com/rss/today_videos/rss.xml'),
+    |     ('Metacafe - New 
Videos','http://www.metacafe.com/rss/new_videos.rss'),
+    |     ('CNN - Now in the 
news','http://rss.cnn.com/services/podcasting/nitn/rss.xml'),
+    |     ('CNN - The Larry 
King','http://rss.cnn.com/services/podcasting/lkl/rss?format=xml'),
+    |     ('Discovery 
Chanel','http://www.discovery.com/radio/xml/discovery_video.xml')
+    | ]
+    |
+    | VPODCAST_DIR = '/home/user_name/VPODCAST'
+    """
+
+    def __init__(self):
+        """ Initialise the Video postcast plug-in interface """
+        plugin.MainMenuPlugin.__init__(self)
+        self.plugin_name = 'vpodcast'
+        self.check_dir()
+
+
+    def config(self):
+        """ freevo plugins -i audio.vpodcast returns the info """
+        return [
+            ('VPODCAST_LOCATIONS', None, 'List of podcast locations'),
+            ('VPODCAST_DIR', None, 'Directory for downloaded podcasts')
+        ]
+
+
+    def items(self, parent):
+        return [ VPodcastMainMenuItem(parent) ]
+
+
+    def check_dir(self):
+        """ Check that the VPODCAST_DIR directories exist, if not create them 
"""
+        if not os.path.isdir(config.VPODCAST_DIR):
+            _debug_('%r does not exist, directory created' % 
(config.VPODCAST_DIR))
+            os.makedirs(config.VPODCAST_DIR)
+
+        for pcdir in config.VPODCAST_LOCATIONS:
+            pc_dir = config.VPODCAST_DIR + '/' + pcdir[0]
+            if not os.path.isdir(pc_dir):
+                os.makedirs(pc_dir)
+
+
+class VPVideoItem(VideoItem):
+    def __init__(self, name, url, parent):
+        self.vp_url = url
+        url = name
+        VideoItem.__init__(self, name, parent)
+
+
+    def play(self, arg=None, menuw=None, alternateplayer=False):
+        """ execute commands if defined """
+
+        if config.VIDEO_PRE_PLAY:
+            os.system(config.VIDEO_PRE_PLAY)
+
+        # play the item.
+        isYT = self.vp_url.find('youtube.com')  #YouTube podcast
+        isMC = self.vp_url.find('metacafe.com') #Metacafe podcast
+
+        if isYT != -1:
+            self.download_url = self.youtube(self.vp_url)
+
+        elif isMC != -1:
+            self.download_url = self.metacafe(self.vp_url)
+
+        else:
+            self.download_url = self.vp_url
+
+
+        if not os.path.exists(self.filename):
+            background = BGDownload(self.download_url,self.filename)
+            background.start()
+            popup = PopupBox(text=_('Buffering podcast...'))
+            popup.show()
+            time.sleep(20) # 20s. buffering time
+            popup.destroy()
+
+        if not self.possible_player:
+            for p in plugin.getbyname(plugin.VIDEO_PLAYER, True):
+                rating = p.rate(self) * 10
+                if config.VIDEO_PREFERED_PLAYER == p.name:
+                    rating += 1
+                if hasattr(self, 'force_player') and p.name == 
self.force_player:
+                    rating += 100
+                self.possible_player.append((rating, p))
+
+            self.possible_player.sort(lambda l, o: -cmp(l[0], o[0]))
+
+        if alternateplayer:
+            self.possible_player.reverse()
+
+        if not self.possible_player:
+            return
+
+        self.player_rating, self.player = self.possible_player[0]
+        if self.parent:
+            self.parent.current_item = self
+
+        if not self.menuw:
+            self.menuw = menuw
+
+        # if we have variants, play the first one as default
+        if self.variants:
+            self.variants[0].play(arg, menuw)
+            return
+
+        # if we have subitems (a movie with more than one file),
+        # we start playing the first that is physically available
+        if self.subitems:
+            self.error_in_subitem = 0
+            self.last_error_msg   = ''
+            self.current_subitem  = None
+
+            result = self.set_next_available_subitem()
+            if self.current_subitem: # 'result' is always 1 in this case
+                # The media is available now for playing
+                # Pass along the options, without loosing the subitem's own
+                # options
+                if self.current_subitem.mplayer_options:
+                    if self.mplayer_options:
+                        # With this set the player options are incorrect when 
there is more than 1 item
+                        #self.current_subitem.mplayer_options += ' ' + 
self.mplayer_options
+                        pass
+                else:
+                    self.current_subitem.mplayer_options = self.mplayer_options
+                # When playing a subitem, the menu must be hidden. If it is 
not,
+                # the playing will stop after the first subitem, since the
+                # PLAY_END/USER_END event is not forwarded to the parent
+                # videoitem.
+                # And besides, we don't need the menu between two subitems.
+                self.menuw.hide()
+                self.last_error_msg = self.current_subitem.play(arg, 
self.menuw)
+                if self.last_error_msg:
+                    self.error_in_subitem = 1
+                    # Go to the next playable subitem, using the loop in
+                    # eventhandler()
+                    self.eventhandler(PLAY_END)
+
+            elif not result:
+                # No media at all was found: error
+                ConfirmBox(text=(_('No media found for "%s".\n')+
+                                 _('Please insert the media.')) %
+                                 self.name, handler=self.play).show()
+            return
+
+        # normal plackback of one file
+        if self.url.startswith('file://'):
+            file = self.filename
+            if self.media_id:
+                mountdir, file = 
util.resolve_media_mountdir(self.media_id,file)
+                if mountdir:
+                    util.mount(mountdir)
+                else:
+                    self.menuw.show()
+                    ConfirmBox(text=(_('No media found for "%s".\n')+
+                                     _('Please insert the media.')) % file,
+                               handler=self.play).show()
+                    return
+
+            elif self.media:
+                util.mount(os.path.dirname(self.filename))
+
+        elif self.mode in ('dvd', 'vcd') and not self.filename and not 
self.media:
+            media = util.check_media(self.media_id)
+            if media:
+                self.media = media
+            else:
+                self.menuw.show()
+                ConfirmBox(text=(_('No media found for "%s".\n')+
+                                 _('Please insert the media.')) % self.url,
+                           handler=self.play).show()
+                return
+
+        if self.player_rating < 10:
+            AlertBox(text=_('No player for this item found')).show()
+            return
+
+        mplayer_options = self.mplayer_options.split(' ')
+        if not mplayer_options:
+            mplayer_options = []
+
+        if arg:
+            mplayer_options += arg.split(' ')
+
+        if self.menuw.visible:
+            self.menuw.hide()
+
+        self.plugin_eventhandler(PLAY, menuw)
+
+        error = self.player.play(mplayer_options, self)
+
+        if error:
+            # If we are a subitem we don't show any error message before
+            # having tried all the subitems
+            if hasattr(self.parent, 'subitems') and self.parent.subitems:
+                return error
+            else:
+                AlertBox(text=error, handler=self.error_handler).show()
+
+
+    def youtube(self, url):
+        const_video_url_str = 'http://www.youtube.com/watch?v=%s'
+        const_video_url_re = 
re.compile(r'^((?:http://)?(?:\w+\.)?youtube\.com/(?:v/|(?:watch(?:\.php)?)?\?(?:.+&)?v=))?([0-9A-Za-z_-]+)(?(1)[&/].*)?$')
+        const_url_t_param_re = re.compile(r'[,{]t:\'([^\']*)\'')
+        const_video_url_real_str = 
'http://www.youtube.com/get_video?video_id=%s&t=%s'
+        const_video_title_re = re.compile(r'<title>YouTube - ([^<]*)</title>', 
re.M | re.I)
+        try:
+            video_url_mo = const_video_url_re.match(url)
+            video_url_id = video_url_mo.group(2)
+            video_url = const_video_url_str % video_url_id
+            # Retrieve video webpage
+            video_webpage = urllib.urlopen(video_url).read()
+            match = const_url_t_param_re.search(video_webpage)
+            if match is None:
+                print 'step_error'
+            video_url_t_param = match.group(1)
+            # Retrieve real video URL
+            video_url_real = const_video_url_real_str % (video_url_id, 
video_url_t_param)
+            return video_url_real
+        except:
+            print 'Error YouTube URL'
+
+
+
+    def metacafe(self, url):
+        video_url =  url
+        const_video_url_re = 
re.compile(r'(?:http://)?(?:www\.)?metacafe\.com/watch/([^/]+)/([^/]+/)?.*')
+        const_normalized_url_str = 'http://www.metacafe.com/watch/%s/'
+        const_age_post_data = 
r'allowAdultContent=1&submit=Continue+-+I%27m+over+18'
+        const_video_mediaurl_re = re.compile(r'&mediaURL=([^&]+)&', re.M)
+
+        try:
+            # Verify video URL format and extract URL data to normalize URL
+            video_url_mo = const_video_url_re.match(video_url)
+            if video_url_mo is None:
+                sys.exit('Error: URL does not seem to be a metacafe video URL. 
If it is, report a bug.')
+            video_url_id = video_url_mo.group(1)
+            video_url_title = (video_url_mo.group(2) is not None) and 
video_url_mo.group(2)[:-1] or None
+            video_url = const_normalized_url_str % video_url_id
+
+            # Retrieve video webpage
+            video_webpage = urllib.urlopen(video_url).read()
+            # Retrieve real video URL
+            video_url_real = self.extract_step('Extracting real video URL', 
'unable to extract real video URL', \
+                const_video_mediaurl_re, video_webpage)
+            return video_url_real
+        except:
+            print 'Error Metacafe URL'
+
+
+
+    def extract_step(self,step_title, step_error, regexp, data):
+        try:
+            match = regexp.search(data)
+            if match is None:
+                error_advice_exit(step_error)
+
+            extracted_data = match.group(1)
+
+            return extracted_data
+
+        except KeyboardInterrupt:
+            sys.exit('\n')
+
+
+
+class VPodcastMainMenuItem(MenuItem):
+    """
+    this is the item for the main menu and creates the list
+    of commands in a submenu.
+    """
+    def __init__(self, parent):
+        MenuItem.__init__(self, parent, arg='audio', skin_type='radio')
+        self.name = _('Video Podcast')
+
+
+    def actions(self):
+        """
+        return a list of actions for this item
+        """
+        return [ (self.create_podcast_menu, 'stations') ]
+
+
+    def create_podcast_submenu(self, arg=None, menuw=None, image=None):
+        popup = PopupBox(text=_('Fetching podcast...'))
+        popup.show()
+        url = arg[1]
+        p = podcast()
+        p.open_rss(url)
+        p.rss_title()
+        p.rss_count()
+
+        podcast_items = []
+        for pc_location in range(p.rss.count):
+            p.rss_item(pc_location)
+            if p.image != None:
+                image = config.VPODCAST_DIR + '/' + arg[0] + '/' + p.title + 
'.jpg'
+                self.download(p.image,image)
+            else:
+                image = None
+            url = p.link
+            name = p.title
+            if url != 'ERROR':
+                isYT = url.find('youtube.com')
+                isMC = url.find('metacafe.com')
+                if isYT == -1 and isMC == -1:
+                    file_ext = '.avi'
+                else:
+                    file_ext = '.flv'
+
+                filename  = config.VPODCAST_DIR + '/' + arg[0] + '/' + name + 
file_ext
+                podcast_items += [menu.MenuItem(_(p.title), \
+                    action=VPVideoItem(filename, url, self), arg=None, 
image=image)]
+
+        popup.destroy()
+        if (len(podcast_items) == 0):
+            podcast_items += [menu.MenuItem(_('No Podcast locations found'),
+                                             menwu.goto_prev_page, 0)]
+        podcast_sub_menu = menu.Menu(_('Video Podcasts'), podcast_items)
+        rc.app(None)
+        menuw.pushmenu(podcast_sub_menu)
+        menuw.refresh()
+
+    def create_podcast_menu(self,arg=None, menuw=None):
+        popup = PopupBox(text=_('Fetching podcasts...'))
+        popup.show()
+        podcast_menu_items = []
+
+
+        for location in config.VPODCAST_LOCATIONS:
+            url = location[1]
+            image_path = config.VPODCAST_DIR + '/' + location[0] + '/' + 
'cover.jpg'
+            if self.check_logo(image_path):
+                p = podcast()
+                p.open_rss(url)
+                p.rss_title()
+                name = p.rss_title
+                try:
+                    image_url = p.rss_image
+                    self.download(image_url,image_path)
+                except:
+                    print 'No image in RSS'
+
+            if (len(config.VPODCAST_DIR) == 0):
+                podcast_items += [menu.MenuItem(_('Set VPODCAST_DIR in 
local_conf.py'), menwu.goto_prev_page, 0)]
+
+            podcast_menu_items += [menu.MenuItem(_(location[0]), 
action=self.create_podcast_submenu, \
+                arg=location, image=image_path)]
+
+        popup.destroy()
+        podcast_main_menu = menu.Menu(_('Video Podcasts'), podcast_menu_items)
+        rc.app(None)
+        menuw.pushmenu(podcast_main_menu)
+        menuw.refresh()
+
+    def download(self,url,savefile):
+        file = urllib2.urlopen(url).read()
+        save = open(savefile, 'w')
+        print >> save, file
+        save.close()
+
+    def check_logo(self,logo_file):
+        if os.path.exists(logo_file) == 0 or (abs(time.time() - 
os.path.getmtime(logo_file)) > MAX_AGE):
+            return True
+        else:
+            return False
+
+
+
+
+class podcast:
+
+    def __init__(self):
+        pass
+
+    def open_rss(self, url):
+        self.rss = util.feedparser.parse(url)
+        self.encoding = self.rss.encoding
+
+
+    def rss_title(self):
+        try:
+            self.rss_title = self.rss.feed.title.encode(self.encoding)
+
+            #self.rss_date = self.rss.feed.date
+        except:
+            print 'Error rss_title'
+            self.rss_title = None
+
+        try:
+            self.rss_description = 
self.rss.feed.description.encode(self.encoding)
+        except:
+            print 'Error rss_description'
+
+        try:
+            self.rss_image = self.rss.feed.image.url
+        except:
+            self.rss_image = None
+
+    def rss_count(self):
+        self.rss.count =  len(self.rss.entries)
+
+
+    def rss_item(self,item=0):
+
+        try:
+            self.title = self.rss.entries[item].title.encode(self.encoding)
+            self.title = re.sub('(/)','_',self.title)
+            description_all = self.rss.entries[item].description
+            self.link = self.rss.entries[item].link
+            # Search for image
+            #img_pattern = '<img src="(.*?)" align='
+            img_pattern = 'img src="(.*?)"'
+            try:
+                self.image = re.search(img_pattern, description_all).group(1)
+            except:
+                self.image = None
+
+        except:
+            pass
+
+        if self.link == None:
+            self.link = 'ERROR'
+
+
+
+class BGDownload(threading.Thread):
+        # Download file in background
+    def __init__(self, url, savefile):
+        threading.Thread.__init__(self)
+        self.url = url
+        self.savefile = savefile
+
+    def run(self):
+        try:
+            file = urllib2.urlopen(self.url)
+            info = file.info()
+            save = open(self.savefile, 'wb')
+            chunkSize = 25
+            totalBytes = int(info['Content-Length'])
+            downloadBytes = 0
+            bytesLeft = totalBytes
+            while bytesLeft > 0:
+                chunk = file.read(chunkSize)
+                readBytes = len(chunk)
+                downloadBytes += readBytes
+                bytesLeft -= readBytes
+                save.write(chunk)
+        except:
+            print 'Download Error !'

Modified: branches/rel-1/freevo/ChangeLog
==============================================================================
--- branches/rel-1/freevo/ChangeLog     (original)
+++ branches/rel-1/freevo/ChangeLog     Mon Nov  5 03:06:24 2007
@@ -13,6 +13,12 @@
 
 svn co svn://svn.freevo.org/freevo/branches/rel-1/freevo freevo-1.x
 
+== Release 1.8.0 (2007-12-22) ==
+--------------------------------
+
+ * New changed the event and timer handling to use kaa notifier and kaa rpc
+ * New mplayer aspect plug-in to force the aspect ratio (F#1825484)
+
 == Release 1.7.4 (2007-11-15) ==
 --------------------------------
 
@@ -26,6 +32,7 @@
  * New internet TV plug-in (F#1811634)
  * New lastfm menu plug-in (F#1792494)
  * New RSS audio podcast plug-in (F#1807634)
+ * New RSS video podcast plug-in (F#1825640)
  * New text entry and program search (F#1768790)
  * New youtube plug-in for the webserver (F#1783643,F#1792819)
  * Updated ivtv_xine_tv plug-in for stop confirmation, progressive seek, video 
group selection and more (F#1814720)

Added: branches/rel-1/freevo/src/video/plugins/vpodcast.py
==============================================================================
--- (empty file)
+++ branches/rel-1/freevo/src/video/plugins/vpodcast.py Mon Nov  5 03:06:24 2007
@@ -0,0 +1,509 @@
+# -*- coding: iso-8859-1 -*-
+# -----------------------------------------------------------------------
+# Video Podcast Player
+# -----------------------------------------------------------------------
+# $Id$
+#
+# -----------------------------------------------------------------------
+# Freevo - A Home Theater PC framework
+# Copyright (C) 2002 Krister Lagerstrom, et al.
+# Please see the file freevo/Docs/CREDITS for a complete list of authors.
+#
+# 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 MER-
+# CHANTABILITY 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.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# -----------------------------------------------------------------------
+
+# __author__ = 'Krasimir Atanasov'
+# __author_email__ = '[EMAIL PROTECTED]'
+
+import urllib2, os, threading, urllib, time, re, string
+import config, menu, rc, plugin, util, skin
+from item import Item
+from audio.player import PlayerGUI
+from video.videoitem import VideoItem
+from menu import MenuItem
+from gui import AlertBox, PopupBox, GUIObject
+from event import *
+#import util.feedparser
+#import youtube-dl
+
+MAX_AGE = 3600 * 10
+
+_player_ = None
+
+
+
+class PluginInterface(plugin.MainMenuPlugin):
+
+    """
+    Video podcast plugin
+
+    Add to local_conf.py
+    | plugin.activate('video.vpodcast')
+    | VPODCAST_LOCATIONS = [
+    |     ('You Tube - Top 
Viewed','http://youtube.com/rss/global/top_viewed.rss'),
+    |     ('You Tube - Norah Jones', 
'http://www.referd.info/tag/norah_jones/rss.php'),
+    |     ('You Tube - Top 
Rated','http://youtube.com/rss/global/top_rated.rss'),
+    |     ('Metacafe - Top 
Videos','http://www.metacafe.com/tags/top_videos/rss.xml'),
+    |     ('Metacafe - Music','http://www.metacafe.com/tags/music/rss.xml'),
+    |     ('Metacafe - Today Videos 
','http://www.metacafe.com/rss/today_videos/rss.xml'),
+    |     ('Metacafe - New 
Videos','http://www.metacafe.com/rss/new_videos.rss'),
+    |     ('CNN - Now in the 
news','http://rss.cnn.com/services/podcasting/nitn/rss.xml'),
+    |     ('CNN - The Larry 
King','http://rss.cnn.com/services/podcasting/lkl/rss?format=xml'),
+    |     ('Discovery 
Chanel','http://www.discovery.com/radio/xml/discovery_video.xml')
+    | ]
+    |
+    | VPODCAST_DIR = '/home/user_name/VPODCAST'
+    """
+
+    def __init__(self):
+        """ Initialise the Video postcast plug-in interface """
+        plugin.MainMenuPlugin.__init__(self)
+        self.plugin_name = 'vpodcast'
+        self.check_dir()
+
+
+    def config(self):
+        """ freevo plugins -i audio.vpodcast returns the info """
+        return [
+            ('VPODCAST_LOCATIONS', None, 'List of podcast locations'),
+            ('VPODCAST_DIR', None, 'Directory for downloaded podcasts')
+        ]
+
+
+    def items(self, parent):
+        return [ VPodcastMainMenuItem(parent) ]
+
+
+    def check_dir(self):
+        """ Check that the VPODCAST_DIR directories exist, if not create them 
"""
+        if not os.path.isdir(config.VPODCAST_DIR):
+            _debug_('%r does not exist, directory created' % 
(config.VPODCAST_DIR))
+            os.makedirs(config.VPODCAST_DIR)
+
+        for pcdir in config.VPODCAST_LOCATIONS:
+            pc_dir = config.VPODCAST_DIR + '/' + pcdir[0]
+            if not os.path.isdir(pc_dir):
+                os.makedirs(pc_dir)
+
+
+class VPVideoItem(VideoItem):
+    def __init__(self, name, url, parent):
+        self.vp_url = url
+        url = name
+        VideoItem.__init__(self, name, parent)
+
+
+    def play(self, arg=None, menuw=None, alternateplayer=False):
+        """ execute commands if defined """
+
+        if config.VIDEO_PRE_PLAY:
+            os.system(config.VIDEO_PRE_PLAY)
+
+        # play the item.
+        isYT = self.vp_url.find('youtube.com')  #YouTube podcast
+        isMC = self.vp_url.find('metacafe.com') #Metacafe podcast
+
+        if isYT != -1:
+            self.download_url = self.youtube(self.vp_url)
+
+        elif isMC != -1:
+            self.download_url = self.metacafe(self.vp_url)
+
+        else:
+            self.download_url = self.vp_url
+
+
+        if not os.path.exists(self.filename):
+            background = BGDownload(self.download_url,self.filename)
+            background.start()
+            popup = PopupBox(text=_('Buffering podcast...'))
+            popup.show()
+            time.sleep(20) # 20s. buffering time
+            popup.destroy()
+
+        if not self.possible_player:
+            for p in plugin.getbyname(plugin.VIDEO_PLAYER, True):
+                rating = p.rate(self) * 10
+                if config.VIDEO_PREFERED_PLAYER == p.name:
+                    rating += 1
+                if hasattr(self, 'force_player') and p.name == 
self.force_player:
+                    rating += 100
+                self.possible_player.append((rating, p))
+
+            self.possible_player.sort(lambda l, o: -cmp(l[0], o[0]))
+
+        if alternateplayer:
+            self.possible_player.reverse()
+
+        if not self.possible_player:
+            return
+
+        self.player_rating, self.player = self.possible_player[0]
+        if self.parent:
+            self.parent.current_item = self
+
+        if not self.menuw:
+            self.menuw = menuw
+
+        # if we have variants, play the first one as default
+        if self.variants:
+            self.variants[0].play(arg, menuw)
+            return
+
+        # if we have subitems (a movie with more than one file),
+        # we start playing the first that is physically available
+        if self.subitems:
+            self.error_in_subitem = 0
+            self.last_error_msg   = ''
+            self.current_subitem  = None
+
+            result = self.set_next_available_subitem()
+            if self.current_subitem: # 'result' is always 1 in this case
+                # The media is available now for playing
+                # Pass along the options, without loosing the subitem's own
+                # options
+                if self.current_subitem.mplayer_options:
+                    if self.mplayer_options:
+                        # With this set the player options are incorrect when 
there is more than 1 item
+                        #self.current_subitem.mplayer_options += ' ' + 
self.mplayer_options
+                        pass
+                else:
+                    self.current_subitem.mplayer_options = self.mplayer_options
+                # When playing a subitem, the menu must be hidden. If it is 
not,
+                # the playing will stop after the first subitem, since the
+                # PLAY_END/USER_END event is not forwarded to the parent
+                # videoitem.
+                # And besides, we don't need the menu between two subitems.
+                self.menuw.hide()
+                self.last_error_msg = self.current_subitem.play(arg, 
self.menuw)
+                if self.last_error_msg:
+                    self.error_in_subitem = 1
+                    # Go to the next playable subitem, using the loop in
+                    # eventhandler()
+                    self.eventhandler(PLAY_END)
+
+            elif not result:
+                # No media at all was found: error
+                ConfirmBox(text=(_('No media found for "%s".\n')+
+                                 _('Please insert the media.')) %
+                                 self.name, handler=self.play).show()
+            return
+
+        # normal plackback of one file
+        if self.url.startswith('file://'):
+            file = self.filename
+            if self.media_id:
+                mountdir, file = 
util.resolve_media_mountdir(self.media_id,file)
+                if mountdir:
+                    util.mount(mountdir)
+                else:
+                    self.menuw.show()
+                    ConfirmBox(text=(_('No media found for "%s".\n')+
+                                     _('Please insert the media.')) % file,
+                               handler=self.play).show()
+                    return
+
+            elif self.media:
+                util.mount(os.path.dirname(self.filename))
+
+        elif self.mode in ('dvd', 'vcd') and not self.filename and not 
self.media:
+            media = util.check_media(self.media_id)
+            if media:
+                self.media = media
+            else:
+                self.menuw.show()
+                ConfirmBox(text=(_('No media found for "%s".\n')+
+                                 _('Please insert the media.')) % self.url,
+                           handler=self.play).show()
+                return
+
+        if self.player_rating < 10:
+            AlertBox(text=_('No player for this item found')).show()
+            return
+
+        mplayer_options = self.mplayer_options.split(' ')
+        if not mplayer_options:
+            mplayer_options = []
+
+        if arg:
+            mplayer_options += arg.split(' ')
+
+        if self.menuw.visible:
+            self.menuw.hide()
+
+        self.plugin_eventhandler(PLAY, menuw)
+
+        error = self.player.play(mplayer_options, self)
+
+        if error:
+            # If we are a subitem we don't show any error message before
+            # having tried all the subitems
+            if hasattr(self.parent, 'subitems') and self.parent.subitems:
+                return error
+            else:
+                AlertBox(text=error, handler=self.error_handler).show()
+
+
+    def youtube(self, url):
+        const_video_url_str = 'http://www.youtube.com/watch?v=%s'
+        const_video_url_re = 
re.compile(r'^((?:http://)?(?:\w+\.)?youtube\.com/(?:v/|(?:watch(?:\.php)?)?\?(?:.+&)?v=))?([0-9A-Za-z_-]+)(?(1)[&/].*)?$')
+        const_url_t_param_re = re.compile(r'[,{]t:\'([^\']*)\'')
+        const_video_url_real_str = 
'http://www.youtube.com/get_video?video_id=%s&t=%s'
+        const_video_title_re = re.compile(r'<title>YouTube - ([^<]*)</title>', 
re.M | re.I)
+        try:
+            video_url_mo = const_video_url_re.match(url)
+            video_url_id = video_url_mo.group(2)
+            video_url = const_video_url_str % video_url_id
+            # Retrieve video webpage
+            video_webpage = urllib.urlopen(video_url).read()
+            match = const_url_t_param_re.search(video_webpage)
+            if match is None:
+                print 'step_error'
+            video_url_t_param = match.group(1)
+            # Retrieve real video URL
+            video_url_real = const_video_url_real_str % (video_url_id, 
video_url_t_param)
+            return video_url_real
+        except:
+            print 'Error YouTube URL'
+
+
+
+    def metacafe(self, url):
+        video_url =  url
+        const_video_url_re = 
re.compile(r'(?:http://)?(?:www\.)?metacafe\.com/watch/([^/]+)/([^/]+/)?.*')
+        const_normalized_url_str = 'http://www.metacafe.com/watch/%s/'
+        const_age_post_data = 
r'allowAdultContent=1&submit=Continue+-+I%27m+over+18'
+        const_video_mediaurl_re = re.compile(r'&mediaURL=([^&]+)&', re.M)
+
+        try:
+            # Verify video URL format and extract URL data to normalize URL
+            video_url_mo = const_video_url_re.match(video_url)
+            if video_url_mo is None:
+                sys.exit('Error: URL does not seem to be a metacafe video URL. 
If it is, report a bug.')
+            video_url_id = video_url_mo.group(1)
+            video_url_title = (video_url_mo.group(2) is not None) and 
video_url_mo.group(2)[:-1] or None
+            video_url = const_normalized_url_str % video_url_id
+
+            # Retrieve video webpage
+            video_webpage = urllib.urlopen(video_url).read()
+            # Retrieve real video URL
+            video_url_real = self.extract_step('Extracting real video URL', 
'unable to extract real video URL', \
+                const_video_mediaurl_re, video_webpage)
+            return video_url_real
+        except:
+            print 'Error Metacafe URL'
+
+
+
+    def extract_step(self,step_title, step_error, regexp, data):
+        try:
+            match = regexp.search(data)
+            if match is None:
+                error_advice_exit(step_error)
+
+            extracted_data = match.group(1)
+
+            return extracted_data
+
+        except KeyboardInterrupt:
+            sys.exit('\n')
+
+
+
+class VPodcastMainMenuItem(MenuItem):
+    """
+    this is the item for the main menu and creates the list
+    of commands in a submenu.
+    """
+    def __init__(self, parent):
+        MenuItem.__init__(self, parent, arg='audio', skin_type='radio')
+        self.name = _('Video Podcast')
+
+
+    def actions(self):
+        """
+        return a list of actions for this item
+        """
+        return [ (self.create_podcast_menu, 'stations') ]
+
+
+    def create_podcast_submenu(self, arg=None, menuw=None, image=None):
+        popup = PopupBox(text=_('Fetching podcast...'))
+        popup.show()
+        url = arg[1]
+        p = podcast()
+        p.open_rss(url)
+        p.rss_title()
+        p.rss_count()
+
+        podcast_items = []
+        for pc_location in range(p.rss.count):
+            p.rss_item(pc_location)
+            if p.image != None:
+                image = config.VPODCAST_DIR + '/' + arg[0] + '/' + p.title + 
'.jpg'
+                self.download(p.image,image)
+            else:
+                image = None
+            url = p.link
+            name = p.title
+            if url != 'ERROR':
+                isYT = url.find('youtube.com')
+                isMC = url.find('metacafe.com')
+                if isYT == -1 and isMC == -1:
+                    file_ext = '.avi'
+                else:
+                    file_ext = '.flv'
+
+                filename  = config.VPODCAST_DIR + '/' + arg[0] + '/' + name + 
file_ext
+                podcast_items += [menu.MenuItem(_(p.title), \
+                    action=VPVideoItem(filename, url, self), arg=None, 
image=image)]
+
+        popup.destroy()
+        if (len(podcast_items) == 0):
+            podcast_items += [menu.MenuItem(_('No Podcast locations found'),
+                                             menwu.goto_prev_page, 0)]
+        podcast_sub_menu = menu.Menu(_('Video Podcasts'), podcast_items)
+        rc.app(None)
+        menuw.pushmenu(podcast_sub_menu)
+        menuw.refresh()
+
+    def create_podcast_menu(self,arg=None, menuw=None):
+        popup = PopupBox(text=_('Fetching podcasts...'))
+        popup.show()
+        podcast_menu_items = []
+
+
+        for location in config.VPODCAST_LOCATIONS:
+            url = location[1]
+            image_path = config.VPODCAST_DIR + '/' + location[0] + '/' + 
'cover.jpg'
+            if self.check_logo(image_path):
+                p = podcast()
+                p.open_rss(url)
+                p.rss_title()
+                name = p.rss_title
+                try:
+                    image_url = p.rss_image
+                    self.download(image_url,image_path)
+                except:
+                    print 'No image in RSS'
+
+            if (len(config.VPODCAST_DIR) == 0):
+                podcast_items += [menu.MenuItem(_('Set VPODCAST_DIR in 
local_conf.py'), menwu.goto_prev_page, 0)]
+
+            podcast_menu_items += [menu.MenuItem(_(location[0]), 
action=self.create_podcast_submenu, \
+                arg=location, image=image_path)]
+
+        popup.destroy()
+        podcast_main_menu = menu.Menu(_('Video Podcasts'), podcast_menu_items)
+        rc.app(None)
+        menuw.pushmenu(podcast_main_menu)
+        menuw.refresh()
+
+    def download(self,url,savefile):
+        file = urllib2.urlopen(url).read()
+        save = open(savefile, 'w')
+        print >> save, file
+        save.close()
+
+    def check_logo(self,logo_file):
+        if os.path.exists(logo_file) == 0 or (abs(time.time() - 
os.path.getmtime(logo_file)) > MAX_AGE):
+            return True
+        else:
+            return False
+
+
+
+
+class podcast:
+
+    def __init__(self):
+        pass
+
+    def open_rss(self, url):
+        self.rss = util.feedparser.parse(url)
+        self.encoding = self.rss.encoding
+
+
+    def rss_title(self):
+        try:
+            self.rss_title = self.rss.feed.title.encode(self.encoding)
+
+            #self.rss_date = self.rss.feed.date
+        except:
+            print 'Error rss_title'
+            self.rss_title = None
+
+        try:
+            self.rss_description = 
self.rss.feed.description.encode(self.encoding)
+        except:
+            print 'Error rss_description'
+
+        try:
+            self.rss_image = self.rss.feed.image.url
+        except:
+            self.rss_image = None
+
+    def rss_count(self):
+        self.rss.count =  len(self.rss.entries)
+
+
+    def rss_item(self,item=0):
+
+        try:
+            self.title = self.rss.entries[item].title.encode(self.encoding)
+            self.title = re.sub('(/)','_',self.title)
+            description_all = self.rss.entries[item].description
+            self.link = self.rss.entries[item].link
+            # Search for image
+            #img_pattern = '<img src="(.*?)" align='
+            img_pattern = 'img src="(.*?)"'
+            try:
+                self.image = re.search(img_pattern, description_all).group(1)
+            except:
+                self.image = None
+
+        except:
+            pass
+
+        if self.link == None:
+            self.link = 'ERROR'
+
+
+
+class BGDownload(threading.Thread):
+        # Download file in background
+    def __init__(self, url, savefile):
+        threading.Thread.__init__(self)
+        self.url = url
+        self.savefile = savefile
+
+    def run(self):
+        try:
+            file = urllib2.urlopen(self.url)
+            info = file.info()
+            save = open(self.savefile, 'wb')
+            chunkSize = 25
+            totalBytes = int(info['Content-Length'])
+            downloadBytes = 0
+            bytesLeft = totalBytes
+            while bytesLeft > 0:
+                chunk = file.read(chunkSize)
+                readBytes = len(chunk)
+                downloadBytes += readBytes
+                bytesLeft -= readBytes
+                save.write(chunk)
+        except:
+            print 'Download Error !'

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >> http://get.splunk.com/
_______________________________________________
Freevo-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog

Reply via email to