Changeset 1760

Show
Ignore:
Timestamp:
08/09/08 13:51:33 (4 months ago)
Author:
duncan
svm:headrev:

cc3e1ea1-1e01-0410-8d68-8b121e83a9d5:10987
Message:

Fixed vpodcast:
- this crashed quite a lot
- uses youtube downloader, which needed to be renamed to youtube_dl.py
- made the buffering a lot faster, starts playing when there is something downloaded
- corrected the file extensions
Added .mp4 to freevo_config and local_conf.py.example
- the nocache option is good to mplayer when playing and downloading at the same time

Location:
freevo
Files:
3 modified

Legend:

Unmodified
Added
Removed
  • freevo/freevo_config.py

    r1759 r1760  
    109109# is different, there will be only a warning 
    110110 
    111 LOCAL_CONF_VERSION = 5.25 
     111LOCAL_CONF_VERSION = 5.26 
    112112 
    113113# Description of changes in each new version 
     
    379379     Added SYS_USE_MOUSE option for enabling mouse support if needed. False by default 
    380380     """), 
     381    (5.26, 
     382     """ 
     383     """), 
    381384] 
    382385 
     
    15921595    'avi'    : '-cache 5000 -idx', 
    15931596    'flv'    : '-nocache -forceidx', 
     1597    'mp4'    : '-nocache -forceidx', 
    15941598    'rm'     : '-cache 5000 -forceidx', 
    15951599    'rmvb'   : '-cache 5000 -forceidx', 
  • freevo/local_conf.py.example

    r1759 r1760  
    5151# ----------------------------------------------------------------------- 
    5252 
    53 CONFIG_VERSION = 5.23 
     53CONFIG_VERSION = 5.26 
    5454 
    5555# ====================================================================== 
     
    11271127# MPLAYER_ARGS['avi']     = '-cache 5000 -idx' 
    11281128# MPLAYER_ARGS['flv']     = '-nocache -forceidx' 
     1129# MPLAYER_ARGS['mp4']     = '-nocache -forceidx' 
    11291130# MPLAYER_ARGS['rm']      = '-cache 5000 -forceidx' 
    11301131# MPLAYER_ARGS['rmvb']    = '-cache 5000 -forceidx' 
  • freevo/src/video/plugins/vpodcast.py

    r1571 r1760  
    3838from gui import AlertBox, PopupBox, GUIObject 
    3939from event import * 
    40 #import youtube-dl 
     40import util.youtube_dl as youtube 
     41from util.benchmark import benchmark 
     42benchmarking = config.DEBUG_BENCHMARKING 
     43from pprint import pformat, pprint 
    4144 
    4245MAX_AGE = 3600 * 10 
     
    4447_player_ = None 
    4548 
     49 
     50 
     51class PodcastException: pass 
    4652 
    4753 
     
    6672    | ] 
    6773    | 
    68     | VPODCAST_DIR = '/home/user_name/VPODCAST' 
    69     """ 
     74    | VPODCAST_DIR = '/path/to/vpodcasts' 
     75    """ 
     76    @benchmark(benchmarking) 
    7077    def __init__(self): 
    7178        """ Initialise the Video postcast plug-in interface """ 
     79        if config.VPODCAST_DIR is None: 
     80            self.reason = 'VPODCAST_DIR not set' 
     81            return 
    7282        plugin.MainMenuPlugin.__init__(self) 
    7383        self.plugin_name = 'vpodcast' 
     
    7585 
    7686 
     87    @benchmark(benchmarking) 
    7788    def config(self): 
    78         """ freevo plugins -i audio.vpodcast returns the info """ 
     89        """ freevo plugins -i video.vpodcast returns the info """ 
    7990        return [ 
    8091            ('VPODCAST_LOCATIONS', None, 'List of podcast locations'), 
    81             ('VPODCAST_DIR', None, 'Directory for downloaded podcasts') 
     92            ('VPODCAST_DIR', os.path.join(config.FREEVO_CACHEDIR, 'vposcasts'), 'Directory for downloaded podcasts'), 
     93            ('YOUTUBE_USERNAME', None, 'YouTube user name (optional)'), 
     94            ('YOUTUBE_PASSWORD', None, 'YouTube password (optional)'), 
     95            ('YOUTUBE_FORMAT', '18', 'YouTube format 18=high 17=mobile (optional)'), 
     96            ('VPODCAST_BUFFERING_TIME', 20, 'Length of time to wait while buffering the poscast'), 
     97            ('VPODCAST_BUFFERING_SIZE', 20*1024, 'size of downloaded file to start playing'), 
    8298        ] 
    8399 
    84100 
     101    @benchmark(benchmarking) 
    85102    def items(self, parent): 
    86103        return [ VPodcastMainMenuItem(parent) ] 
    87104 
    88105 
     106    @benchmark(benchmarking) 
    89107    def check_dir(self): 
    90108        """ Check that the VPODCAST_DIR directories exist, if not create them """ 
     
    94112 
    95113        for pcdir in config.VPODCAST_LOCATIONS: 
    96             pc_dir = config.VPODCAST_DIR + '/' + pcdir[0] 
    97             if not os.path.isdir(pc_dir): 
    98                 os.makedirs(pc_dir) 
    99  
    100  
    101 class VPVideoItem(VideoItem): 
    102     """ 
    103     Video podcast video item 
    104     """ 
    105     def __init__(self, name, url, parent): 
    106         """ Initialise the VPVideoItem class """ 
    107         self.vp_url = url 
    108         url = name 
    109         VideoItem.__init__(self, name, parent) 
    110  
    111  
    112     def play(self, arg=None, menuw=None): 
    113         """ Play this Podcast""" 
    114  
    115         # play the item. 
    116         isYT = self.vp_url.find('youtube.com')  #YouTube podcast 
    117         isMC = self.vp_url.find('metacafe.com') #Metacafe podcast 
    118  
    119         if isYT != -1: 
    120             self.download_url = self.youtube(self.vp_url) 
    121  
    122         elif isMC != -1: 
    123             self.download_url = self.metacafe(self.vp_url) 
    124  
    125         else: 
    126             self.download_url = self.vp_url 
    127  
    128         if not os.path.exists(self.filename): 
    129             background = BGDownload(self.download_url, self.filename) 
    130             background.start() 
    131             popup = PopupBox(text=_('Buffering podcast...')) 
    132             popup.show() 
    133             time.sleep(20) # 20s. buffering time 
    134             popup.destroy() 
    135  
    136         # call the play funuction of VideoItem 
    137         VideoItem.play(self, menuw=menuw, arg=arg) 
    138  
    139  
    140     def youtube(self, url): 
    141         const_video_url_str = 'http://www.youtube.com/watch?v=%s' 
    142         const_video_url_re = re.compile( 
    143         r'^((?:http://)?(?:\w+\.)?youtube\.com/(?:v/|(?:watch(?:\.php)?)?\?(?:.+&)?v=))?([0-9A-Za-z_-]+)(?(1)[&/].*)?$') 
    144         const_url_t_param_re = re.compile(r', "t": "([^"]+)"') 
    145         const_video_url_real_str = 'http://www.youtube.com/get_video?video_id=%s&t=%s' 
    146         const_video_title_re = re.compile(r'<title>YouTube - ([^<]*)</title>', re.M | re.I) 
    147         try: 
    148             video_url_mo = const_video_url_re.match(url) 
    149             video_url_id = video_url_mo.group(2) 
    150             video_url = const_video_url_str % video_url_id 
    151             # Retrieve video webpage 
    152             video_webpage = urllib.urlopen(video_url).read() 
    153             match = const_url_t_param_re.search(video_webpage) 
    154             if match is None: 
    155                 _debug_('No matches found for youtube', DWARNING) 
    156             video_url_t_param = match.group(1) 
    157             # Retrieve real video URL 
    158             video_url_real = const_video_url_real_str % (video_url_id, video_url_t_param) 
    159             return video_url_real 
    160         except Exception, why: 
    161             _debug_('Cannot read youtube URL: %s' (why,), DWARNING) 
    162  
    163  
    164     def metacafe(self, url): 
    165         video_url =  url 
    166         const_video_url_re = re.compile(r'(?:http://)?(?:www\.)?metacafe\.com/watch/([^/]+)/([^/]+/)?.*') 
    167         const_normalized_url_str = 'http://www.metacafe.com/watch/%s/' 
    168         const_age_post_data = r'allowAdultContent=1&submit=Continue+-+I%27m+over+18' 
    169         const_video_mediaurl_re = re.compile(r'&mediaURL=([^&]+)&', re.M) 
    170  
    171         try: 
    172             # Verify video URL format and extract URL data to normalize URL 
    173             video_url_mo = const_video_url_re.match(video_url) 
    174             if video_url_mo is None: 
    175                 _debug_('No matches found for metacafe', DWARNING) 
    176             video_url_id = video_url_mo.group(1) 
    177             video_url_title = (video_url_mo.group(2) is not None) and video_url_mo.group(2)[:-1] or None 
    178             video_url = const_normalized_url_str % video_url_id 
    179  
    180             # Retrieve video webpage 
    181             video_webpage = urllib.urlopen(video_url).read() 
    182             # Retrieve real video URL 
    183             video_url_real = self.extract_step('Extracting real video URL', 'unable to extract real video URL', \ 
    184                 const_video_mediaurl_re, video_webpage) 
    185             return video_url_real 
    186         except Exception, why: 
    187             _debug_('Cannot read metacafe URL: %s' (why,), DWARNING) 
    188  
    189  
    190     def extract_step(self, step_title, step_error, regexp, data): 
    191         try: 
    192             match = regexp.search(data) 
    193             if match is None: 
    194                 error_advice_exit(step_error) 
    195  
    196             extracted_data = match.group(1) 
    197  
    198             return extracted_data 
    199  
    200         except KeyboardInterrupt: 
    201             sys.exit('\n') 
     114            podcastdir = os.path.join(config.VPODCAST_DIR, pcdir[0].strip().replace(' ', '_').lower()) 
     115            if not os.path.isdir(podcastdir): 
     116                os.makedirs(podcastdir) 
    202117 
    203118 
     
    208123    of commands in a submenu. 
    209124    """ 
     125    @benchmark(benchmarking) 
    210126    def __init__(self, parent): 
    211127        MenuItem.__init__(self, parent, arg='audio', skin_type='radio') 
     
    213129 
    214130 
     131    @benchmark(benchmarking) 
    215132    def actions(self): 
    216133        """ return a list of actions for this item """ 
     
    218135 
    219136 
     137    @benchmark(benchmarking) 
    220138    def create_podcast_submenu(self, arg=None, menuw=None, image=None): 
    221139        """ create the sub-menu for the podcast """ 
     140        dir, url = arg 
     141        podcastdir = dir.strip().replace(' ', '_').lower() 
     142 
    222143        popup = PopupBox(text=_('Fetching podcast...')) 
    223144        popup.show() 
    224         url = arg[1] 
    225         p = podcast() 
    226         p.open_rss(url) 
    227         p.rss_title() 
    228         p.rss_count() 
    229  
    230         podcast_items = [] 
    231         for pc_location in range(p.rss.count): 
    232             p.rss_item(pc_location) 
    233             if p.image != None: 
    234                 image = config.VPODCAST_DIR + '/' + arg[0] + '/' + p.title + '.jpg' 
    235                 self.download(p.image, image) 
     145        try: 
     146            p = Podcast(url) 
     147            feed = p.feed() 
     148            rss_title = p.rss_title() 
     149            rss_description = p.rss_description() 
     150            rss_imageurl = p.rss_image() 
     151            if rss_imageurl: 
     152                filename = rss_title.strip().replace(' ', '_').lower() 
     153                image = os.path.join(config.VPODCAST_DIR, podcastdir, filename+'.jpg') 
     154                self.download(rss_imageurl, image) 
    236155            else: 
    237156                image = None 
    238             url = p.link 
    239             name = p.title 
    240             if url != 'ERROR': 
    241                 isYT = url.find('youtube.com') 
    242                 isMC = url.find('metacafe.com') 
    243                 if isYT == -1 and isMC == -1: 
    244                     file_ext = '.avi' 
    245                 else: 
    246                     file_ext = '.flv' 
    247  
    248                 filename  = config.VPODCAST_DIR + '/' + arg[0] + '/' + name + file_ext 
    249                 podcast_items += [menu.MenuItem(_(p.title), \ 
    250                     action=VPVideoItem(filename, url, self), arg=None, image=image)] 
    251  
    252         popup.destroy() 
    253         if (len(podcast_items) == 0): 
    254             podcast_items += [menu.MenuItem(_('No Podcast locations found'), 
    255                                              menwu.goto_prev_page, 0)] 
     157 
     158            podcast_items = [] 
     159            for item in feed.entries: 
     160                #pprint(item) #DJW 
     161                try: 
     162                    item_title = p.rss_item_title(item) 
     163                    item_image = p.rss_item_image(item) 
     164                    item_link = p.rss_item_link(item) 
     165                    isYT = item_link.find('youtube.com') 
     166                    isMC = item_link.find('metacafe.com') 
     167                    name = item_title.strip().replace(' ', '_').lower() 
     168                    file_ext = isYT >= 0 and config.YOUTUBE_FORMAT == '18' and '.mp4' \ 
     169                            or isYT >= 0 and config.YOUTUBE_FORMAT == '17' and '.3gp' \ 
     170                            or isMC >= 0 and '.flv' \ 
     171                            or '.avi' 
     172                    filename = os.path.join(config.VPODCAST_DIR, podcastdir, name+file_ext) 
     173                    podcast_items += [ menu.MenuItem(item_title, action=VPVideoItem(filename, item_link, self), 
     174                        arg=None, image=image) ] 
     175                except PodcastException: 
     176                    pass 
     177            if not podcast_items: 
     178                podcast_items += [menu.MenuItem(_('No Podcast locations found'), menuw.goto_prev_page, 0)] 
     179        finally: 
     180            popup.destroy() 
     181 
    256182        podcast_sub_menu = menu.Menu(_('Video Podcasts'), podcast_items) 
    257         rc.app(None) 
    258183        menuw.pushmenu(podcast_sub_menu) 
    259184        menuw.refresh() 
    260  
    261  
     185        rc.app(None) 
     186 
     187 
     188    @benchmark(benchmarking) 
    262189    def create_podcast_menu(self, arg=None, menuw=None): 
    263190        """ Create the main menu item for the video podcasts """ 
    264         popup = PopupBox(text=_('Fetching podcasts...')) 
     191        popup = PopupBox(text=_('Fetching podcast list...')) 
    265192        popup.show() 
    266193        podcast_menu_items = [] 
    267194 
    268         for location in config.VPODCAST_LOCATIONS: 
    269             url = location[1] 
    270             image_path = config.VPODCAST_DIR + '/' + location[0] + '/' + 'cover.jpg' 
    271             if self.check_logo(image_path): 
    272                 p = podcast() 
    273                 p.open_rss(url) 
    274                 p.rss_title() 
    275                 name = p.rss_title 
    276                 try: 
    277                     image_url = p.rss_image 
    278                     self.download(image_url, image_path) 
    279                 except: 
    280                     _debug_('No image in RSS', DINFO) 
    281  
    282             if (len(config.VPODCAST_DIR) == 0): 
    283                 podcast_items += [menu.MenuItem(_('Set VPODCAST_DIR in local_conf.py'), menwu.goto_prev_page, 0)] 
    284  
    285             podcast_menu_items += [menu.MenuItem(_(location[0]), action=self.create_podcast_submenu, \ 
    286                 arg=location, image=image_path)] 
     195        for dir, url in config.VPODCAST_LOCATIONS: 
     196            podcastdir = dir.strip().replace(' ', '_').lower() 
     197            image_path = os.path.join(config.VPODCAST_DIR, podcastdir, 'cover.jpg') 
     198            #XXX here we are only downloading the images, but this slows down the plug-in lots 
     199            #if self.check_logo(image_path): 
     200            #    p = Podcast() 
     201            #    p.open_rss(url) 
     202            #    p.rss_title() 
     203            #    #XXX name = p.rss_title 
     204            #    if p.rss_imageurl: 
     205            #        try: 
     206            #            image_url = p.rss_imageurl 
     207            #            self.download(image_url, image_path) 
     208            #        except Exception, why: 
     209            #            _debug_(why, DWARNING) 
     210 
     211            podcast_menu_items += [ menu.MenuItem(dir, action=self.create_podcast_submenu, 
     212                arg=(dir, url), image=image_path) ] 
    287213 
    288214        popup.destroy() 
     
    293219 
    294220 
     221    @benchmark(benchmarking) 
    295222    def download(self, url, savefile): 
    296223        """ Download the url and save it """ 
     
    301228 
    302229 
     230    @benchmark(benchmarking) 
    303231    def check_logo(self, logo_file): 
    304         """ Check if the logo has changed """ 
    305         if os.path.exists(logo_file) == 0 or (abs(time.time() - os.path.getmtime(logo_file)) > MAX_AGE): 
    306             return True 
    307         else: 
    308             return False 
    309  
    310  
    311  
    312 class podcast: 
    313     """ 
    314     """ 
    315     def __init__(self): 
    316         pass 
    317  
    318  
    319     def open_rss(self, url): 
    320         self.rss = util.feedparser.parse(url) 
    321         self.encoding = self.rss.encoding 
    322  
    323  
     232        """ 
     233        Check if the logo has changed 
     234        @returns: True if the logo does not exist or is too old 
     235        """ 
     236        return not os.path.exists(logo_file) or (time.time() - os.path.getmtime(logo_file)) > MAX_AGE 
     237 
     238 
     239 
     240class Podcast: 
     241    """ 
     242    This bit of code really needs fixing, it's really bad 
     243    """ 
     244    @benchmark(benchmarking) 
     245    def __init__(self, url): 
     246        self.url = url 
     247        self.encoding = 'latin-1' 
     248 
     249 
     250    @benchmark(benchmarking) 
     251    def feed(self): 
     252        try: 
     253            self.rss = util.feedparser.parse(self.url) 
     254            self.encoding = self.rss.encoding 
     255            return self.rss 
     256        except Exception, why: 
     257            _debug_('Cannot parse feed "%s": %s' % (self.url, why), DWARNING) 
     258            return None 
     259 
     260 
     261    @benchmark(benchmarking) 
    324262    def rss_title(self): 
     263        if self.rss.feed.has_key('title'): 
     264            return self.rss.feed.title.encode(self.encoding) 
     265        return None 
     266 
     267 
     268    @benchmark(benchmarking) 
     269    def rss_description(self): 
     270        if self.rss.feed.has_key('description'): 
     271            return self.rss.feed.description.encode(self.encoding) 
     272        return None 
     273 
     274 
     275    @benchmark(benchmarking) 
     276    def rss_image(self): 
     277        if self.rss.feed.has_key('image') and self.rss.feed.image.has_key('url'): 
     278            return self.rss.feed.image.url 
     279        return None 
     280 
     281 
     282    @benchmark(benchmarking) 
     283    def rss_item_title(self, item): 
     284        """ get the item's title """ 
     285        self.title = item.title.encode(self.encoding) 
     286        self.title = re.sub('(/)', '_', self.title) 
     287        return self.title 
     288 
     289 
     290    @benchmark(benchmarking) 
     291    def rss_item_link(self, item): 
     292        """ get the item's link """ 
     293        self.link = item.link.encode(self.encoding) 
     294        return self.link 
     295 
     296 
     297    @benchmark(benchmarking) 
     298    def rss_item_image(self, item): 
     299        """ get the item's image """ 
     300        # Search for image 
     301        #img_pattern = '<img src="(.*?)" align=' 
     302        img_pattern = 'img src="(.*?)"' 
    325303        try: 
    326             self.rss_title = self.rss.feed.title.encode(self.encoding) 
    327  
    328             #self.rss_date = self.rss.feed.date 
    329         except: 
    330             _debug_('No title in rss feed', DINFO) 
    331             self.rss_title = None 
    332  
    333         try: 
    334             self.rss_description = self.rss.feed.description.encode(self.encoding) 
    335         except: 
    336             _debug_('No description in rss feed', DINFO) 
    337  
    338         try: 
    339             self.rss_image = self.rss.feed.image.url 
    340         except: 
    341             self.rss_image = None 
    342  
    343  
    344     def rss_count(self): 
    345         self.rss.count =  len(self.rss.entries) 
    346  
    347  
    348     def rss_item(self, item=0): 
    349  
    350         try: 
    351             self.title = self.rss.entries[item].title.encode(self.encoding) 
    352             self.title = re.sub('(/)','_',self.title) 
    353             description_all = self.rss.entries[item].description 
    354             self.link = self.rss.entries[item].link 
    355             # Search for image 
    356             #img_pattern = '<img src="(.*?)" align=' 
    357             img_pattern = 'img src="(.*?)"' 
    358             try: 
    359                 self.image = re.search(img_pattern, description_all).group(1) 
    360             except: 
    361                 self.image = None 
    362  
    363         except: 
    364             pass 
    365  
    366         if self.link == None: 
    367             self.link = 'ERROR' 
     304            self.image = re.search(img_pattern, item.description).group(1) 
     305        except Exception, why: 
     306            self.image = None 
     307        return self.image 
     308 
     309 
     310 
     311class VPVideoItem(VideoItem): 
     312    """ 
     313    Video podcast video item 
     314    """ 
     315    @benchmark(benchmarking) 
     316    def __init__(self, name, url, parent): 
     317        """ Initialise the VPVideoItem class """ 
     318        _debug_('VPVideoItem.__init__(name=%r, url=%r, parent=%r)' % (name, url, parent), 2) 
     319        VideoItem.__init__(self, name, parent) 
     320        self.vp_url = url 
     321 
     322 
     323    @benchmark(benchmarking) 
     324    def play(self, arg=None, menuw=None): 
     325        """ 
     326        Play this Podcast 
     327        """ 
     328        self.download_url = self.vp_url 
     329        if not os.path.exists(self.filename): 
     330            background = BGDownload(self.download_url, self.filename) 
     331            background.start() 
     332            popup = PopupBox(text=_('Buffering podcast...')) 
     333            popup.show() 
     334            size = 0 
     335            for i in range(int(config.VPODCAST_BUFFERING_TIME)): 
     336                if os.path.exists(self.filename): 
     337                    mode, ino, dev, nlink, uid, gui, size, atime, mtime, ctime = os.stat(self.filename) 
     338                    if size > config.VPODCAST_BUFFERING_SIZE: 
     339                        break 
     340                time.sleep(0.5) 
     341            else: 
     342                if size < 120 * 1024: # a bit arbitary, needs to be bigger than a web page 
     343                    popup.destroy() 
     344                    AlertBox(text=_('Cannot download %s') % self.filename).show() 
     345                    return 
     346            popup.destroy() 
     347 
     348        # call the play funuction of VideoItem 
     349        VideoItem.play(self, menuw=menuw, arg=arg) 
    368350 
    369351 
     
    373355    Download file in background 
    374356    """ 
     357    @benchmark(benchmarking) 
    375358    def __init__(self, url, savefile): 
    376359        threading.Thread.__init__(self) 
    377360        self.url = url 
    378361        self.savefile = savefile 
    379  
    380  
     362        self.youtube_ie = youtube.YoutubeIE() 
     363        self.metacafe_ie = youtube.MetacafeIE(self.youtube_ie) 
     364        self.youtube_pl_ie = youtube.YoutubePlaylistIE(self.youtube_ie) 
     365 
     366 
     367    @benchmark(benchmarking) 
    381368    def run(self): 
    382369        try: 
    383             file = urllib2.urlopen(self.url) 
    384             info = file.info() 
    385             save = open(self.savefile, 'wb') 
    386             chunkSize = 25 
    387             totalBytes = int(info['Content-Length']) 
    388             downloadBytes = 0 
    389             bytesLeft = totalBytes 
    390             while bytesLeft > 0: 
    391                 chunk = file.read(chunkSize) 
    392                 readBytes = len(chunk) 
    393                 downloadBytes += readBytes 
    394                 bytesLeft -= readBytes 
    395                 save.write(chunk) 
    396         except Exception, why: 
    397             _debug_('Cannot download "%s": %s' % (self.url, why), DWARNING) 
     370            fd = youtube.FileDownloader({ 
     371                'usenetrc': False, 
     372                'username': config.YOUTUBE_USERNAME, 
     373                'password': config.YOUTUBE_PASSWORD, 
     374                'quiet': True, 
     375                'forceurl': False, 
     376                'forcetitle': False, 
     377                'simulate': False, 
     378                'format': config.YOUTUBE_FORMAT, 
     379                #'outtmpl': u'%(stitle)s-%(id)s.%(ext)s', 
     380                'outtmpl': self.savefile, 
     381                'ignoreerrors': False, 
     382                'ratelimit': None, 
     383                }) 
     384            fd.add_info_extractor(self.youtube_pl_ie) 
     385            fd.add_info_extractor(self.metacafe_ie) 
     386            fd.add_info_extractor(self.youtube_ie) 
     387            retcode = fd.download([self.url]) 
     388            _debug_('youtube download "%s": %s' % (self.url, retcode), DINFO) 
     389        except youtube.DownloadError: 
     390            # Not sure about this code 
     391            try: 
     392                file = urllib2.urlopen(self.url) 
     393                info = file.info() 
     394                save = open(self.savefile, 'wb') 
     395                chunkSize = 25 
     396                totalBytes = int(info['Content-Length']) 
     397                downloadBytes = 0 
     398                bytesLeft = totalBytes 
     399                while bytesLeft > 0: 
     400                    chunk = file.read(chunkSize) 
     401                    readBytes = len(chunk) 
     402                    downloadBytes += readBytes 
     403                    bytesLeft -= readBytes 
     404                    save.write(chunk) 
     405            except Exception, why: 
     406                _debug_('Cannot download "%s": %s' % (self.url, why), DWARNING)