MediaManager.py :  » RSS » PenguinTV » PenguinTV-4.1.0 » penguintv » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » RSS » PenguinTV 
PenguinTV » PenguinTV 4.1.0 » penguintv » MediaManager.py
# Written by Owen Williams
# see LICENSE for license information

import ptvDB
from types import *
import ThreadPool
import time
import os,os.path
import copy
import logging
import shutil
import re

import Downloader
#import BTDownloader  loaded when needed
#import HTTPDownloader  loaded when needed

from utils import format_size

import utils

if utils.RUNNING_HILDON:
  HAS_GNOME = False
else:
  try:
    import gnome
    HAS_GNOME = True
  except:
    HAS_GNOME = False

from penguintv import DOWNLOAD_ERROR,DOWNLOAD_PROGRESS,DOWNLOAD_WARNING,DOWNLOAD_QUEUED
from Downloader import QUEUED,DOWNLOADING,FINISHED,FINISHED_AND_PLAY,STOPPED,FAILURE,PAUSED

RUNNING = 0
PAUSING = 1 
PAUSED  = 2

#Downloader API:
#constructor takes:  media, params, resume, queue, progress_callback, finished_callback
#  media:  the media dic
#  params: optional params, like for btdownloader
#  resume: are we supposed to resume?
#  queue:  are we supposed queue for playback when download is finished?  this variable is just passed around
#  progress_callback:  function to call for progress update.
#          arg of this is: (media, progress as 0 < x < 1, and text formatted message of progress)
#          the callback will return 1 if we should cancel download
#  finished_callback:  function to call when finished.
#          args is: (media, status, message)
#              where status is the enum above

class MediaManager:
  def __init__(self, app, media_dir, progress_callback=None, finished_callback=None):
    self.index=0
    #should this be lucene compatible?
    if utils.RUNNING_HILDON:
      max_downloads = 1
    else:
      max_downloads = 5
    self.pool = ThreadPool.ThreadPool(max_downloads, "MediaManager")
    self.downloads = []
    self.db = ptvDB.ptvDB()
    self.time_appendix=0
    self.bt_settings = {'min_port':6881, 'max_port':6999, 'ul_limit':0}
    self.id_time=0
    self.quitting = False
    self._net_connected = True
    self.pause_state = RUNNING
    if finished_callback:
      self.app_callback_finished = finished_callback
    else:
      self.app_callback_finished = self._basic_finished_callback
      
    if progress_callback:
      self.app_callback_progress = progress_callback
    else:
      self.app_callback_progress = self._basic_progress_callback  
    home=self.db.home
    
    if media_dir[0] == '~':
      media_dir = os.getenv('HOME') + media_dir[1:]
    
    try:
      os.stat(media_dir)
    except:
      try:
        os.mkdir(media_dir)
      except:
        raise NoDir, "error creating " +media_dir
    self._media_dir = media_dir
    
    app.connect('online-status-changed', self.__online_status_changed)
  
  def finish(self):
    self.quitting = True
    try:
      self.pool.joinAll()
      del self.pool
    except:
      pass
    
  def __del__(self):
    self.finish()
    
  def set_media_dir(self, new_dir):
    """sets new media dir.  returns None, None on success, and returns new dir name
    if db and player need to be remapped to new dirs"""
    old_dir = self._media_dir
    if new_dir == old_dir:
      return None, None
    std_loc = os.path.join(utils.get_home(), 'media')
      
    #stat new folder
    if not os.access(new_dir, os.F_OK & os.R_OK & os.W_OK & os.X_OK):
      raise NoDir, "insufficient permissions to access %s" % new_dir
      
    try:
      os.symlink
      HAVE_SYMLINK = True
    except:
      HAVE_SYMLINK = False  
      
    if HAVE_SYMLINK:
      if old_dir == std_loc:
        self._move_contents(std_loc, new_dir)
        self._media_dir = new_dir
        if os.path.islink(std_loc):
          os.remove(std_loc)
          os.symlink(new_dir, std_loc)
        else:
          os.rmdir(std_loc)
          os.symlink(new_dir, std_loc)
          return old_dir, std_loc
      elif new_dir == std_loc:
        self._media_dir = std_loc
        if os.path.islink(std_loc):
          os.remove(std_loc)
          self._move_contents(old_dir, std_loc)
        else:
          os.rmdir(std_loc)
          os.mkdir(std_loc)
          self._move_contents(old_dir, std_loc)
          return old_dir, std_loc
      else:
        self._move_contents(old_dir, new_dir)
        self._media_dir = new_dir
        if os.path.islink(std_loc):
          os.remove(std_loc)
          os.symlink(new_dir, std_loc)
        else:
          os.rmdir(std_loc)
          os.symlink(new_dir, std_loc)
          return old_dir, std_loc
    else:
      self._move_contents(old_dir, new_dir)
      self._media_dir = new_dir
      return old_dir, new_dir
        
    return None, None
      
  def _move_contents(self, src, dst):
    p = re.compile("\d{4}-\d{2}-\d{2}$")
    for f in os.listdir(src):
      if p.search(f) is not None or f.upper().endswith('M3U'):
        shutil.move(os.path.join(src, f), os.path.join(dst, f))
      
  def get_media_dir(self):
    return self._media_dir
    
  def __online_status_changed(self, app, connected):
    if not connected:
      app.pause_downloads()
    else:
      if not self._net_connected:
        self.unpause_downloads()
        app.resume_resumable()

    self._net_connected = connected
    
  def set_bt_settings(self, bt_settings):
    self.bt_settings = bt_settings
    
  def get_id(self):
    cur_time = int(time.time())
    
    if self.id_time == cur_time:
      self.time_appendix = self.time_appendix+1
    else:
      self.id_time = cur_time
      self.time_appendix=0
    
    return str(self.id_time)+"+"+str(self.time_appendix)
    
  def show_downloads(self):
    url = "file://"+self._media_dir+"/"+utils.get_dated_dir()
    if HAS_GNOME:
      gnome.url_show(url)
    else:
      import webbrowser
      webbrowser.open_new_tab(url)
    
  def download_entry(self, entry_id, queue=False, resume=False):
    """queues a download
     will interact with bittorrent python
     use btlaunchmany code to write our own downloader
     just need to change init funcs, hijack status funcs, add cancelling"""
    media_list = self.db.get_entry_media(entry_id)
    if len(media_list)==0:
      return
    for media in media_list:
      self.download(media['media_id'], queue, resume)
      
  def download(self, media_id, queue=False, resume=False):
    """queues a download"""
    for downloader in self.downloads:
      if downloader.media['media_id'] == media_id:
        self.downloads.remove(downloader)
        break
    
    media = self.db.get_media(media_id)
    media['downloader_index']=self.index
    media['download_status']=1
    media.setdefault('size',0)      
    if media['file'] is None:
      filename = os.path.basename(media['url'])
      filen, ext = os.path.splitext(filename)
      ext = ext.split('?')[0] #grrr lugradio...
      media['file']=os.path.join(self._media_dir, utils.get_dated_dir(), filen+ext)
      dated_dir = os.path.split(os.path.split(media['file'])[0])[1]
      try: #make sure
        os.stat(os.path.join(self._media_dir, dated_dir))
      except:
        os.mkdir(os.path.join(self._media_dir, dated_dir))
      if self.db.media_exists(media['file']): #if the filename is in the db, rename
        media['file']=os.path.join(self._media_dir, utils.get_dated_dir(), filen+"-"+self.get_id()+ext)
      else:
        try:
          os.stat(media['file'])  #if this raises exception, the file doesn't exist and we're ok
          media['file']=os.path.join(self._media_dir, utils.get_dated_dir(), filen+"-"+self.get_id()+ext) #if not, get new name
        except:
          pass #we're ok
      
      if not resume:
        self.db.delete_media(media_id)
      #else:
      #  print "resuming using existing filename: "+str(media['file'])
    extension = os.path.splitext(media['url'])[1]
    
    if media['mimetype'] == 'application/x-bittorrent' or extension.upper()==".TORRENT":
      params = [
        '--minport', str(self.bt_settings['min_port']),
        '--maxport', str(self.bt_settings['max_port']),
        '--max_upload_rate', str(self.bt_settings['ul_limit'])]
        
      import BTDownloader
      downloader = BTDownloader.BTDownloader(media, self._media_dir, params,True, queue, self.callback_progress,self.callback_finished)
      self.downloads.append(downloader)
      self.pool.queueTask(downloader.download)
    else: #http regular download
      ext = os.path.splitext(media['file'])[1]
      if len(ext)>5 or len(ext)==0:
        #I think this isn't really the right extension.   See fucking ask a ninja: http://feeds.feedburner.com/AskANinja
        try:
          import mimetypes
          real_ext = mimetypes.guess_extension(media['mimetype'])
          if real_ext is not None:
            media['file']=media['file']+real_ext
        except:
          print "ERROR couldn't guess mimetype, leaving filename alone"
      import HTTPDownloader
      downloader = HTTPDownloader.HTTPDownloader(media, self._media_dir, None, resume, queue, self.callback_progress, self.callback_finished)
      self.downloads.append(downloader)
      self.pool.queueTask(downloader.download)
      
    #self.db.set_media_download_status(media['media_id'],1)
    #self.db.set_media_filename(media['media_id'],media['file'])
    self.db.set_media(media['media_id'], status=1, filename=media['file'])
    self.index=self.index+1
    
  def has_downloader(self, media_id):
    for download in self.downloads:
      if download.media['media_id'] == media_id:
        return True
    return False
    
  def get_downloader(self, media_id):
    for download in self.downloads:
      if download.media['media_id'] == media_id:
        return download
    raise DownloadNotFound, media_id
    
  def get_download_list(self, status=None):
    list = []
    
    if status is not None:
      list = [d for d in self.downloads if d.status == status]
    else:
      list = copy.copy(self.downloads)
    return list
    
  def _basic_finished_callback(self, data):
    print data
    self.db.set_media_download_status(data[0]['media_id'],ptvDB.D_DOWNLOADED)  
      
  def _basic_progress_callback(self, data):
    print os.path.split(data[0]['file'])[1]+" "+data[2]
    
  def callback_progress(self, obj):
    #print "mediamanager progress"
    return self.app_callback_progress(obj)
  
  def callback_finished(self, obj):
    if obj.status in [STOPPED, FINISHED, FINISHED_AND_PLAY, FAILURE]:
      try:
        self.downloads.remove(obj)
      except:
        print "Warning: couldn't remove download", obj.media
    self.update_playlist(obj.media)
    #if self.pause_state == RUNNING:
    self.app_callback_finished(obj)
      
  def get_download_count(self):
    try:
      #return self.pool.getTaskCount()
      return len(self.downloads)
    except:
      return 0
    
  def stop_all_downloads(self):
    #try:
    if self.pause_state == RUNNING:
      for download in self.downloads:
        download.stop()
        #if not download.status == Downloader.DOWNLOADING: #send signal for all queued downloads
        #  self.finished_callback(download, (download.media,MediaManager.STOPPED,None)) 
      try:
        self.pool.joinAll(False,True) #don't wait for tasks, but let the threads die naturally
      except AttributeError:
        logging.warning("no pool to delete, no problem")
      #reset
      self.downloads = []
      self.pause_state = PAUSED
    #except:
    #  pass

  def pause_all_downloads(self):
    if self.pause_state == RUNNING:
      for download in self.downloads:
        download.pause()
      self.pool.joinAll(False,True) #don't wait for tasks, but let the threads die naturally
      self.pause_state = PAUSED
    
  def unpause_downloads(self):
    """DOES NOT requeue downloads.  Just clears the state"""
    self.pause_state = RUNNING
    
  def stop_download(self, media_id):
    if self.has_downloader(media_id):
      downloader = self.get_downloader(media_id)
      if downloader.status == QUEUED:
        #if it's queued, we can stop it directly
        #the threadpool will still hold on to the object, but 
        #when it tries to run it will see that it has been stopped
        downloader.stop()
        self.update_playlist(downloader.media)
        self.app_callback_finished(downloader)
        self.downloads.remove(downloader)
      else:
        downloader.stop()
      
  def get_disk_usage(self):
    size = 0
    if utils.RUNNING_HILDON:
      #this is much faster on maemo, which sucks at mmc disk access
      import subprocess
      if not os.path.isdir(self._media_dir):
        return 0

      cmd = "du -sk %s" % self._media_dir
      p = subprocess.Popen(cmd, shell=True, close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
      retval = p.wait()
      stderr = p.stderr.read()
      if len(stderr) > 1 or retval != 0:
        return 0
      retval = p.stdout.read().split('\t')[0]
      size = long(retval)*1024L
    else:
      try:
        #filelist = glob.glob(self._media_dir+"/*")
        for f in utils.GlobDirectoryWalker(self._media_dir):
          size = size+os.stat(f)[6]
      except:
        pass
    return size
    
  def generate_playlist(self):
    if utils.RUNNING_SUGAR:
      return
    import glob
    dated_dir = utils.get_dated_dir()
    try:
      os.stat(os.path.join(self._media_dir, dated_dir))
    except:
      os.mkdir(os.path.join(self._media_dir, dated_dir))
    f = open(os.path.join(self._media_dir, dated_dir, "playlist.m3u"),'w')
    f.write('#EXTM3U\n')
    
    for item in glob.glob(os.path.join(self._media_dir, dated_dir, "*")):
      filename = os.path.split(item)[1]
      if filename != "playlist.m3u":
        f.write(filename+"\n")
    f.close()
    
  def update_playlist(self, media):
    """Adds media to the playlist in its directory"""
    
    if utils.RUNNING_SUGAR:
      return
    
    try:
      os.stat(media['file'])
    except:
      return
      
    dated_dir = os.path.split(os.path.split(media['file'])[0])[1]
      
    try:
      os.stat(os.path.join(self._media_dir, dated_dir, "playlist.m3u"))
      f = open(os.path.join(self._media_dir, dated_dir, "playlist.m3u"),'a')
    except:
      f = open(os.path.join(self._media_dir, dated_dir, "playlist.m3u"),'w')
      f.write('#EXTM3U\n')
    
    f.write(os.path.split(media['file'])[1]+"\n")
    f.close()

class NoDir(Exception):
  def __init__(self,durr):
    self.durr = durr
  def __str__(self):
    return "no such directory: "+self.durr
    
class DownloadNotFound(Exception):
  def __init__(self,durr):
    self.durr = durr
  def __str__(self):
    return "download not found: "+str(self.durr)
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.