The branch, eden has been updated
       via  38f24cb252e1fb25fa388f3c32c780a9f0452379 (commit)
       via  5e5d45e0779dd8c6a544291181651a6770d82257 (commit)
      from  78e8d79540075bca3ae43b3ddac58f54c66e6856 (commit)

- Log -----------------------------------------------------------------
http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/scripts;a=commit;h=38f24cb252e1fb25fa388f3c32c780a9f0452379

commit 38f24cb252e1fb25fa388f3c32c780a9f0452379
Author: amet <[email protected]>
Date:   Sun Oct 7 12:07:08 2012 +0400

    [script.xbmc.subtitles] -v 3.4.0
    
    - New service Pipocas.tv, thx highlandr
    - Pipocas.tv and LegendasDivx.com both with pt-pt and pt-br, thx highlandr
    - [fix] crash - check if temp folder exists before trying to create it
    - [fix] crash - create "Subs" folder using xbmcvfs.mkdir instead of mkdirs 
that is only available on Frodo

diff --git a/script.xbmc.subtitles/addon.xml b/script.xbmc.subtitles/addon.xml
index 0444068..8b9f987 100755
--- a/script.xbmc.subtitles/addon.xml
+++ b/script.xbmc.subtitles/addon.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <addon id="script.xbmc.subtitles"
        name="XBMC Subtitles"
-       version="3.3.5"
+       version="3.4.0"
        provider-name="amet, mr_blobby">
   <requires>
     <import addon="xbmc.python" version="2.0"/>
diff --git a/script.xbmc.subtitles/changelog.txt 
b/script.xbmc.subtitles/changelog.txt
index 1cd72a0..5f8abc1 100644
--- a/script.xbmc.subtitles/changelog.txt
+++ b/script.xbmc.subtitles/changelog.txt
@@ -1,3 +1,9 @@
+3.4.0
+- New service Pipocas.tv, thx highlandr
+- Pipocas.tv and LegendasDivx.com both with pt-pt and pt-br, thx highlandr
+- [fix] crash - check if temp folder exists before trying to create it
+- [fix] crash - create "Subs" folder using xbmcvfs.mkdir, instead of mkdirs 
that is only available on Frodo
+
 3.3.5
 - [fix] of a previous [fix]
 
diff --git a/script.xbmc.subtitles/resources/lib/gui.py 
b/script.xbmc.subtitles/resources/lib/gui.py
index 393a6a5..539672f 100644
--- a/script.xbmc.subtitles/resources/lib/gui.py
+++ b/script.xbmc.subtitles/resources/lib/gui.py
@@ -99,7 +99,7 @@ class GUI( xbmcgui.WindowXMLDialog ):
           self.sub_folder = dialog.browse( 0, _( 766 ), "files")
     
     if not xbmcvfs.exists(self.sub_folder):
-      xbmcvfs.mkdirs(self.sub_folder)
+      xbmcvfs.mkdir(self.sub_folder)
     
     if self.episode.lower().find("s") > -1:                                 # 
Check if season is "Special"             
       self.season = "0"                                                     #
diff --git 
a/script.xbmc.subtitles/resources/lib/services/LegendasDivx/service.py 
b/script.xbmc.subtitles/resources/lib/services/LegendasDivx/service.py
index 12a7210..8e0e4cc 100644
--- a/script.xbmc.subtitles/resources/lib/services/LegendasDivx/service.py
+++ b/script.xbmc.subtitles/resources/lib/services/LegendasDivx/service.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 
-# Service LegendasDivx.com version 0.2.4
+# Service LegendasDivx.com version 0.2.5
 # Code based on Undertext service
 # Coded by HiGhLaNdR@OLDSCHOOL
 # Help by VaRaTRoN
@@ -8,6 +8,9 @@
 # http://www.teknorage.com
 # License: GPL v2
 #
+# NEW on Service LegendasDivx.com v0.2.5:
+# Added PortugueseBrazilian. After a few requests the language is now 
available.
+#
 # NEW on Service LegendasDivx.com v0.2.4:
 # Added uuid for better file handling, no more hangups.
 #
@@ -122,6 +125,8 @@ def getallsubs(searchstring, languageshort, languagelong, 
file_original_path, su
        page = 1
        if languageshort == "pt":
                url = main_url + 
"modules.php?name=Downloads&file=jz&d_op=search_next&order=&form_cat=28&page=" 
+ str(page) + "&query=" + urllib.quote_plus(searchstring)
+       if languageshort == "pb":
+               url = main_url + 
"modules.php?name=Downloads&file=jz&d_op=search_next&order=&form_cat=29&page=" 
+ str(page) + "&query=" + urllib.quote_plus(searchstring)
 
        content = geturl(url)
        log( __name__ ,"%s Getting '%s' subs ..." % (debug_pretext, 
languageshort))
@@ -181,7 +186,10 @@ def getallsubs(searchstring, languageshort, languagelong, 
file_original_path, su
                        filename = filename + " " + "(" + movieyear + ")" + "  
" + hits + "Hits" + " - " + desc
                        subtitles_list.append({'rating': str(downloads), 
'no_files': no_files, 'filename': filename, 'desc': desc, 'sync': sync, 'hits' 
: hits, 'id': id, 'language_flag': 'flags/' + languageshort + '.gif', 
'language_name': languagelong})
                page = page + 1
-               url = main_url + 
"modules.php?name=Downloads&file=jz&d_op=search_next&order=&form_cat=28&page=" 
+ str(page) + "&query=" + urllib.quote_plus(searchstring)
+               if languageshort == "pt":
+                       url = main_url + 
"modules.php?name=Downloads&file=jz&d_op=search_next&order=&form_cat=28&page=" 
+ str(page) + "&query=" + urllib.quote_plus(searchstring)
+               if languageshort == "pb":
+                       url = main_url + 
"modules.php?name=Downloads&file=jz&d_op=search_next&order=&form_cat=29&page=" 
+ str(page) + "&query=" + urllib.quote_plus(searchstring)
                content = geturl(url)
 
        if subtitles_list != []:
@@ -268,11 +276,28 @@ def search_subtitles( file_original_path, title, tvshow, 
year, season, episode,
        elif string.lower(lang2) == "portuguese": portuguese = 2
        elif string.lower(lang3) == "portuguese": portuguese = 3
 
-       getallsubs(searchstring, "pt", "Portuguese", file_original_path, 
subtitles_list, searchstring_notclean)
-
-       if portuguese == 0:
-               msg = "Won't work, LegendasDivx is only for Portuguese 
subtitles!"
+       portuguesebrazil = 0
+       if string.lower(lang1) == "portuguesebrazil": portuguesebrazil = 1
+       elif string.lower(lang2) == "portuguesebrazil": portuguesebrazil = 2
+       elif string.lower(lang3) == "portuguesebrazil": portuguesebrazil = 3
        
+       if ((portuguese > 0) and (portuguesebrazil == 0)):
+                       getallsubs(searchstring, "pt", "Portuguese", 
file_original_path, subtitles_list, searchstring_notclean)
+
+       if ((portuguesebrazil > 0) and (portuguese == 0)):
+                       getallsubs(searchstring, "pb", "PortugueseBrazil", 
file_original_path, subtitles_list, searchstring_notclean)
+
+       if ((portuguese > 0) and (portuguesebrazil > 0) and (portuguese < 
portuguesebrazil)):
+                       getallsubs(searchstring, "pt", "Portuguese", 
file_original_path, subtitles_list, searchstring_notclean)
+                       getallsubs(searchstring, "pb", "PortugueseBrazil", 
file_original_path, subtitles_list, searchstring_notclean)
+
+       if ((portuguese > 0) and (portuguesebrazil > 0) and (portuguese > 
portuguesebrazil)):
+                       getallsubs(searchstring, "pb", "PortugueseBrazil", 
file_original_path, subtitles_list, searchstring_notclean)
+                       getallsubs(searchstring, "pt", "Portuguese", 
file_original_path, subtitles_list, searchstring_notclean)
+
+       if ((portuguese == 0) and (portuguesebrazil == 0)):
+                       msg = "Won't work, LegendasDivx.com is only for 
Portuguese and Portuguese Brazil subtitles."
+
        return subtitles_list, "", msg #standard output
        
 def recursive_glob(treeroot, pattern):
@@ -306,8 +331,8 @@ def download_subtitles (subtitles_list, pos, zip_subs, 
tmp_sub_dir, sub_folder,
        d = resp1.read()
        #Now you download the subtitles
        language = subtitles_list[pos][ "language_name" ]
-       if string.lower(language) == "portuguese":
-               content = 
opener.open('http://www.legendasdivx.com/modules.php?name=Downloads&d_op=getit&lid='
 + id + '&username=highlander')
+#      if string.lower(language) == "portuguese" or string.lower(language) == 
"portuguesebrazilian":
+       content = 
opener.open('http://www.legendasdivx.com/modules.php?name=Downloads&d_op=getit&lid='
 + id + '&username=highlander')
 
        if content is not None:
                header = 
content.info()['Content-Disposition'].split('filename')[1].split('.')[-1].strip("\"")
diff --git a/script.xbmc.subtitles/resources/lib/utilities.py 
b/script.xbmc.subtitles/resources/lib/utilities.py
index f2c1f29..a59aa39 100644
--- a/script.xbmc.subtitles/resources/lib/utilities.py
+++ b/script.xbmc.subtitles/resources/lib/utilities.py
@@ -172,8 +172,9 @@ def rem_files(directory):
       shutil.rmtree(directory)
   except:
     pass
-
-  os.makedirs(directory)
+    
+  if not xbmcvfs.exists(directory):
+    os.makedirs(directory)
       
 def copy_files( subtitle_file, file_path ):
   subtitle_set = False
diff --git a/script.xbmc.subtitles/resources/settings.xml 
b/script.xbmc.subtitles/resources/settings.xml
index 7cd1db8..3620657 100644
--- a/script.xbmc.subtitles/resources/settings.xml
+++ b/script.xbmc.subtitles/resources/settings.xml
@@ -38,6 +38,7 @@
       <setting id="Ondertitel" type="bool" label="Ondertitel.com (Movies and 
Dutch subs only)" default="false"/>      
       <setting id="OpenSubtitles" type="bool" label="OpenSubtitles.org" 
default="true"/>
 
+      <setting id="Pipocas" type="bool" label="Pipocas.tv" default="false"/>
       <setting id="Podnapisi" type="bool" label="Podnapisi.net" 
default="true"/>
       <setting id="PNuser" type="text"  visible= "eq(-1,true)" 
enable="eq(-1,true)" label="30104" default=""/>
       <setting id="PNpass" type="text" option = "hidden" visible= 
"eq(-2,true)" enable="eq(-2,true)" label="30105" default=""/> 

http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/scripts;a=commit;h=5e5d45e0779dd8c6a544291181651a6770d82257

commit 5e5d45e0779dd8c6a544291181651a6770d82257
Author: amet <[email protected]>
Date:   Sun Oct 7 12:04:42 2012 +0400

    [script.module.xbmcswift2] -v1.2.0
    
    - Addon name, id and filepath are now no longer necessary for Plugin(). They
      will be discovered automatically.
    - Plugin() now creates the storage folder
    - Plugin.set_info now supports different info types
    - Sort methods can be passed as strings to plugin.finish()
    - Plugin caching has been renamed to storage and now works properly

diff --git a/script.module.xbmcswift2/addon.xml 
b/script.module.xbmcswift2/addon.xml
index ec634ad..86b96fb 100644
--- a/script.module.xbmcswift2/addon.xml
+++ b/script.module.xbmcswift2/addon.xml
@@ -1,4 +1,4 @@
-<addon id="script.module.xbmcswift2" name="xbmcswift2" provider-name="Jonathan 
Beluch (jbel)" version="1.1.1">
+<addon id="script.module.xbmcswift2" name="xbmcswift2" provider-name="Jonathan 
Beluch (jbel)" version="1.2.0">
   <requires>
     <import addon="xbmc.python" version="2.0" />
   </requires>
diff --git a/script.module.xbmcswift2/changelog.txt 
b/script.module.xbmcswift2/changelog.txt
index 3b89881..bb8ccc2 100644
--- a/script.module.xbmcswift2/changelog.txt
+++ b/script.module.xbmcswift2/changelog.txt
@@ -2,6 +2,17 @@ CHANGES
 =======
 
 
+Version 1.2
+-----------
+
+- Addon name, id and filepath are now no longer necessary for Plugin(). They
+  will be discovered automatically.
+- Plugin() now creates the storage folder
+- Plugin.set_info now supports different info types
+- Sort methods can be passed as strings to plugin.finish()
+- Plugin caching has been renamed to storage and now works properly
+
+
 Version 1.1.1
 -------------
 
diff --git a/script.module.xbmcswift2/lib/xbmcswift2/__init__.py 
b/script.module.xbmcswift2/lib/xbmcswift2/__init__.py
index 73aebc4..8c411d1 100644
--- a/script.module.xbmcswift2/lib/xbmcswift2/__init__.py
+++ b/script.module.xbmcswift2/lib/xbmcswift2/__init__.py
@@ -13,13 +13,15 @@ from types import ModuleType
 class module(ModuleType):
     '''A wrapper class for a module used to override __getattr__. This class
     will behave normally for any existing module attributes. For any attributes
-    which do not existin in the wrapped module, a mock function will be
+    which do not existi in in the wrapped module, a mock function will be
     returned. This function will also return itself enabling multiple mock
     function calls.
     '''
 
     def __init__(self, wrapped=None):
         self.wrapped = wrapped
+        if wrapped:
+            self.__dict__.update(wrapped.__dict__)
 
     def __getattr__(self, name):
         '''Returns any existing attr for the wrapped module or returns a mock
@@ -60,7 +62,7 @@ except ImportError:
     xbmcvfs = module()
 
 
-from xbmcswift2.cache import Cache, TimedCache
+from xbmcswift2.storage import TimedStorage
 from xbmcswift2.request import Request
 from xbmcswift2.common import (xbmc_url, enum, clean_dict, pickle_dict,
     unpickle_args, unpickle_dict, download_page, unhex)
diff --git a/script.module.xbmcswift2/lib/xbmcswift2/constants.py 
b/script.module.xbmcswift2/lib/xbmcswift2/constants.py
index 58f59f5..0a25692 100644
--- a/script.module.xbmcswift2/lib/xbmcswift2/constants.py
+++ b/script.module.xbmcswift2/lib/xbmcswift2/constants.py
@@ -19,7 +19,16 @@ class SortMethod(object):
 
     e.g. SORT_METHOD_TITLE becomes SortMethod.TITLE
     '''
-    pass
+
+    @classmethod
+    def from_string(cls, sort_method):
+        '''Returns the sort method specified. sort_method is case insensitive.
+        Will raise an AttributeError if the provided sort_method does not
+        exist.
+
+        >>> SortMethod.from_string('title')
+        '''
+        return getattr(cls, sort_method.upper())
 
 
 PREFIX = 'SORT_METHOD_'
diff --git a/script.module.xbmcswift2/lib/xbmcswift2/listitem.py 
b/script.module.xbmcswift2/lib/xbmcswift2/listitem.py
index 09c2676..fd6db23 100644
--- a/script.module.xbmcswift2/lib/xbmcswift2/listitem.py
+++ b/script.module.xbmcswift2/lib/xbmcswift2/listitem.py
@@ -181,7 +181,7 @@ class ListIib/xbmcswift2/listitem.pyem.py 
b/script.module.xbmcswift2/lib/xbmcswift2/listitem.pyt=""/> 
b.quote_plus(searchstring)_flag': 'flags/' + languageshort + '.gif', 
'language_name': languagelong})ÿЌۂÿk n]6+6 
èè!è! ˜Û‚ÿ™Û‚ÿxԊ^6+Û‚ÿ’n]6+†
 n]6+ ˜Û‚ÿ`n]6+ߘۂÿÀÏî^6+u
n]6+Àî^6+ØÄî^6+¨Éî^6+€*o]6+xԊ^6+Û‚ÿЍۂÿk
 
n]6+à!à! ˜Û‚ÿ™Û‚ÿHžj^6+àÛ‚ÿ’n]6+†
 n]6+ ˜Û‚ÿ`n]6+ߘۂÿИۂÿȘۂÿ8+]6+Œp˜™Û‚ÿu
n]6+€*o]6+àÛ‚ÿ°ŽÛ‚ÿk 
n]6+GHIJKMNOP ˜Û‚ÿ™Û‚ÿHUA^6+€‘Û‚ÿ’n]6+†
 n]6+ ˜Û‚ÿ0Ïî^6+u
n]6+àŠ^6+ ‰^6+Àî^6+ØÄî^6+¨Éî^6+€*o]6+HUA^6+€‘Û‚ÿÀÛ‚ÿk
 n]6+8UA^6+€‘Û‚ÿðÛ‚ÿk 
n]6+(UA^6+€‘Û‚ÿ Û‚ÿk 
n]6+UA^6+€‘Û‚ÿPÛ‚ÿk n]6+  ¨Îî^6+u
n]6+¨Éî^6+ЊۂÿŠØ^6+e 
n]6+ØÄî^6+ðÛ‚ÿyØ^6+e n]6+Àî^6+ 
‘Û‚ÿZØ^6+e 
n]6+؄^6+àŠ^6+ ‰^6+Àî^6+ØäŠ^6+ØÄî^6+¨Éî^6+€*o]6+8+]6+Mê]6+™Û‚ÿp^6+Вۂÿ ‘Û‚ÿk
 
n]6+5678:<=>?@BD ˜Û‚ÿ™Û‚ÿàMç]6+™Û‚ÿu
n]6+%]6+P‘Û‚ÿMê]6+e n]6+%]6+Õ_@fÆS 
Qöl±íÁc¶]6+™Û‚ÿàMç]6+à“Û‚ÿ°’Û‚ÿk 
n]6+u]ÞÓ      
4Q ÷U^Qʉ§¶uª¹ñìò*ēv ˜Û‚ÿ™Û‚ÿHž³]6+äï]6+PšÛ‚ÿ¦ðQ_6+¦ðQ_6+6í]6+ߘۂÿšÛ‚ÿȘۂÿ0•Û‚ÿïŠ^6+u
n]6+ØäŠ^6+›Û‚ÿ¢ðQ_6+`šÛ‚ÿð•Û‚ÿÿÿÿÿÿÿÿÿ¢ðQ_6+™Û‚ÿtem(object):
     @classmethod
     def from_dict(cls, label=None, label2=None, icon=None, thumbnail=None,
                   path=None, selected=None, info=None, properties=None,
-                  context_menu=None, is_playable=None):
+                  context_menu=None, is_playable=None, info_type='video'):
         '''A ListItem constructor for setting a lot of properties not
         available in the regular __init__ method. Useful to collect all
         the properties in a dict and then use the **dct to call this
@@ -193,7 +193,7 @@ class ListItem(object):
             listitem.select(selected)
 
         if info:
-            listitem.set_info('video', info)
+            listitem.set_info(info_type, info)
 
         if is_playable:
             listitem.set_is_playable(True)
diff --git a/script.module.xbmcswift2/lib/xbmcswift2/plugin.py 
b/script.module.xbmcswift2/lib/xbmcswift2/plugin.py
index b8a923c..eaad77b 100644
--- a/script.module.xbmcswift2/lib/xbmcswift2/plugin.py
+++ b/script.module.xbmcswift2/lib/xbmcswift2/plugin.py
@@ -40,22 +40,52 @@ class Plugin(XBMCMixin):
     plugin. Typical creation looks like this::
 
         from xbmcswift2 import Plugin
-        plugin = Plugin('Hello XBMC', 'plugin.video.helloxbmc', __file__)
+        plugin = Plugin('Hello XBMC')
+
+
+    .. versionchanged:: 0.2
+        The *addon_id* and *filepath* parameters are now optional. They will
+        now default to the correct values.
 
     :param name: The name of the plugin, e.g. 'Academic Earth'.
+
     :param addon_id: The XBMC addon ID for the plugin, e.g.
-                     'plugin.video.academicearth'
-    :param filepath: The path to the addon.py file. In typical usage, the
-                     builtin ``__file__`` variable can used.
+                     'plugin.video.academicearth'. This parameter is now
+                     optional and is really only useful for testing purposes.
+                     If it is not provided, the correct value will be parsed
+                     from the addon.xml file.
+
+    :param filepath: Optional parameter. If provided, it should be the path to
+                     the addon.py file in the root of the addon directoy. This
+                     only has an effect when xbmcswift2 is running on the
+                     command line. Will default to the current working
+                     directory since xbmcswift2 requires execution in the root
+                     addon directoy anyway. The parameter still exists to ease
+                     testing.
     '''
 
-    def __init__(self, name, addon_id, filepath):
+    def __init__(self, name=None, addon_id=None, filepath=None, 
info_type=None):
         self._name = name
-        self._filepath = filepath
-        self._addon_id = addon_id
         self._routes = []
         self._view_functions = {}
-        self._addon = xbmcaddon.Addon(id=self._addon_id)
+
+        # addon_id is no longer required as it can be parsed from addon.xml
+        if addon_id:
+            self._addon = xbmcaddon.Addon(id=addon_id)
+        else:
+            self._addon = xbmcaddon.Addon()
+
+        self._addon_id = addon_id or self._addon.getAddonInfo('id')
+        self._name = name or self._addon.getAddonInfo('name')
+
+        self._info_type = info_type
+        if not self._info_type:
+            types = {
+                'video': 'video',
+                'audio': 'music',
+                'image': 'pictures',
+            }
+            self._info_type = types.get(self._addon_id.split('.')[1], 'video')
 
         # Keeps track of the added list items
         self._current_items = []
@@ -67,26 +97,41 @@ class Plugin(XBMCMixin):
         self._end_of_directory = False
 
         # The plugin's named logger
-        self._log = setup_log(addon_id)
+        self._log = setup_log(self._addon_id)
 
-        # The path to the cache directory for the addon
-        self._cache_path = xbmc.translatePath(
-            'special://profile/addon_data/%s/.cache/' % self._addon_id)
+        # The path to the storage directory for the addon
+        self._storage_path = xbmc.translatePath(
+            'special://profile/addon_data/%s/.storage/' % self._addon_id)
+        if not os.path.isdir(self._storage_path):
+            os.makedirs(self._storage_path)
 
         # If we are runing in CLI, we need to load the strings.xml manually
-        # TODO: a better way to do this. Perhaps allow a user provided filepath
+        # Since xbmcswift2 currently relies on execution from an addon's root
+        # directly, we can rely on cwd for now...
         if xbmcswift2.CLI_MODE:
             from xbmcswift2.mockxbmc import utils
-            utils.load_addon_strings(self._addon,
-                os.path.join(os.path.dirname(self._filepath), 'resources',
-                             'language', 'English', 'strings.xml'))
+            if filepath:
+                addon_dir = os.path.dirname(filepath)
+            else:
+                addon_dir = os.getcwd()
+            strings_fn = os.path.join(addon_dir, 'resources', 'language',
+                                      'English', 'strings.xml')
+            utils.load_addon_strings(self._addon, strings_fn)
+
+    @property
+    def info_type(self):
+        return self._info_type
 
     @property
     def log(self):
         '''The log instance for the plugin. Returns an instance of the
         stdlib's ``logging.Logger``. This log will print to STDOUT when running
         in CLI mode and will forward messages to XBMC's log when running in
-        XBMC.
+        XBMC. Some examples::
+
+            plugin.log.debug('Debug message')
+            plugin.log.warning('Warning message')
+            plugin.log.error('Error message')
         '''
         return self._log
 
@@ -96,13 +141,13 @@ class Plugin(XBMCMixin):
         return self._addon_id
 
     @property
-    def cache_path(self):
-        '''A full path to the cache folder for this plugin's addon data.'''
-        return self._cache_path
+    def storage_path(self):
+        '''A full path to the storage folder for this plugin's addon data.'''
+        return self._storage_path
 
     @property
     def addon(self):
-        '''This plugin's underlying instance of xbmcaddon.Addon.'''
+        '''This plugin's wrapped instance of xbmcaddon.Addon.'''
         return self._addon
 
     @property
@@ -176,7 +221,7 @@ class Plugin(XBMCMixin):
         '''A decorator to add a route to a view and also apply caching.
         '''
         route_decorator = self.route(url_rule, name=name, options=options)
-        cache_decorator = self.cache()
+        cache_decorator = self.cached()
         def new_decorator(func):
             return route_decorator(cache_decorator(func))
         return new_decorator
@@ -202,10 +247,12 @@ class Plugin(XBMCMixin):
         rule = UrlRule(url_rule, view_func, name, options)
         if name in self._view_functions.keys():
             # TODO: Raise exception for ambiguous views during registration
-            log.warning('Cannot add url rule "%s" with name "%s". There is 
already a view with that name' % (url_rule, name))
+            log.warning('Cannot add url rule "%s" with name "%s". There is '
+                        'already a view with that name' % (url_rule, name))
             self._view_functions[name] = None
         else:
-            log.debug('Adding url rule "%s" named "%s" pointing to function 
"%s"' % (url_rule, name, view_func.__name__))
+            log.debug('Adding url rule "%s" named "%s" pointing to function '
+                      '"%s"' % (url_rule, name, view_func.__name__))
             self._view_functions[name] = rule
         self._routes.append(rule)
 
@@ -236,7 +283,8 @@ class Plugin(XBMCMixin):
                 view_func, items = rule.match(path)
             except NotFoundException:
                 continue
-            log.info('Request for "%s" matches rule for function "%s"' % 
(path, view_func.__name__))
+            log.info('Request for "%s" matches rule for function "%s"'
+                     % (path, view_func.__name__))
             listitems = view_func(**items)
 
             # TODO: Verify the main UI handle is always 0, this check exists so
diff --git a/script.module.xbmcswift2/lib/xbmcswift2/xbmcmixin.py 
b/script.module.xbmcswift2/lib/xbmcswift2/xbmcmixin.py
index 94c3f5b..7fe7645 100644
--- a/script.module.xbmcswift2/lib/xbmcswift2/xbmcmixin.py
+++ b/script.module.xbmcswift2/lib/xbmcswift2/xbmcmixin.py
@@ -7,9 +7,9 @@ from functools import wraps
 
 import xbmcswift2
 from xbmcswift2 import xbmc, xbmcaddon, xbmcplugin
-from xbmcswift2.cache import Cache, TimedCache
+from xbmcswift2.storage import TimedStorage
 from xbmcswift2.logger import log
-from xbmcswift2.constants import VIEW_MODES
+from xbmcswift2.constants import VIEW_MODES, SortMethod
 from common import Modes, DEBUG_MODES
 from request import Request
 
@@ -19,24 +19,40 @@ class XBMCMixin(object):
     the child class must implement the following methods and
     properties:
 
-        def cache_path(self, path)
+        # Also, the child class is responsible for ensuring that this path
+        # exists.
+        self.storage_path  
 
-        self.cache_path
-        self.addon
         self.added_items
+
         self.request
-    _end_of_directory = False
-    _memoized_cache = None
-    _unsynced_caches = None
+
+        self.addon
+
+        _end_of_directory = False
+
+        self.handle
+
+    # optional 
+    self.info_type: should be in ['video', 'music', 'pictures']
+    _memoized_storage = None
+    _unsynced_storages = None
     # TODO: Ensure above is implemented
     '''
-    def cache(self, ttl_hours=24):
-        '''View caching decorator. Currently must be closest to the
-        view because route decorators don't wrap properly.
+    def cached(self, TTL=60 * 24):
+        '''A decorator that will cache the output of the wrapped function. The
+        key used for the cache is the function name as well as the `*args` and
+        `**kwargs` passed to the function.
+
+        :param TTL: time to live in minutes
+
+        .. note:: For route caching, you should use
+                  :meth:`xbmcswift2.Plugin.cached_route`.
         '''
         def decorating_function(function):
-            cache = self.get_timed_cache('function_cache', 
file_format='pickle',
-                                         ttl=timedelta(hours=ttl_hours))
+            # TODO test this method
+            storage = self.get_storage('.functions', file_format='pickle',
+                                       TTL=TTL)
             kwd_mark = 'f35c2d973e1bbbc61ca60fc6d7ae4eb3'
 
             @wraps(function)
@@ -46,42 +62,68 @@ class XBMCMixin(object):
                     key += (kwd_mark,) + tuple(sorted(kwargs.items()))
 
                 try:
-                    result = cache[key]
+                    result = storage[key]
                     #log.debug('Cache hit for key "%s"' % (key, ))
-                    log.debug('Cache hit for function "%s" with args "%s" and 
kwargs "%s"' % (function.__name__, args, kwargs))
+                    log.debug('Storage hit for function "%s" with args "%s" '
+                              'and kwargs "%s"' % (function.__name__, args,
+                                                   kwargs))
                 except KeyError:
-                    log.debug('Cache miss for function "%s" with args "%s" and 
kwargs "%s"' % (function.__name__, args, kwargs))
+                    log.debug('Storage miss for function "%s" with args "%s" '
+                              'and kwargs "%s"' % (function.__name__, args,
+                                                   kwargs))
                     result = function(*args, **kwargs)
-                    cache[key] = result
-                cache.sync()
+                    storage[key] = result
+                storage.sync()
                 return result
             return wrapper
         return decorating_function
 
-    def _get_cache(self, cache_type, cache_name, **kwargs):
-        if not hasattr(self, '_unsynced_caches'):
-            self._unsynced_caches = {}
-        filename = os.path.join(self.cache_path, cache_name)
+    def list_storages(self):
+        '''Returns a list of existing stores. The returned names can then be
+        used to call get_storage().
+        '''
+        # Filter out any storages used by xbmcswift2 so caller doesn't corrupt
+        # them.
+        return [name for name in os.listdir(self.storage_path)
+                if not name.startswith('.')]
+
+    def get_storage(self, name='main', file_format='pickle', TTL=None):
+        '''Returns a storage for the given name. The returned storage is a
+        fully functioning python dictionary and is designed to be used that
+        way. It is usually not necessary for the caller to load or save the
+        storage manually. If the storage does not already exist, it will be
+        created.  
+
+        .. seealso:: :class:`xbmcswift2.TimedStorage` for more details.
+
+        :param name: The name  of the storage to retrieve.
+        :param file_format: Choices are 'pickle', 'csv', and 'json'. Pickle is
+                            recommended as it supports python objects.
+
+                            .. note:: If a storage already exists for the given
+                                      name, the file_format parameter is
+                                      ignored. The format will be determined by
+                                      the existing storage file.
+        :param TTL: The time to live for storage items specified in minutes or 
None
+                    for no expiration. Since storage items aren't expired 
until a
+                    storage is loaded form disk, it is possible to call
+                    get_storage() with a different TTL than when the storage 
was
+                    created. The currently specified TTL is always honored.
+        '''
+
+        if not hasattr(self, '_unsynced_storages'):
+            self._unsynced_storages = {}
+        filename = os.path.join(self.storage_path, name)
         try:
-            cache = self._unsynced_caches[filename]
-            log.debug('Used live cache "%s" located at "%s"' % (cache_name, 
filename))
+            storage = self._unsynced_storages[filename]
+            log.debug('Loaded storage "%s" from memory' % name)
         except KeyError:
-            cache = cache_type(filename, **kwargs)
-            self._unsynced_caches[filename] = cache
-            log.debug('Used cold cache "%s" located at "%s"' % (cache_name, 
filename))
-        return cache
-
-    def get_timed_cache(self, cache_name, file_format='pickle', ttl=None):
-        return self._get_cache(TimedCache, cache_name, 
file_format=file_format, ttl=ttl)
-
-    def get_cache(self, cache_name, file_format='pickle'):
-        return self._get_cache(TimedCache, cache_name, file_format=file_format)
-
-    def cache_fn(self, path):
-        # TODO:
-        #if not os.path.exists(self._cache_path):
-            #os.mkdir(self._cache_path)
-        return os.path.join(self.cache_path, path)
+            if TTL:
+                TTL = timedelta(minutes=TTL)
+            storage = TimedStorage(filename, file_format, TTL)
+            self._unsynced_storages[filename] = storage
+            log.debug('Loaded storage "%s" from disk' % name)
+        return storage
 
     def temp_fn(self, path):
         return os.path.join(xbmc.translatePath('special://temp/'), path)
@@ -127,6 +169,12 @@ class XBMCMixin(object):
         _items = []
         for item in items:
             if not hasattr(item, 'as_xbmc_listitem'):
+                if 'info_type' in item.keys():
+                    log.warning('info_type key has no affect for playlist '
+                                'items as the info_type is inferred from the '
+                                'playlist type.')
+                # info_type has to be same as the playlist type
+                item['info_type'] = playlist
                 item = xbmcswift2.ListItem.from_dict(**item)
             _items.append(item)
             selected_playlist.add(item.get_path(), item.as_xbmc_listitem())
@@ -157,7 +205,7 @@ class XBMCMixin(object):
         if not msg:
             log.warning('Empty message for notification dialog')
         if title is None:
-            title = self.plugin.name
+            title = self.addon.getAddonInfo('name')
         xbmc.executebuiltin('XBMC.Notification("%s", "%s", "%s", "%s")' %
                             (msg, title, delay, image))
 
@@ -168,10 +216,12 @@ class XBMCMixin(object):
         return [item]
 
     def play_video(self, item, player=xbmc.PLAYER_CORE_DVDPLAYER):
-        if not isinstance(item, xbmcswift2.ListItem):
+        if not hasattr(item, 'as_xbmc_listitem'):
+            if 'info_type' not in item.keys():
+                item['info_type'] = 'video'
             item = xbmcswift2.ListItem.from_dict(**item)
         item.set_played(True)
-        xbmc.Player(player).play(item.get_path, item)
+        xbmc.Player(player).play(item.get_path(), item.as_xbmc_listitem())
         return [item]
 
     def add_items(self, items):
@@ -187,11 +237,14 @@ class XBMCMixin(object):
         '''
         # For each item if it is not already a list item, we need to create one
         _items = []
+        info_type = self.info_type if hasattr(self, 'info_type') else 'video'
 
         # Create ListItems for anything that is not already an instance of
         # ListItem
         for item in items:
             if not isinstance(item, xbmcswift2.ListItem):
+                if 'info_type' not in item.keys():
+                    item['info_type'] = info_type
                 item = xbmcswift2.ListItem.from_dict(**item)
             _items.append(item)
 
@@ -220,19 +273,56 @@ class XBMCMixin(object):
                                              update_listing, cache_to_disc)
         assert False, 'Already called endOfDirectory.'
 
+    def add_sort_method(self, sort_method, label2_mask=None):
+        '''A wrapper for `xbmcplugin.addSortMethod()
+        
<http://mirrors.xbmc.org/docs/python-docs/xbmcplugin.html#-addSortMethod>`_.
+        You can use ``dir(xbmcswift2.SortMethod)`` to list all available sort
+        methods.
+
+        :param sort_method: A valid sort method. You can provided the constant
+                            from xbmcplugin, an attribute of SortMethod, or a
+                            string name. For instance, the following method
+                            calls are all equivalent:
+        
+                            * 
``plugin.add_sort_method(xbmcplugin.SORT_METHOD_TITLE)``
+                            * ``plugin.add_sort_metohd(SortMethod.TITLE)``
+                            * ``plugin.add_sort_method('title')``
+        :param label2_mask: A mask pattern for label2. See the `XBMC
+                            documentation
+                            
<http://mirrors.xbmc.org/docs/python-docs/xbmcplugin.html#-addSortMethod>`_
+                            for more information.
+        '''
+        try:
+            # Assume it's a string and we need to get the actual int value
+            sort_method = SortMethod.from_string(sort_method)
+        except AttributeError:
+            # sort_method was already an int (or a bad value)
+            pass
+
+        if label2_mask:
+            xbmcplugin.addSortMethod(self.handle, sort_method, label2_mask)
+        else:
+            xbmcplugin.addSortMethod(self.handle, sort_method)
+
     def finish(self, items=None, sort_methods=None, succeeded=True,
                update_listing=False, cache_to_disc=True, view_mode=None):
-        '''Adds the provided items to the XBMC interface. Each item in
-        the provided list should either be an instance of
-        xbmcswift2.ListItem or a dictionary that will be passed to
-        xbmcswift2.ListItem.from_dict().
+        '''Adds the provided items to the XBMC interface. 
 
         :param items: an iterable of items where each item is either a
             dictionary with keys/values suitable for passing to
             :meth:`xbmcswift2.ListItem.from_dict` or an instance of
             :class:`xbmcswift2.ListItem`.
-        :param sort_methods: a list of valid XBMC sort_methods. See
-            :attr:`xbmcswift2.SortMethod`.
+        :param sort_methods: a list of valid XBMC sort_methods. Each item in
+                             the list can either be a sort method or a tuple of
+                             ``sort_method, label2_mask``. See
+                             :meth:`add_sort_method` for
+                             more detail concerning valid sort_methods.
+
+                             Example call with sort_methods::
+
+                                sort_methods = ['label', 'title', ('date', 
'%D')]
+                                plugin.finish(items, sort_methods=sort_methods)
+                                
         :param view_mode: can either be an integer (or parseable integer
             string) corresponding to a view_mode or the name of a type of view.
             Currrently the only view type supported is 'thumbnail'.
@@ -243,7 +333,10 @@ class XBMCMixin(object):
             self.add_items(items)
         if sort_methods:
             for sort_method in sort_methods:
-                xbmcplugin.addSortMethod(self.handle, sort_method)
+                if not isinstance(sort_method, basestring) and 
hasattr(sort_method, '__len__'):
+                    self.add_sort_method(*sort_method)
+                else: 
+                    self.add_sort_method(sort_method)
 
         # Attempt to set a view_mode if given
         if view_mode is not None:
@@ -260,11 +353,11 @@ class XBMCMixin(object):
         # Finalize the directory items
         self.end_of_directory(succeeded, update_listing, cache_to_disc)
 
-        # Close any open caches which will persist them to disk
-        if hasattr(self, '_unsynced_caches'):
-            for cache in self._unsynced_caches.values():
-                log.debug('Saving a %s cache to disk at "%s"' % 
(cache.file_format, cache.filename))
-                cache.close()
+        # Close any open storages which will persist them to disk
+        if hasattr(self, '_unsynced_storages'):
+            for storage in self._unsynced_storages.values():
+                log.debug('Saving a %s storage to disk at "%s"' % 
(storage.file_format, storage.filename))
+                storage.close()
 
         # Return the cached list of all the list items that were added
         return self.added_items
diff --git a/script.module.xbmcswift2/xbmcswift2_version 
b/script.module.xbmcswift2/xbmcswift2_version
index 8729fe2..27fa158 100644
--- a/script.module.xbmcswift2/xbmcswift2_version
+++ b/script.module.xbmcswift2/xbmcswift2_version
@@ -1 +1 @@
-7328e8897aee9de57c78305dcc1724fe361835ad
\ No newline at end of file
+8316a8e340dc979b1888a961ac4e0909b158b253
\ No newline at end of file

-----------------------------------------------------------------------

Summary of changes:
 script.module.xbmcswift2/addon.xml                 |    2 +-
 script.module.xbmcswift2/changelog.txt             |   11 +
 .../lib/xbmcswift2/__init__.py                     |    6 +-
 .../lib/xbmcswift2/constants.py                    |   11 +-
 .../lib/xbmcswift2/listitem.py                     |    4 +-
 script.module.xbmcswift2/lib/xbmcswift2/plugin.py  |   98 +++++--
 .../lib/xbmcswift2/{cache.py => storage.py}        |   48 ++--
 .../lib/xbmcswift2/xbmcmixin.py                    |  203 +++++++++++----
 script.module.xbmcswift2/xbmcswift2_version        |    2 +-
 script.xbmc.subtitles/addon.xml                    |    2 +-
 script.xbmc.subtitles/changelog.txt                |    6 +
 script.xbmc.subtitles/resources/lib/gui.py         |    2 +-
 .../resources/lib/services/LegendasDivx/service.py |   41 +++-
 .../resources/lib/services/Pipocas}/__init__.py    |    0
 .../resources/lib/services/Pipocas/logo.png        |  Bin 0 -> 70166 bytes
 .../services/{LegendasDivx => Pipocas}/service.py  |  276 +++++++++++---------
 script.xbmc.subtitles/resources/lib/utilities.py   |    5 +-
 script.xbmc.subtitles/resources/settings.xml       |    1 +
 18 files changed, 477 insertions(+), 241 deletions(-)
 rename script.module.xbmcswift2/lib/xbmcswift2/{cache.py => storage.py} (78%)
 copy {script.cu.lyrics/resources/lib/scrapers => 
script.xbmc.subtitles/resources/lib/services/Pipocas}/__init__.py (100%)
 create mode 100644 
script.xbmc.subtitles/resources/lib/services/Pipocas/logo.png
 copy script.xbmc.subtitles/resources/lib/services/{LegendasDivx => 
Pipocas}/service.py (53%)


hooks/post-receive
-- 
Scripts

------------------------------------------------------------------------------
Don't let slow site performance ruin your business. Deploy New Relic APM
Deploy New Relic app performance management and know exactly
what is happening inside your Ruby, Python, PHP, Java, and .NET app
Try New Relic at no cost today and get our sweet Data Nerd shirt too!
http://p.sf.net/sfu/newrelic-dev2dev
_______________________________________________
Xbmc-addons mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xbmc-addons

Reply via email to