Guest - Testers are needed for the reworked CDateTime core component. See... https://forum.kodi.tv/showthread.php?tid=378981 (September 29) x
Addons development: how does Python handle cookies?
#1
I didn't find any location dedicated to addon development (is there any?).

Anyway, I need some help as I'm making circles since a few weeks now, working on Orange TV France for Kodi. No need to be in France or use Orange to understand this problem.

I'm working on a feature to allow people to use their Orange credentials so then can access TV when they're not at home. This is possible using both their mobile app and their website, so it should be doable. As for the rest of this addon, I'm replicating the behaviour of a web browser in order to do so. There're basically four steps:
  1. Load login page
  2. Post user email
  3. Post user password
  4. Retrieve auth token
Quite easy, but there is a trick: each call sends back a token I have to include in the next one as a cookie. As I'm more fluent in JS, I made a prototype with and I'm able to get the final auth token. So, that's scriptable. But I'm stuck with Kodi Python. Step 2 call keeps responding with 412. I ran a few tests and I'm able to reproduce that issue with Postman and JS: cookie is set in step 2 call, but the value is wrong.

I tried everything I could think of: cookie set into the headers, cookie set using HTTPCookieProcessor and CookieJar, etc. Result is still the same. What am I missing?
Windows 11 x64 | Kodi 21 | Developer of Orange TV France addon
Reply
#2
There is a section for adding development - I’ll move the thread there…
|Banned add-ons (wiki)|Forum rules (wiki)|VPN policy (wiki)|First time user (wiki)|FAQs (wiki) Troubleshooting (wiki)|Add-ons (wiki)|Free content (wiki)|Debug Log (wiki)|

Kodi Blog Posts
Reply
#3
Thanks!
Windows 11 x64 | Kodi 21 | Developer of Orange TV France addon
Reply
#4
My addon handles this by saving the cookie/login cred info that is returned into a json file in the userdata folder for the addon.

Here's a snippet of getting the cookie (yours will obviously be different and depends a lot on the sytem your logging into), I'm using request sessions to handle the request:
python:

with self.session.post(self.config.get('login_url'),verify=False,headers=self.login_headers,data=self.login_form_data) as r:
r.raise_for_status()
if r.ok and isinstance(r.text,str) and 'Successful login' in r.text:
self.logged_in = True
xbmc.log(msg='User is logged in',level=xbmc.LOGDEBUG)
if isinstance(r.cookies.get_dict(),dict) and all([x in r.cookies.get_dict().keys() for x in ['logged-in-sig', 'logged-in-user']]):
user_cookie = {'cookie':r.cookies.get_dict(),'expires':max([x.expires for x in r.cookies]),'domain':next(iter([x.domain for x in r.cookies]),None)}
self.config.files.get('user_cookie').write_text(json.dumps(user_cookie))
xbmc.log(msg='User session cookie set, expires at: {}'.format(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(user_cookie.get('expires')))),level=xbmc.LOGDEBUG)


And the next time the user opens the addon, i see if the cookie json file exists, and load it if the cookie hasn't expired, remove it if it has expired:
python:

def load_previous_cookie(self):
user_cookie = None
if self.config.files.get('user_cookie').exists():
try:
user_cookie = json.loads(self.config.files.get('user_cookie').read_text())
except Exception as exc:
xbmc.log(msg='Cookie read error: {}'.format(exc),level=xbmc.LOGERROR)
if isinstance(user_cookie,dict) and isinstance(user_cookie.get('cookie'),dict) and isinstance(user_cookie.get('expires'),int) and user_cookie.get('expires')>time.time():
for k,v in user_cookie.get('cookie').items():
self.session.cookies.set(k,v,domain=user_cookie.get('domain'))
xbmc.log(msg='Cookie loaded from previous session',level=xbmc.LOGDEBUG)
else:
if self.config.files.get('user_cookie').exists():
self.config.files.get('user_cookie').unlink()
xbmc.log(msg='Cookie deleted from previous session (expired or malformed)',level=xbmc.LOGDEBUG)
Reply
#5
Oh, quite interesting, I never thought I could actually store them into a file.

Where do you get self.session from? What object is it?

Thanks for you help!
Windows 11 x64 | Kodi 21 | Developer of Orange TV France addon
Reply
#6
I think it's a HTTP session object, probably from the Python's 'requests' library that many add-ons use (docs).
It's quite a big library with many dependencies, and I got fed up with the loading time that it added on my slow Android device, so I wrote my own lighter HTTP client with Python's built-in modules, like 'sockets' and 'ssl'.

---------

Many add-ons store data using custom files like zachmorris showed.
Since your usecase is a simple value like a boolean, integer or string, you could also use a hidden add-on setting as storage method. There's less code needed with this method.

For Kodi 18 Leia, you use a setting that has its "visible" attribute set to "false".
XML:
<!-- Using dots in the ID like with "user.cookies" is just a convention. Use anything that XML accepts. -->
<setting id="user.cookies" label="Hidden User Cookies" type="text" visible="false" default="" />
Then to access it:
Python:
from xbmcaddon import Addon
ADDON = Addon()
# Reading it (defaults to the default value set in the XML).
orangeTVCookies = ADDON.getSetting('user.cookies')
# Writing it.
ADDON.setSetting('user.cookies', orangeTVCookies + 'asdf')

For Kodi 19 Matrix and above, you need to use a "Level 4" setting, it's the same as an invisible setting as explained in here: https://kodi.wiki/view/Add-on_settings_conversion#Level

I don't use Kodi 19+ so I defer to MoojMidge's YouTube add-on on how they're defined:
https://github.com/anxdpanic/plugin.vide...ml#L11-L17
(If you search for "level>4" in that file you'll find other hidden settings that they use to store data.)

You can access those level 4 settings in the same way as with Kodi Leia, or also use the specialized functions for each data type (note that Kodi 19 and 20 went through some relevant changes to these functions):  
https://xbmc.github.io/docs.kodi.tv/mast...bd180af650

To "clear" a setting, either delete the entire "settings.xml" file in the /Kodi/userdata/my_addon_folder/ directory, or just set it to a ("") blank string, and react to it being empty in your Python code, like fetching a new cookie or whatever.

Edit: regarding the size limits of the contents that a setting can hold.
The Kodi source code says that it returns a std::string value: https://github.com/xbmc/xbmc/blob/master...#L126-L133
And the maximum size of that comes from string::max_size from C++ (a huge value), so assume that your hidden settings can only be as big as a piece of the device RAM -- lowball it like 32 MB or something (which is quite a big string).
Reply
#7
I finally found a way using HTTPSConnection. At the end of the login process I'm retrieving a few tokens I can finally use to get video streams. I pretty sure I used that before, so I guess I made thing cleaner this time.

I read a lot about that requests library, I didn't know I could actually use it within Kodi. How do I tell Kodi to load a library I would normally fetch with pip?

I never thought about storing data within settings either, that's very cleaver. And I just understood about settings levels. I learnt so much in two posts guys, big thanks, really. That made my day!
Windows 11 x64 | Kodi 21 | Developer of Orange TV France addon
Reply
#8
(2024-09-29, 01:05)BreizhReloaded Wrote: I read a lot about that requests library, I didn't know I could actually use it within Kodi. How do I tell Kodi to load a library I would normally fetch with pip?
You have a bunch of modules that come bundled with the Python interpreter in Kodi already, but for third party libraries, you either need to copy their code files to distribute them with your add-on (literally copying the source directory to your add-on resources, so you can import it from your scripts), or rely on script add-ons that people have created.

For example, Requests is packed as script.module.requests, a code library add-on that must be added as a dependency in your addon.xml manifest so your add-on can "see" it and be able to import its contents, like this:
https://github.com/anxdpanic/plugin.vide....xml#L3-L9
It doesn't come with Kodi out of the box, so when someone installs your add-on, Kodi will try to resolve its dependencies and download them as well, like that Requests script add-on (which depends on other script add-ons too, like script.module.urllib3 etc).

More in here:
https://kodi.wiki/view/Addon.xml#%3Crequires%3E
Reply
#9
(2024-09-29, 01:05)BreizhReloaded Wrote: I never thought about storing data within settings either, that's very cleaver. And I just understood about settings levels. I learnt so much in two posts guys, big thanks, really. That made my day!

One other trick, if you run into cookies or similar that are large or may be hard to store in Kodi settings you can use pickle.  I do it for storing UPnP server data in Kodi settings:

settings with pickle:
def settings(setting, value = None):
    # Get or add addon setting
    if value:
        xbmcaddon.Addon().setSetting(setting, value)
    else:
        return xbmcaddon.Addon().getSetting(setting)  


saved_servers = settings('saved_servers')
if len(saved_servers) < 5 or saved_servers == 'none' or force:
    srvrlist = ssdp.discover("urnConfusedchemas-upnp-org:device:MediaServer:1", timeout=timeoutval)
    servers = onlyDiscMezzmo(srvrlist)
    # save the servers for faster loading
    settings('saved_servers', pickle.dumps(servers,0,fix_imports=True))
else:
    saved_servers = saved_servers.encode('utf-8')
    servers = pickle.loads(saved_servers, fix_imports=True)

Thanks,

Jeff
Running with the Mezzmo Kodi addon.  The easier way to share your media with multiple Kodi clients.
Service.autostop , CBC Sports, Kodi Selective Cleaner and Mezzmo Kodi addon author.
Reply
#10
Wonderful. That will drive me to some code refactoring I guess Big Grin

Thank you for all your inputs!
Windows 11 x64 | Kodi 21 | Developer of Orange TV France addon
Reply

Logout Mark Read Team Forum Stats Members Help
Addons development: how does Python handle cookies?0
This forum uses Lukasz Tkacz MyBB addons.