home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2009 June / maximum-cd-2009-06.iso / DiscContents / digsby_setup.exe / lib / plugins / nowplaying / winamp.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-02-26  |  11.4 KB  |  411 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.5)
  3.  
  4. __all__ = [
  5.     'WinampSongChecker']
  6. import sys
  7. from util.ffi import cimport, Struct
  8. from ctypes.wintypes import DWORD, MAX_PATH
  9. from ctypes import byref, create_string_buffer, create_unicode_buffer, WinError, c_int, c_char_p, addressof, c_char, c_void_p
  10. from path import path
  11. from traceback import print_exc
  12. from nowplaying import SongChecker, register
  13. import logging
  14. log = logging.getLogger('nowplaying.winamp')
  15.  
  16. class WinampSongChecker(SongChecker):
  17.     PROCESS_NAME = 'winamp.exe'
  18.     app_name = 'Winamp'
  19.     name_id = 'winamp'
  20.     CLASS_NAME = u'Winamp v1.x'
  21.     important_keys = ('title', 'artist')
  22.     
  23.     def __init__(self):
  24.         SongChecker.__init__(self)
  25.         self.hwnd = None
  26.         self.hProcess = None
  27.         self.WinampData = None
  28.         self.methods = [
  29.             self._currentSongFromMetadata,
  30.             self._currentSongFromPlaylistTitle,
  31.             self._currentSongFromAppTitle]
  32.  
  33.     
  34.     def running(self, processes = None):
  35.         
  36.         try:
  37.             self.get_instance()
  38.         except:
  39.             self.release()
  40.             return False
  41.  
  42.         return True
  43.  
  44.     
  45.     def WASend(self, *a):
  46.         return SendMessageW(self.hwnd, WM_WA_IPC, *a)
  47.  
  48.     
  49.     def WAWrite(self, what, towhere):
  50.         if type(what) is str:
  51.             buf = create_string_buffer(what)
  52.         else:
  53.             buf = what
  54.         outsize = c_int()
  55.         success = WriteProcessMemory(self.hProcess, towhere._ptr, addressof(buf), len(buf), byref(outsize))
  56.         if not success:
  57.             raise WinError()
  58.         
  59.         if len(buf) != outsize.value:
  60.             raise Exception("Didn't write enough data (wrote %d, should have written %d)", len(buf), outsize.value)
  61.         
  62.  
  63.     
  64.     def WARead(self, address, bufsize = 512):
  65.         if not self.hProcess:
  66.             return None
  67.         
  68.         string = create_string_buffer(bufsize)
  69.         if not ReadProcessMemory(self.hProcess, address, byref(string), bufsize, 0):
  70.             raise WinError()
  71.         
  72.         return string.value
  73.  
  74.     
  75.     def get_metadata(self, filename, metaname):
  76.         if not self.hProcess:
  77.             return None
  78.         
  79.         metaname = metaname.upper()
  80.         WinampData = self.WinampData
  81.         WAWrite = self.WAWrite
  82.         WASend = self.WASend
  83.         WARead = self.WARead
  84.         efiRemote = None
  85.         fileNameRemote = None
  86.         fieldRemote = None
  87.         resultRemote = None
  88.         efiRemote = WinampData(extendedFileInfoStruct())
  89.         fileNameRemote = WinampData(len(filename) + 1)
  90.         fieldRemote = WinampData(len(metaname) + 1)
  91.         resultRemote = WinampData(512)
  92.         WAWrite(filename, fileNameRemote)
  93.         WAWrite(metaname, fieldRemote)
  94.         WAWrite(extendedFileInfoStruct(filename = fileNameRemote._ptr, metadata = fieldRemote._ptr, ret = resultRemote._ptr, retlen = 512), efiRemote)
  95.         if not WASend(efiRemote._ptr, IPC.GET_EXTENDED_FILE_INFO):
  96.             err = WinError()
  97.             if err.winerror != 0:
  98.                 raise err
  99.             
  100.         
  101.         result = WARead(resultRemote._ptr)
  102.         return result
  103.  
  104.     
  105.     def _currentSong(self):
  106.         if not self.hProcess:
  107.             return None
  108.         
  109.         WinampData = self.WinampData
  110.         WAWrite = self.WAWrite
  111.         WASend = self.WASend
  112.         WARead = self.WARead
  113.         (filename, position) = self.get_current_filename()
  114.         info = dict(status = isplaying.get(WASend(1, IPC.ISPLAYING), 'stopped'), length = WASend(1, IPC.GETOUTPUTTIME), playlist_position = position)
  115.         extrainfo = { }
  116.         errors = { }
  117.         print_errors = False
  118.         for method in self.methods[:]:
  119.             
  120.             try:
  121.                 moreinfo = method(filename, position)
  122.                 for k, v in moreinfo.iteritems():
  123.                     if v:
  124.                         extrainfo[k] = v
  125.                         continue
  126.             except Exception:
  127.                 e = None
  128.                 errors[method] = e
  129.                 del e
  130.  
  131.             if (all,)((lambda .0: for key in .0:
  132. key in extrainfo)(self.important_keys)):
  133.                 break
  134.                 continue
  135.         else:
  136.             print_errors = True
  137.         if (sys.DEV or print_errors) and errors:
  138.             for method, error in errors.items():
  139.                 log.info('Exception calling %r: %r', method, error)
  140.             
  141.             del method
  142.             del error
  143.         
  144.         del errors
  145.         if not extrainfo:
  146.             return None
  147.         
  148.         if filename:
  149.             filepath = path(filename.decode(sys.getfilesystemencoding()))
  150.             extrainfo.update(filepath = unicode(filepath), filename = filepath.namebase)
  151.         
  152.         info.update(extrainfo)
  153.         return info
  154.  
  155.     
  156.     def _currentSongFromAppTitle(self, filename = None, position = None):
  157.         if not self.hProcess:
  158.             return { }
  159.         
  160.         
  161.         try:
  162.             title = fix_title(GetWindowText(self.hwnd)).decode('fuzzy utf8')
  163.         except WindowsError:
  164.             e = None
  165.             if e.winerror == 1400:
  166.                 log.debug('invalid window handle for winamp. releasing process handles, etc.')
  167.                 self._release()
  168.                 return { }
  169.             else:
  170.                 raise e
  171.         except:
  172.             e.winerror == 1400
  173.  
  174.         if re.match('Winamp [0-9]\\.[0-9]+', title):
  175.             return { }
  176.         else:
  177.             
  178.             try:
  179.                 (artist, title) = title.split(' - ', 1)
  180.             except ValueError:
  181.                 return { }
  182.  
  183.             return dict(artist = artist, title = title)
  184.  
  185.     
  186.     def _currentSongFromPlaylistTitle(self, filename = None, position = None):
  187.         if not filename and position:
  188.             (filename, position) = self.get_current_filename()
  189.         
  190.         
  191.         try:
  192.             playlist_title = self.WARead(self.WASend(position, IPC.GETPLAYLISTTITLE)).decode('fuzzy utf8')
  193.             if not playlist_title:
  194.                 raise Exception("Couldn't get playlist title")
  195.         except Exception:
  196.             e = None
  197.             return { }
  198.  
  199.         
  200.         try:
  201.             (artist, title) = playlist_title.split(' - ', 1)
  202.         except ValueError:
  203.             return { }
  204.  
  205.         return dict(artist = artist, title = title)
  206.  
  207.     
  208.     def _currentSongFromMetadata(self, filename = None, position = None):
  209.         if not self.hProcess:
  210.             return { }
  211.         
  212.         if not filename and position:
  213.             (filename, position) = self.get_current_filename()
  214.             if filename is None:
  215.                 return { }
  216.             
  217.         
  218.         metas = 'title artist genre album comment length year'.split()
  219.         metainfo = { }
  220.         for meta in metas:
  221.             
  222.             try:
  223.                 metainfo[meta] = self.get_metadata(filename, meta).decode('fuzzy utf8')
  224.             continue
  225.             except Exception:
  226.                 e = None
  227.                 if sys.DEV:
  228.                     print_exc()
  229.                 
  230.                 return { }
  231.                 continue
  232.             
  233.  
  234.         
  235.         return metainfo
  236.  
  237.     
  238.     def get_current_filename(self):
  239.         WASend = self.WASend
  240.         WARead = self.WARead
  241.         filename = None
  242.         position = WASend(0, IPC.GETLISTPOS)
  243.         playlist_length = WASend(0, IPC.GETLISTLENGTH)
  244.         if playlist_length:
  245.             filename = WARead(WASend(position, IPC.GETPLAYLISTFILE))
  246.         
  247.         return (filename, position)
  248.  
  249.     
  250.     def get_instance(self):
  251.         if self.hwnd and self.hProcess:
  252.             return None
  253.         
  254.         hwnd = FindWindowW(self.CLASS_NAME, None)
  255.         if not hwnd:
  256.             log.debug("couldn't get window handle for winamp")
  257.             return self.release()
  258.         
  259.         self.hwnd = hwnd
  260.         processId = DWORD()
  261.         GetWindowThreadProcessId(hwnd, byref(processId))
  262.         hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, 0, processId)
  263.         if not hProcess:
  264.             pass
  265.         hProcess = None
  266.         if not hProcess:
  267.             log.debug("couldn't get process handle for winamp")
  268.             self.release()
  269.         else:
  270.             self.hProcess = hProcess
  271.             self.WinampData = MakeIPCDatatype(hProcess)
  272.  
  273.     
  274.     def release(self):
  275.         if self.hProcess is not None:
  276.             CloseHandle(self.hProcess)
  277.             self.hwnd = None
  278.             self.hProcess = None
  279.             self.WinampData = None
  280.         
  281.  
  282.  
  283. register(WinampSongChecker)
  284. import re
  285. num_matcher = re.compile('^([0-9]+\\. )')
  286.  
  287. def fix_title(s):
  288.     idx = s.find(' - Winamp')
  289.     if idx >= 0:
  290.         s = s[:idx]
  291.     
  292.     match = num_matcher.match(s)
  293.     if match:
  294.         s = s[len(match.group()):]
  295.     
  296.     return s
  297.  
  298. gwt_buffer = create_unicode_buffer(256)
  299.  
  300. def GetWindowText(hwnd):
  301.     if not GetWindowTextW(hwnd, byref(gwt_buffer), 256):
  302.         raise WinError()
  303.     
  304.     return gwt_buffer.value
  305.  
  306. cimport(user32 = [
  307.     'SendMessageW',
  308.     'FindWindowW',
  309.     'GetWindowTextW',
  310.     'GetWindowThreadProcessId'], kernel32 = [
  311.     'ReadProcessMemory',
  312.     'WriteProcessMemory',
  313.     'VirtualAllocEx',
  314.     'VirtualFreeEx',
  315.     'OpenProcess',
  316.     'CloseHandle'])
  317.  
  318. def MakeIPCDatatype(hProcess):
  319.     
  320.     class IPCData((object,)):
  321.         HPROCESS = hProcess
  322.         
  323.         def __init__(self, sz, datatype = None):
  324.             if type(sz) is int:
  325.                 if datatype is None:
  326.                     datatype = c_char * sz()
  327.                 
  328.             else:
  329.                 datatype = sz
  330.                 sz = len(datatype)
  331.             self._sz = sz
  332.             self._ptr = VirtualAllocEx(self.HPROCESS, None, sz, MEM_COMMIT, PAGE_READWRITE)
  333.             if not self._ptr:
  334.                 print >>sys.stderr, 'the following WindowsError occurred when callingVirtualAllocEx(%s, None, %s, MEM_COMMIT, PAGE_READWRITE)' % (self.HPROCESS, sz)
  335.                 raise WinError()
  336.             
  337.             self._value = type(datatype).from_address(self._ptr)
  338.  
  339.         
  340.         def free(self):
  341.             if self._ptr is not None:
  342.                 if not VirtualFreeEx(self.HPROCESS, self._ptr, 0, MEM_RELEASE):
  343.                     raise WinError()
  344.                 
  345.                 self._ptr = None
  346.             
  347.  
  348.         __del__ = free
  349.  
  350.     return IPCData
  351.  
  352.  
  353. class extendedFileInfoStruct(Struct):
  354.     _fields_ = [
  355.         ('filename', c_char_p),
  356.         ('metadata', c_char_p),
  357.         ('ret', c_char_p),
  358.         ('retlen', c_int)]
  359.  
  360. WM_WA_IPC = 1024
  361. PROCESS_ALL_ACCESS = 0
  362. PROCESS_VM_OPERATION = 8
  363. PROCESS_VM_READ = 16
  364. PROCESS_VM_WRITE = 32
  365. MEM_COMMIT = 4096
  366. MEM_DECOMMIT = 16384
  367. MEM_RELEASE = 32768
  368. PAGE_READWRITE = 4
  369.  
  370. class IPC:
  371.     GETVERSION = 0
  372.     ISPLAYING = 104
  373.     GETOUTPUTTIME = 105
  374.     GETLISTLENGTH = 124
  375.     GETLISTPOS = 125
  376.     GETINFO = 126
  377.     GETPLAYLISTFILE = 211
  378.     GETPLAYLISTTITLE = 212
  379.     GET_EXTENDED_FILE_INFO = 290
  380.     GET_EXTENDED_FILE_INFO_HOOKABLE = 296
  381.  
  382. isplaying = {
  383.     0: 'stopped',
  384.     1: 'playing',
  385.     3: 'paused' }
  386.  
  387. def process_string_at(hProcess, address):
  388.     bufsize = 512
  389.     string = create_string_buffer('\x00' * bufsize)
  390.     if not ReadProcessMemory(hProcess, address, byref(string), bufsize, 0):
  391.         raise WinError()
  392.     
  393.     return string.value
  394.  
  395. if __name__ == '__main__':
  396.     logging.basicConfig()
  397.     from time import clock
  398.     before = clock()
  399.     checker = WinampSongChecker()
  400.     checker.get_instance()
  401.     print GetWindowText(checker.hwnd)
  402.     for i in range(1):
  403.         print 'apptitle %r' % checker._currentSongFromAppTitle()
  404.         print 'playlist %r' % checker._currentSongFromPlaylistTitle()
  405.         print 'metadata %r' % checker._currentSongFromMetadata()
  406.     
  407.     checker.release()
  408.     duration = clock() - before
  409.     print 'duration', duration
  410.  
  411.