Information to keep in mind when creating a url resolver

Status
Not open for further replies.

Eldorado

Moderator
Staff member
May 7, 2012
990
0
0
Vinny, I pushed some small updates to the movreel resolver

For the settings - this is a tip for everyone creating resolvers, make sure they are preceded by the name of the resolver class to simplify your code and eliminate the need to import xbmcaddon

eg.

This is the typical start to a new resolver, I put the name in RED:

Code:
class [COLOR="#FF0000"]MovreelResolver[/COLOR](Plugin, UrlResolver, SiteAuth, PluginSettings):
    implements = [UrlResolver, SiteAuth, PluginSettings]
    name = "movreel"
    profile_path = common.profile_path
    cookie_file = os.path.join(profile_path, '%s.cookies' % name)
Further down when you do your settings, name them as so, note the setting id naming convention:

Code:
#Obtaining the Movreel login info from your app
    def get_settings_xml(self):
        xml = PluginSettings.get_settings_xml(self)
        xml += '<setting id="[COLOR="#FF0000"]MovreelResolver[/COLOR]_login" '
        xml += 'type="bool" label="login" default="false"/>\n'
        xml += '<setting id="[COLOR="#FF0000"]MovreelResolver[/COLOR]_username" enable="eq(-1,true)" '
        xml += 'type="text" label="username" default=""/>\n'
        xml += '<setting id="[COLOR="#FF0000"]MovreelResolver[/COLOR]_password" enable="eq(-2,true)" '
        xml += 'type="text" label="password" option="hidden" default=""/>\n'
        return xml
Then in your code, you just need to do this to grab a setting, no need to import in xbmcaddon as it's already available via urlresolvers PluginSettings which you have imported

Note how I don't use the preceding 'MovreelResolver_' portion:

Code:
login = self.get_setting('login')

Some have done this for their settings so that you don't need to worry about the convention:

Code:
    #PluginSettings methods
    def get_settings_xml(self):
        xml = PluginSettings.get_settings_xml(self)
        xml += '<setting label="Highest Quality" id="[COLOR="#FF0000"]%s[/COLOR]_quality" ' % [COLOR="#FF0000"]self.__class__.__name__[/COLOR]
        xml += 'type="enum" values="FLV|Maximum" default="0" />\n'
        xml += '<setting id="%s_login" ' % self.__class__.__name__
        xml += 'type="bool" label="login" default="false"/>\n'
        xml += '<setting id="%s_username" enable="eq(-1,true)" ' % self.__class__.__name__
        xml += 'type="text" label="username" default=""/>\n'
        xml += '<setting id="%s_password" enable="eq(-2,true)" ' % self.__class__.__name__
        xml += 'type="text" label="password" option="hidden" default=""/>\n'
        xml += '<setting id="%s_notify" ' % self.__class__.__name__
        xml += 'type="bool" label="Notify on login" default="false"/>\n'
        return xml
 

Eldorado

Moderator
Staff member
May 7, 2012
990
0
0
Error Trapping

Here's another tip - though this could have some debate depending on your coding style

I prefer trapping all exceptions as script errors are not a pretty sight to end users, handle them and give the user an informative/friendly msg.. it's how I've been taught by doing development in the financial industry - capture and log everything, programs should never fail unexpectedly! (within reason)

I wrap any code in try:except blocks where I feel there is a chance for failure - requesting a page, searching for a string, data manipulation etc.

If the error cannot be handled programmatically (eg. setting a default value) and it will interrupt/fail the current process not allowing the code/user action to continue past this point, then I capture the error msg, write full details out to log file, and display a msg to the user just letting them know it failed and briefly the reason

Also do some due diligence and try to find any possible areas for errors such as file deleted from host type scenarios, these should be easy to find by simply typing in random letters in the url in your browser (eg. http://billionuploads.com/abcd12354). If you can get the most common msgs a file host has and handle them within your resolver then all the better!

The more informed the user is as to what is going on the better, makes our lives a bit easier on the support side - script errors will prompt questions and claims of broken resolvers, where a msg indicating file has been deleted is straight forward and the user will (hopefully) move on to the next host

So in a resolver you would have this basic structure, I'll use the get_media_url() method as it is likely to be the most error prone in the entire class:

Code:
def get_media_url(self, host, media_id):
    try:
        <your code here>
         
        #Checks for possible error msgs returned in html pages
        r = re.search("File does not exist", html)
        if r:
            common.addon.log_notice("File has been deleted")
            raise Exception("File has been deleted")  <---- I raise an exception here since I have an all in 1 error catch/handler at the bottom

         <more code>

    except Exception, e:  <-- e now contains all exception information
        common.addon.log_error("Error : %s" % e)
        <display a msg box to the user with appropriate msg>
        return False  <---- Always return something back to URLResolver, a false will indicate resolving failed
You might even want to also check for specific exceptions as you will gain more detailed information by doing so, such as error requesting a url will be trapped like this:

Code:
except urllib2.URLError, e:
You can do them in tandom and have a list of possible exception traps:

Code:
except urllib2.URLError, e:
    common.addon.log_error("Http error code: %d requesting url: %s" % (e.code, url))

except Exception, e:
    common.addon.log_error("Error : %s" % e)
Now we have a possible issue Bstrdsmkr brought up regarding pop up msg boxes and the auto-play feature in 1Ch**nel, if we pop up any on screen with the ok button they will interfere with that functionality

I'm proposing using just the small notification boxes rather than forcing the user to hit ok, both do the job equally well and hopefully the small boxes stay out of the way

Here's quick code on one of those guys, reference vidxden.py where I believe it was voinage that did one with a small red X:

Code:
xbmc.executebuiltin('XBMC.Notification([B][COLOR white]VIDXDEN[/COLOR][/B],[COLOR red]No such file or the file has been removed due to copyright infringement issues[/COLOR],2500,'+logo+')')
Looks like the logo is pulled from here - http://googlechromesupportnow.com/wp-content/uploads/2012/06/Installation-103-error-in-Chrome.png

I think it might be nice to add this graphic into urlresolver

Edit - I forgot t0mm0 common has msg box stuff already coded for you, so no need to import xbmc

Code:
common.addon.show_small_popup(title, msg, delay (seconds in thousands), image)
 
Last edited:

Eldorado

Moderator
Staff member
May 7, 2012
990
0
0
Logging

And a quick one - message logs!

Log as much as you feel you need to in order for ongoing debugging/support - but be mindful on what you are spitting out as to not fill our logs with useless info making it harder for us to debug

Do it at logical points, immediately before performing an operation that is error prone, before requesting a url etc.

Avoid using print statements in your final code

Use the common.addon.log_<type> function that is apart of URLResolver:

Import:

Code:
from urlresolver import common
Errors:
Code:
common.addon.log_error("your message here")
Debug (only printed when user is in debug mode):
Code:
common.addon.log_debug("your message here")
Notice (always printed):
Code:
common.addon.log_notice("your message here")
Or you can use common.addon.log("message", loglevel) where log level is:

Code:
            xbmc.LOGDEBUG = 0
            xbmc.LOGERROR = 4
            xbmc.LOGFATAL = 6
            xbmc.LOGINFO = 1
            xbmc.LOGNONE = 7
            xbmc.LOGNOTICE = 2
            xbmc.LOGSEVERE = 5
            xbmc.LOGWARNING = 3
 

Eldorado

Moderator
Staff member
May 7, 2012
990
0
0
Sleep/Delay/Countdowns

If you are in need of performing a delay in your resolver due to such things as the host requiring to wait X number of seconds before proceeding to the final link, you can use a pre-built function in common.addon

This will display a progress bar to the user and countdown the 'time_to_wait' in seconds

common.addon.show_countdown(self, time_to_wait, title='', text=''):
 

Eldorado

Moderator
Staff member
May 7, 2012
990
0
0
Imports

1. Be VERY mindful of what you are importing!

- Check that a function does not already exist in common.addon first that can do this for you
- Make sure you are using every single one of your imports

2. Many common functions and libs are already import via common.addon and common.net

Python apparently will not import libs multiple times, though repeatedly importing can affect performance

3. If you only make use of a lib once or twice in a single function, consider changing your import to be called within the function where it's being used versus having it imported every time on init, this can result in a performance gain
 
Last edited:
Status
Not open for further replies.