Kodi Community Forum
script.module.urlresolver development - Printable Version

+- Kodi Community Forum (https://forum.kodi.tv)
+-- Forum: Development (https://forum.kodi.tv/forumdisplay.php?fid=32)
+--- Forum: Add-ons (https://forum.kodi.tv/forumdisplay.php?fid=26)
+--- Thread: script.module.urlresolver development (/showthread.php?tid=105707)

Pages: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28


- DragonWin - 2011-09-04

t0mm0 Wrote:this looks great! thanks for the video - you are really good at that Wink

a couple of questions.....

i don't think there's any need to be passing sys.argv[2] around - that is already available to methods in addon as self.queries already parsed and as a dictionary. (just noticed that's not documented - i should fix that!)

I'm not sure I can use the self.queries, as I pass a rather commplex data structure, and keywords can clash with existing keywords used by the addon itself, so I have my own pack / unpack function for the savefavorite. For directories I need to keep the original queries intact, and to avoid keyword clashes I created the dict_to_string and string_to_dict functions, which can convert a dict where the values are dict, tuple, list, str, Nonetype, Bol, dict.

The delete favorite and showfavorites should be able to use the self.queries though.

I'll look into deletefavorite and showfavorite if those can't be changed to use the self.queries dict.

t0mm0 Wrote:rather than passing the context menu object back into addon.create_context_Menu(), would it be better to make a ContextMenu class? then you could do something like:
Code:
cm=ContextMenu()
cm.add(title, query, new_list)
(it would probably be nicer if the query bit were a dictionary rather than a string so it is the same as elsewhere {'mode': 'mymode'} which would make it easier to add more items too)

This is interesting, and it makes good sense to make it a class for itself, but I need a bit of help with how to do that. Should it be in it's own file, if so how do I make sure it can be imported, where should the file be placed etc Eek

It should be a minimal of effor to change the string in contextmenu('mode='mail') into a dict.

t0mm0 Wrote:would that also make it possible to make create_favorite() a method on the context menu object so everything was kept together?

Makes sense, and it's easy to do, as the create_favorite only returns a pre-defined dict, the data in the dict is first used/added on add_item / add_dir. This was more of a way to also separate the documentation, as it gave me a headache trying to make it look nice, and communicate the data structure without it getting confusing in the add_item, add_dir docs, and I think it makes it more logical to work with.

Guess I'll just commit what I have now to my branch, before I break it Laugh


- t0mm0 - 2011-09-04

DragonWin Wrote:I'm not sure I can use the self.queries, as I pass a rather commplex data structure, and keywords can clash with existing keywords used by the addon itself, so I have my own pack / unpack function for the savefavorite. For directories I need to keep the original queries intact, and to avoid keyword clashes I created the dict_to_string and string_to_dict functions, which can convert a dict where the values are dict, tuple, list, str, Nonetype, Bol, dict.
sounds complicated Wink of course it is easy for me to say these things without seeing the code!

if you REALLY need access to the original sys.argv[2] (which i still need to be convinced of Wink) then you could alter the addon.__init__() to store it as a class attribute alongside the parsed version and that will at least save you passing it around again.
DragonWin Wrote:This is interesting, and it makes good sense to make it a class for itself, but I need a bit of help with how to do that. Should it be in it's own file, if so how do I make sure it can be imported, where should the file be placed etc Eek
it could probably go in addon.py (at least for now). when using it from an addon, you would then just do
Code:
from t0mm0.common.addon import ContextMenu
DragonWin Wrote:Makes sense, and it's easy to do, as the create_favorite only returns a pre-defined dict, the data in the dict is first used/added on add_item / add_dir. This was more of a way to also separate the documentation, as it gave me a headache trying to make it look nice, and communicate the data structure without it getting confusing in the add_item, add_dir docs, and I think it makes it more logical to work with.
i think it makes sense to keep it together as from a user point of view it is just adding a context menu item.
DragonWin Wrote:Guess I'll just commit what I have now to my branch, before I break it Laugh
cool, git comes in handy when you start breaking things Wink

t0mm0.


- DragonWin - 2011-09-04

t0mm0 Wrote:sounds complicated Wink of course it is easy for me to say these things without seeing the code!
Actually I don't think it's that complicated if I just knew more about python Wink

t0mm0 Wrote:if you REALLY need access to the original sys.argv[2] (which i still need to be convinced of Wink) then you could alter the addon.__init__() to store it as a class attribute alongside the parsed version and that will at least save you passing it around again.
Your right, I have just changed it to use the self.queries

t0mm0 Wrote:it could probably go in addon.py (at least for now). when using it from an addon, you would then just do
Code:
from t0mm0.common.addon import ContextMenu
i think it makes sense to keep it together as from a user point of view it is just adding a context menu item.
I agree, I'm thinking some thing along the lines of:

cm.add_context()
Would keep appending / building on what exists

cm.get_context()
Would be used to feed the contextmenuobj to add_dir etc

cm.add_favorite()
Would create the favorite option

cm.get_favorite()
Would be used to feed the favorite to add_dir etc

t0mm0 Wrote:cool, git comes in handy when you start breaking things Wink

t0mm0.

He he most definitely.


- t0mm0 - 2011-09-04

DragonWin Wrote:I agree, I'm thinking some thing along the lines of:

cm.add_context()
Would keep appending / building on what exists

cm.get_context()
Would be used to feed the contextmenuobj to add_dir etc

cm.add_favorite()
Would create the favorite option

cm.get_favorite()
Would be used to feed the favorite to add_dir etc

sounds good, but regarding passing to add_dir() etc. - why not just pass cm itself and then add_dir() would call get_context() etc? that way you only need to add one argument to add_dir().

t0mm0


- DragonWin - 2011-09-04

t0mm0 Wrote:sounds good, but regarding passing to add_dir() etc. - why not just pass cm itself and then add_dir() would call get_context() etc? that way you only need to add one argument to add_dir().

t0mm0

again you make a lot of sense, also seeing as we can add additional information if required in the cm object, without changing any thing for the author.

And I just officially broke 70% of the favorite stuff while moving stuff around, time to go fix Cool


- DragonWin - 2011-09-05

Hey,

I have 3 items I'm not satisfied with in the favorite code, and I hope for some input on it.

1) Passing addon.url to ContextMenu
The only way I have found to get hold of the addon.url is to initialize ContextMenu with it as an arg.

Code:
addon = Addon('plugin.video.solarmovie', sys.argv)
cm = ContextMenu(addon.url)

2) Getting hold of the addon's ContextMenu object
In order to utilize the existing code to generate the "Delete favorite" menu, the addon needs to pass the cm object in order to generate the menu.

Code:
addon.show_favorites(cm)

3) A way to make a dict to a string and the other way around
As I need to hide data from xbmc when it's sent to the mode showfavoties, I had to base64 encode it, how ever only strings can be base64 encoded. I could not find an existing function that could do this without it "molested" the data by adding other stuff to it. So I created 2 new functions in Addon.

I would really like to utilize existing functions as not to clutter the code.
Code:
def dict_to_string(self, dictionary):
        '''
        This is a convinient way to convert a dictionary to a string. It can
        only be 2 layers deep e.g.
        
        { 'mylist' : ('foo', 'bar', 'chimera'), 'mydict' : { 'key1' : 'key1value',
         'key2' : 'key2value' }, 'myurl' : 'http://xmbx.org' }

        Args:
            dictionary (dict): Each value in the dict can contain one of the
            following: bol, Nontype, str, dict, list, tuple, int. Note that the
            dict in dict, list in dict, tuple in dict's contents must be
            one of the above types, also any int values will have become
            str values, and you need to set it again after using string_to_dict.
            
            See :meth:`string_to_dict` for infomation.
            
        Returns:
            A string in the format &key=value, how ever it's not url safe.
        
        '''
        dictstring = ''
        for key in dictionary:
            string = key
            m = re.match(r'([\d|\w|\s]+)', string)
            if m:
                if type(dictionary[key]) == tuple:
                    tuplestring = '&%s=%s' % (key,'__tuple__/')
                    for value in dictionary[key]:
                        tuplestring = tuplestring + '__%s' % (value)
                    dictstring = dictstring + tuplestring
                elif type(dictionary[key]) == list:
                    liststring = '&%s=%s' % (key,'__list__/')
                    for value in dictionary[key]:
                        liststring = liststring + '__%s' % (value)
                    dictstring = dictstring + liststring
                elif type(dictionary[key]) == dict:
                    dictstring = '&%s=%s' % (key, '__dict__/')
                    for middlekey in dictionary[key]:
                        dictstring = dictstring + '___%s__%s' % \
                        (middlekey, dictionary[key][middlekey])
                    dictstring = dictstring + dictstring
                else:
                    dictstring = dictstring + '&%s=__str__/%s' % \
                    (key, dictionary[key])
        return dictstring
    
    

        
        
    def string_to_dict(self, dictstring):
        '''
        This function rebuilts a dict that was previously turned into a string
        using dict_to_string. Remember as it was turned into a string the
        values and keys are all str now.
        
        Args:
            dictstring (str): A string priveously generated by dict_to_string.
            
            See :meth:`dict_to_string` for further infomation.
        
        Returns:
            A rebuilt dictionary
        '''
        restoreddict = {}
        splitter=re.split('&', dictstring)
        for string in splitter:
            if re.match('.+?__str__/', string):
                key, value = re.split('=__str__/', string)
                restoreddict[key] = value
            elif re.match('.+?__tuple__/', string):
                key, values = re.split('=__tuple__/', string)
                splits = re.split('__', values)
                splits.pop(0)
                tmptuple = tuple(splits)
                restoreddict[key] = tmptuple
            elif re.match('.+?__list__/', string):
                key, values = re.split('=__list__/', string)
                splits = re.split('__', values)
                splits.pop(0)
                restoreddict[key] = splits
            elif re.match('.+?__dict__/', string):
                tmpdict = {}
                key, values = re.split('=__dict__/', string)
                entrypairs = re.split('___', values)
                for pair in entrypairs:
                    if pair:
                        pairkey, pairvalue = re.split('__', pair)
                        tmpdict[pairkey] = pairvalue
                restoreddict[key] = tmpdict
        return restoreddict

Other than that it's looking pretty good now, I'll see if I can quickly make a new video of how it all fits together now that it's a class by itself.


- t0mm0 - 2011-09-05

DragonWin Wrote:Hey,

I have 3 items I'm not satisfied with in the favorite code, and I hope for some input on it.

1) Passing addon.url to ContextMenu
The only way I have found to get hold of the addon.url is to initialize ContextMenu with it as an arg.

Code:
addon = Addon('plugin.video.solarmovie', sys.argv)
cm = ContextMenu(addon.url)
seems ok to me for the moment, maybe even just pass addon as a whole?
DragonWin Wrote:2) Getting hold of the addon's ContextMenu object
In order to utilize the existing code to generate the "Delete favorite" menu, the addon needs to pass the cm object in order to generate the menu.

Code:
addon.show_favorites(cm)
hmmmm, that's where putting the favourites and context menu stuff together falls down a bit isn't it. might have some more thoughts on looking at the code.
DragonWin Wrote:3) A way to make a dict to a string and the other way around
As I need to hide data from xbmc when it's sent to the mode showfavoties, I had to base64 encode it, how ever only strings can be base64 encoded. I could not find an existing function that could do this without it "molested" the data by adding other stuff to it. So I created 2 new functions in Addon.

I would really like to utilize existing functions as not to clutter the code.
yeah, need to lose that stuff Wink

to serialise objects you can use existing stuff like pickle or json for example. but depending on what you actually need to pass around you might be better off just adding the variables needed to create the object again to the query string? or you could use your previous pickle code to save the object to disk and just send a hash so you can retrieve it again?

t0mm0


- DragonWin - 2011-09-05

t0mm0 Wrote:seems ok to me for the moment, maybe even just pass addon as a whole?

I'll change that in the next commit to my branch.

t0mm0 Wrote:hmmmm, that's where putting the favourites and context menu stuff together falls down a bit isn't it. might have some more thoughts on looking at the code.

I have just committed the branch to Github here: Favorite Branch

t0mm0 Wrote:yeah, need to lose that stuff Wink

to serialise objects you can use existing stuff like pickle or json for example. but depending on what you actually need to pass around you might be better off just adding the variables needed to create the object again to the query string? or you could use your previous pickle code to save the object to disk and just send a hash so you can retrieve it again?

t0mm0
I'll take another look at pickle on how I can serialize data so it does not clash. I'll rather try and reuse pickle as it's already imported instead of importing yet another class.

I can't add it to the original query string as that would create clashes with the original string, as some part of the key's are the same. Id' rather not change the key names during the process as it makes it harder to keep track of the data passed around.

The problem is the save data call comes from the context menu when the user clicks the menu, so I don't control when it should be used, as the menu is unique to each item shown. This would create a lot of disk IO (relative of cause), but the XBMC best practice says that disk IO should be kept to a minimum to take into account those who runs if from ssd / flash drives.

[EDIT]
Changed the init function to accept the addon object instead of addon.url

Found pickle.dumps and pickle.loads to encode to a string instead of to a file. I originally did not consider pickle, because when I print the pickled data, I saw line breaks in the pickled data

Still need to find a way around passing the cm module to addon. The parts to concentrate on is show_favorites (line 856 where the cm.add_favorite is used and line 860 and 865 where it's passed to add_item and add_direcotry)
[/EDIT]


- k1m0s - 2011-09-09

Hey t0mm0, Could you tell me if there is a way to add a direct url to an avi file using your test.py as a base? Im making a simple addon without a host page. I manually add megaupl urls to the list.
When I add:
Code:
addon.add_video_item('http://www...com...avi',
                         {'title': 'Whatever'}, 'http://www...com...png')
I get an unable to resolve url error. Plays no prob in vlc, so addresses are good. I assume it automatically checks for a compatible resorse like megaul or 2gbhost and fails without finding one of those addresses. Thanks for any help Smile


- t0mm0 - 2011-09-09

k1m0s Wrote:Hey t0mm0, Could you tell me if there is a way to add a direct url to an avi file using your test.py as a base? Im making a simple addon without a host page. I manually add megaupl urls to the list.
When I add:
Code:
addon.add_video_item('http://www...com...avi',
                         {'title': 'Whatever'}, 'http://www...com...png')
I get an unable to resolve url error. Plays no prob in vlc, so addresses are good. I assume it automatically checks for a compatible resorse like megaul or 2gbhost and fails without finding one of those addresses. Thanks for any help Smile

yes, see the docs for add_video_item()
Quote:resolved (bool): If False (default), play will be sent as a query to the addon when the item is played. If False, play will be treated as a URL to the media item.

this part of the code is currently undergoing some changes though so be prepared for it to break!

but if it is a megaupload url wouldn't you be better off using urlresolver rather than direct links?

on a good note i have another couple of weeks off work so you should see another spike in commits - maybe we can get a release done at last Wink

and DragonWin and Eldorado - i'm not ignoring your code i'll get to it in the next few days (busy over the weekend but will start working on this stuff seriously again after that!)

t0mm0


- k1m0s - 2011-09-10

t0mm0 Wrote:yes, see the docs for add_video_item()


this part of the code is currently undergoing some changes though so be prepared for it to break!

but if it is a megaupload url wouldn't you be better off using urlresolver rather than direct links?

on a good note i have another couple of weeks off work so you should see another spike in commits - maybe we can get a release done at last Wink

and DragonWin and Eldorado - i'm not ignoring your code i'll get to it in the next few days (busy over the weekend but will start working on this stuff seriously again after that!)

t0mm0


Kickass t0mm0 thx, the ones in question are furk, and furk seems to be stable at keeping a direct url to their video files


- DragonWin - 2011-09-12

No problem t0mm0, this is after all an open source project, and not a work we are paid to do Wink

I have not had much time to redo the code that requires the cm to be passed to the mode = showfavorite, the only thing I can see right on the top of my head is to put it in the addon itself, as by design the cm.addcontext etc is build to be used by the addon code, and not the t0mm0.addon module.

Hence why I have not made a pull request yet Wink

I hope I get a chance to look at it further soon.


- DragonWin - 2011-09-12

I really hate to add more "code required in the addon" but the only way I can see right now is to change the showfavorites to some thing like this, where addon.show_favorites returns False if no favorites are found, and returns a list of dicts that can be used to generate the directories / play items.

Code:
elif mode == 'showfavorites':
    favorites = addon.show_favorites()
    if favorites:
        cm.add_favorite('Delete favorite',{ 'mode' : 'deletefavorite'},
                        'deletefavorite', addon.queries['favtype'] )
        for data in favorites:
            if data['callback'] == 'play':
                addon.add_item(data['url'], { 'title' : data['title']},
                              item_type=data['item_type'], cm=cm)
            else:
                addon.add_directory(data['queries'], data['title'], cm=cm)



- anarchintosh - 2011-09-13

hi guys, it's really neat you all actually followed through on devving this! was worried it'd become another cold turkey.

i messed around with making a reCaptcha module https://github.com/anarchintosh/script.module.recaptcha
basically what is needed is an actual patch to XBMC itself to have a 'captcha keyboard view'* as part of xbmcgui.

Because the current method of using a folder image as the captcha image is a horrible hack that many hate.

I suppose it's important because many popular cyberlockers use reCaptcha these days (fileserve/sonic, wupload, hotfile etc)

*ie. a keyboard with a little space for a captcha image above the text box.


- t0mm0 - 2011-09-14

anarchintosh Wrote:hi guys, it's really neat you all actually followed through on devving this! was worried it'd become another cold turkey.

i messed around with making a reCaptcha module https://github.com/anarchintosh/script.module.recaptcha
basically what is needed is an actual patch to XBMC itself to have a 'captcha keyboard view'* as part of xbmcgui.

Because the current method of using a folder image as the captcha image is a horrible hack that many hate.

I suppose it's important because many popular cyberlockers use reCaptcha these days (fileserve/sonic, wupload, hotfile etc)

*ie. a keyboard with a little space for a captcha image above the text box.

cool, thanks anarchintosh! i guess it might be useful to make it more generic at some point so it would work for any captcha not just recaptcha.