|
| 1 | +#!/usr/bin/python |
| 2 | +# * |
| 3 | +# * Copyright (C) 2012-2013 Garrett Brown |
| 4 | +# * Copyright (C) 2010 j48antialias |
| 5 | +# * |
| 6 | +# * This Program is free software; you can redistribute it and/or modify |
| 7 | +# * it under the terms of the GNU General Public License as published by |
| 8 | +# * the Free Software Foundation; either version 2, or (at your option) |
| 9 | +# * any later version. |
| 10 | +# * |
| 11 | +# * This Program is distributed in the hope that it will be useful, |
| 12 | +# * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | +# * GNU General Public License for more details. |
| 15 | +# * |
| 16 | +# * You should have received a copy of the GNU General Public License |
| 17 | +# * along with XBMC; see the file COPYING. If not, write to |
| 18 | +# * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
| 19 | +# * http://www.gnu.org/copyleft/gpl.html |
| 20 | +# * |
| 21 | +# * Based on code by j48antialias: |
| 22 | +# * https://anarchintosh-projects.googlecode.com/files/addons_xml_generator.py |
| 23 | + |
| 24 | +""" addons.xml generator """ |
| 25 | + |
| 26 | +import os |
| 27 | +import sys |
| 28 | +import time |
| 29 | +import re |
| 30 | +import xml.etree.ElementTree as ET |
| 31 | +try: |
| 32 | + import shutil, zipfile |
| 33 | +except Exception as e: |
| 34 | + print('An error occurred importing module!\n%s\n' % e) |
| 35 | + |
| 36 | +# Compatibility with 3.0, 3.1 and 3.2 not supporting u"" literals |
| 37 | +print(sys.version) |
| 38 | +if sys.version < '3': |
| 39 | + import codecs |
| 40 | + def u(x): |
| 41 | + return codecs.unicode_escape_decode(x)[0] |
| 42 | +else: |
| 43 | + def u(x): |
| 44 | + return x |
| 45 | + |
| 46 | +class Generator: |
| 47 | + """ |
| 48 | + Generates a new addons.xml file from each addons addon.xml file |
| 49 | + and a new addons.xml.md5 hash file. Must be run from the root of |
| 50 | + the checked-out repo. Only handles single depth folder structure. |
| 51 | + """ |
| 52 | + def __init__(self): |
| 53 | + # generate files |
| 54 | + self._generate_addons_file() |
| 55 | + self._generate_md5_file() |
| 56 | + # notify user |
| 57 | + print("Finished updating addons xml and md5 files\n") |
| 58 | + |
| 59 | + def _generate_addons_file(self): |
| 60 | + # addon list |
| 61 | + addons = os.listdir(".") |
| 62 | + # final addons text |
| 63 | + addons_xml = u("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<addons>\n") |
| 64 | + # loop thru and add each addons addon.xml file |
| 65 | + for addon in addons: |
| 66 | + try: |
| 67 | + # skip any file or .svn folder or .git folder |
| 68 | + if (not os.path.isdir(addon) or addon == ".svn" or addon == ".git" or addon == "zips"): continue |
| 69 | + # create path |
| 70 | + _path = os.path.join(addon, "addon.xml") |
| 71 | + # split lines for stripping |
| 72 | + xml_lines = open(_path, "r").read().splitlines() |
| 73 | + # new addon |
| 74 | + addon_xml = "" |
| 75 | + # loop thru cleaning each line |
| 76 | + for line in xml_lines: |
| 77 | + # skip encoding format line |
| 78 | + if (line.find("<?xml") >= 0): continue |
| 79 | + # add line |
| 80 | + if sys.version < '3': |
| 81 | + addon_xml += unicode(line.rstrip() + "\n", "UTF-8") |
| 82 | + else: |
| 83 | + addon_xml += line.rstrip() + "\n" |
| 84 | + # we succeeded so add to our final addons.xml text |
| 85 | + addons_xml += addon_xml.rstrip() + "\n\n" |
| 86 | + except Exception as e: |
| 87 | + # missing or poorly formatted addon.xml |
| 88 | + print("Excluding %s for %s" % (_path, e)) |
| 89 | + # clean and add closing tag |
| 90 | + addons_xml = addons_xml.strip() + u("\n</addons>\n") |
| 91 | + # save file |
| 92 | + self._save_file(addons_xml.encode("UTF-8"), file="addons.xml") |
| 93 | + |
| 94 | + def _generate_md5_file(self): |
| 95 | + # create a new md5 hash |
| 96 | + try: |
| 97 | + import md5 |
| 98 | + m = md5.new(open("addons.xml", "r").read()).hexdigest() |
| 99 | + except ImportError: |
| 100 | + import hashlib |
| 101 | + m = hashlib.md5(open("addons.xml", "r", encoding="UTF-8").read().encode("UTF-8")).hexdigest() |
| 102 | + |
| 103 | + # save file |
| 104 | + try: |
| 105 | + self._save_file(m.encode("UTF-8"), file="addons.xml.md5") |
| 106 | + except Exception as e: |
| 107 | + # oops |
| 108 | + print("An error occurred creating addons.xml.md5 file!\n%s" % e) |
| 109 | + |
| 110 | + def _save_file(self, data, file): |
| 111 | + try: |
| 112 | + # write data to the file (use b for Python 3) |
| 113 | + open(file, "wb").write(data) |
| 114 | + except Exception as e: |
| 115 | + # oops |
| 116 | + print("An error occurred saving %s file!\n%s" % (file, e)) |
| 117 | + |
| 118 | + |
| 119 | +def zipfolder(foldername, target_dir, zips_dir, addon_dir): |
| 120 | + zipobj = zipfile.ZipFile(zips_dir + foldername, 'w', zipfile.ZIP_DEFLATED) |
| 121 | + rootlen = len(target_dir) + 1 |
| 122 | + for base, dirs, files in os.walk(target_dir): |
| 123 | + for f in files: |
| 124 | + fn = os.path.join(base, f) |
| 125 | + zipobj.write(fn, os.path.join(addon_dir, fn[rootlen:])) |
| 126 | + zipobj.close() |
| 127 | + |
| 128 | + |
| 129 | + |
| 130 | +if (__name__ == "__main__"): |
| 131 | + # start |
| 132 | + Generator() |
| 133 | + |
| 134 | + # rezip files and move |
| 135 | + try: |
| 136 | + print('Starting zip file creation...') |
| 137 | + rootdir = sys.path[0] |
| 138 | + zipsdir = rootdir + os.sep + 'zips' |
| 139 | + filesinrootdir = os.listdir(rootdir) |
| 140 | + |
| 141 | + for x in filesinrootdir: |
| 142 | + if re.search("^(context|plugin|script|service|skin|repository)" , x) and not re.search('.zip', x): |
| 143 | + zipfilename = x + '.zip' |
| 144 | + zipfilenamefirstpart = zipfilename[:-4] |
| 145 | + zipfilenamelastpart = zipfilename[len(zipfilename) - 4:] |
| 146 | + zipsfolder = os.path.normpath(os.path.join('zips', x)) + os.sep |
| 147 | + foldertozip = rootdir + os.sep + x |
| 148 | + filesinfoldertozip = os.listdir(foldertozip) |
| 149 | + # #check if zips folder exists |
| 150 | + if not os.path.exists(zipsfolder): |
| 151 | + os.makedirs(zipsfolder) |
| 152 | + print('Directory doesn\'t exist, creating: ' + zipsfolder) |
| 153 | + # #get addon version number |
| 154 | + if "addon.xml" in filesinfoldertozip: |
| 155 | + tree = ET.parse(os.path.join(rootdir, x, "addon.xml")) |
| 156 | + root = tree.getroot() |
| 157 | + for elem in root.iter('addon'): |
| 158 | + print('%s %s version: %s' % (x, elem.tag, elem.attrib['version'])) |
| 159 | + version = '-' + elem.attrib['version'] |
| 160 | + # #check if and move addon, changelog, fanart and icon to zipdir |
| 161 | + for y in filesinfoldertozip: |
| 162 | + # print('processing file: ' + os.path.join(rootdir,x,y)) |
| 163 | + if re.search("addon|changelog|icon|fanart", y): |
| 164 | + shutil.copyfile(os.path.join(rootdir, x, y), os.path.join(zipsfolder, y)) |
| 165 | + print('Copying %s to %s' % (y, zipsfolder)) |
| 166 | + # #check for and zip the folders |
| 167 | + print('Zipping %s and moving to %s\n' % (x, zipsfolder)) |
| 168 | + zfilename = zipfilenamefirstpart + version + zipfilenamelastpart |
| 169 | + try: |
| 170 | + zipfolder(zfilename, foldertozip, zipsfolder, x) |
| 171 | + print('zipped with zipfolder\n') |
| 172 | + except: |
| 173 | + if os.path.exists(zipsfolder + x + version + '.zip'): |
| 174 | + os.remove(zipsfolder + x + version + '.zip') |
| 175 | + print('trying shutil') |
| 176 | + try: |
| 177 | + shutil.move(shutil.make_archive(foldertozip + version, 'zip', rootdir, x), zipsfolder) |
| 178 | + print('zipped with shutil\n') |
| 179 | + except Exception as e: |
| 180 | + print('Cannot create zip file\nshutil %s\n' % e) |
| 181 | + fpath = os.path.join(rootdir, zipsfolder, zfilename) |
| 182 | + shutil.copyfile(fpath, fpath.replace("zips/","")) |
| 183 | + fpath = fpath.replace(rootdir, "") |
| 184 | + print('Copying .{0} to .{1}'.format(fpath, fpath.replace("zips/",""))) |
| 185 | + except Exception as e: |
| 186 | + print('Cannot create or move the needed files\n%s' % e) |
| 187 | + print('Done') |
0 commit comments