Converting a kodi python 2 video addon to a version that works in kodi python 2 and 3
#1
A few notes beforehand:
- big thanks to RomanVM for all the help with the conversion.
- the addon i converted is not very complex, f.e. it doesn't use youtube.
- the conversion was done on a windows 10 machine. For linux/Mac/etc the file paths will be different.
- i use kodi in portable mode. This means it's get started like this: Kodi -p.
- there will be some errors in the log with sha stuff. This prop has to do with python 3 kodi not being entirely correct yet with some linked binaries or something.

Step 1: in my python 2 scripts directory, install the latest version of future: C:\Python27\Scripts>pip install future
Step 2: in my python 2 kodi addon directory: futurize all the .py files with the -w1 parm: f.e. C:\Kodi_18.x_Portable\portable_data\addons\plugin.video.tweakers\resources\lib>futurize -w1 tweakers_search.py
Step 3: added this in my addon.xml:     <import addon="script.module.future"        version="0.0.1"/>
Step 4: check that the addon still works in python 2. It worked.
Step 5: in my python 2 kodi addon directory: futurize all the .py files with the -w2 parm: f.e. C:\Kodi_18.x_Portable\portable_data\addons\plugin.video.tweakers\resources\lib>futurize -w2 tweakers_search.py

For the brave 'n lazy it's also possible to do step 2 thu 5 a bit faster. I made a bat file that should run in the directory where the *.py files are. The content of the bat file:
for %%f in (*.py) do futurize -w0 %%f

Step 6: check that the addon still works in python 2. It didn't work: it threw a error in the logging: UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 3: ordinal not in range(128). 
Step 7: refactoring the logging and the string stuff: every text i scraped from the webpage i converted with the convertToUnicodeString method:

The string stuff was this:

        # Get HTML page
        response = requests.get(self.video_list_page_url, headers=headers)
        # response.status
        html_source = response.text
-->     html_source = html_source.encode('utf-8', 'ignore')
                
and became this:
        # Get HTML page
        response = requests.get(self.video_list_page_url, headers=headers)
        # response.status
        html_source = response.text
-->     html_source = convertToUnicodeString(html_source)      
             

if sys.version_info[0] > 2:
    unicode = str


def convertToUnicodeString(s, encoding='utf-8'):
    """Safe decode byte strings to Unicode"""
    if isinstance(s, bytes):  # This works in Python 2.7 and 3+
        s = s.decode(encoding)
    return s

I also had to remove statements like this one:
        html_source = ''


Logging was this:
            xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (ADDON, VERSION, DATE, "title", str(title)),
                         xbmc.LOGDEBUG)
and became this:
                        log("title", title)
            
def log(name_object, object):
    try:
        xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
            ADDON, VERSION, DATE, name_object, convertToUnicodeString(object)), xbmc.LOGDEBUG)
    except:
        xbmc.log("[ADDON] %s v%s (%s) debug mode, %s = %s" % (
            ADDON, VERSION, DATE, name_object, "Unable to log the object due to an error while converting it to an unicode string"), xbmc.LOGDEBUG)


Step 8: upgraded from beautiful soup 3 to 4. Bs4 can use a parser like html5lib.
In addon.xml: 
    <import addon="script.module.beautifulsoup4"    version="4.5.3"/>
    <import addon="script.module.html5lib"          version="0.999.0"/>.

from bs4 import BeautifulSoup

def getSoup(html,default_parser="html5lib"):
    try:
        soup = BeautifulSoup(html, default_parser)
        log("htmlparser", "html5lib")
    except:
        log("Unexpected error when using " + default_parser + ":", sys.exc_info()[0])
        soup = BeautifulSoup(html,"html.parser")
        log("htmlparser", "html.parser")
    return soup

Later after a comment i realised that this would be better:
def getSoup(html,default_parser="html5lib"):
    soup = BeautifulSoup(html, default_parser)
    return soup

If an error would occur then it means your code and/or addon.xml isn't correct. And that should be fixed.

Also in android the usage of future's old_div caused crashing. Removing old_div and just do regular division, fixes that.

Step 9: zipped my addon and installed in my test python 2 kodo (install from zip). This way the kodi module bs4 and html5lib were downloaded. 
Test 10: Tested my addon in python 2 kodi. It worked. Now it's finally time to try it out on python 3...
Step 11: Installed a path_order_patched Python 3 kodi i got from romanvm. However i have heard now that there is an official python 3 kodi that will work IF the python 3 version of future is used. See at the bottom of this post.
Step 12: zipped my addon and installed it on fresh python 3 kodi (install from zip). This way the dependencies like bs4 and html5lib get downloaded.
Step 13: Tested my addon in python 3 kodi. Starting it with the -p parameter ('Kodi -p') for portable mode. No dice: got a message that future was python 2. 
Step 14: install the python 3 version of future. See the bottom of the pages for file links. 
Step 15: Tested my addon in python 3 kodi. It worked. Rejoice! Time for Beer! or Wine! or some orange juice!
Step 16: Removed the py.bak files from my addon.

Helpfull files:
Official Python 3 kodi: it's in this directory http://mirrors.kodi.tv/test-builds/windows/win32/ and should be called KodiSetup-<something>-feature_python3-x86.exe
Official Python 3 future module: http://www.mediafire.com/file/f4w3ydj1kw...16.0.0.zip. This version however won't work in android. RomanVM made a new version of the future module that should work in py2/3 on Windows/linux/mac/android... This version (0.16.0.4) should be availabe soon in Gotham and higher soonish: https://github.com/xbmc/repo-scripts/pull/761
(THIS FILE IS NOT NEEDED BECAUSE THERE IS A WORKING PYTHON 3 BS4 MODULE THAT WILL BE DOWNLOADED AS A DEPENDENCY: Official Python 3 beautiful soup 4 module: http://www.mediafire.com/file/qr62a49es1...-4.6.0.zip)
My addon before conversion: http://www.mediafire.com/file/9n7kixgq1o...urized.zip
My addon after conversion: http://www.mediafire.com/file/1zdzi4ex2h...urized.zip

I hope this helps with the conversion of addons...
Reply
#2
Just wanted to say thanks for taking time to write this up and sharing you converted Add-on. A about 30-min to a hour I now have my add-on running on both python 2 and 3. the future module was a big help and seeing the correct way to import folder and sub folders.
Shield TV | Windows 10 | Mariadb | Mii Box
Evolve Ecosystem Repo | TV Melodies | Madnox Holiday Mod
Reply
#3
Bump
Reply
#4
Quote:and became this:
# Get HTML page
response = requests.get(self.video_list_page_url, headers=headers)
# response.status
html_source = response.text
--> html_source = convertToUnicodeString(html_source)
response.text always returns Unicode. If you need a raw byte string, use response.content. But it's better to store all you text as Unicode unless some API specifically expects byte strings. And you should never use bare except statements, it just plain wrong.
Reply

Logout Mark Read Team Forum Stats Members Help
Converting a kodi python 2 video addon to a version that works in kodi python 2 and 30