diff --git a/updatelibrary.py b/updatelibrary.py deleted file mode 100644 index b2adf2d..0000000 --- a/updatelibrary.py +++ /dev/null @@ -1,692 +0,0 @@ -#!/usr/bin/python - -# updatelibrary.py Version 1.2 by Rob Knop aka Prospero Frobozz (rknop@pobox.com) - -# Written with some reference to genassets.pl by Illuminous Beltran/IBM, -# as found on the OpenSim wiki. - -# Copyright 2011 Rob Knop (aka Prospero Frobozz). All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. - -# 2. Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials provided -# with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY ROB KNOP ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ROB KNOP OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and documentation -# are those of the authors and should not be interpreted as representing -# official policies, either expressed or implied, of Rob Knop. - -### --- - -# Die wichtigsten **Einstellungsmöglichkeiten** aus dem Skript updatelibrary.py - -# ### **1. Bibliotheksname und Kurzname** -# - **`-n` oder `--lib-name`** → Das ist der Name deiner Bibliothek, der später im OpenSimulator-Viewer angezeigt wird. -# **Beispiel:** -# ```bash -# python updatelibrary.py -n "Meine Bibliothek" -# ``` -# - **`-s` oder `--short-name`** → Eine kompakte Version ohne Leerzeichen, die für Datei- und Ordnernamen genutzt wird. -# **Beispiel:** -# ```bash -# python updatelibrary.py -s "MeineBibliothek" -# ``` - -# ### **2. Inventar zurücksetzen** -# - **`-w` oder `--wipe-inventory`** → Falls du vorherige Inhalte der Bibliothek löschen und von Grund auf neu starten möchtest. -# **Beispiel:** -# ```bash -# python updatelibrary.py -w -# ``` -# _Hinweis:_ Das ist eine sichere Option, aber alle alten Einträge werden entfernt. - -# ### **3. Asset- und Inventar-Verzeichnisse** -# - **`-a` oder `--asset-dir`** → Gibt das Verzeichnis an, wo deine Dateien gespeichert sind (`.j2k`, `.txt`, `.lsl`). -# **Beispiel:** -# ```bash -# python updatelibrary.py -a D:\MeineAssets -# ``` -# - **`-i` oder `--inv-dir`** → Das Zielverzeichnis, in dem die generierten **Inventar-XML-Dateien** gespeichert werden. -# **Beispiel:** -# ```bash -# python updatelibrary.py -i D:\MeineBibliothek -# ``` - -# ### **4. Namen der generierten XML-Dateien** -# - **`--asset-xmlfile`** → Gibt den Namen der Datei an, die alle Assets enthält. -# **Beispiel:** -# ```bash -# python updatelibrary.py --asset-xmlfile MeineBibliothekAssetSet.xml -# ``` -# - **`--folders-xmlfile`** → Name der Datei, die die Ordnerstruktur speichert. -# **Beispiel:** -# ```bash -# python updatelibrary.py --folders-xmlfile MeineBibliothekOrdner.xml -# ``` -# - **`--items-xmlfile`** → Name der Datei, die die einzelnen Inventar-Objekte speichert. -# **Beispiel:** -# ```bash -# python updatelibrary.py --items-xmlfile MeineBibliothekItems.xml -# ``` - -# ### **5. Dateien ignorieren (Filtern)** -# - **`--skip`** → Falls du bestimmte Dateien oder Ordner nicht verarbeiten willst, kannst du dies über eine **Reguläre Ausdrucksregel (Regex)** angeben. -# Standardmäßig ignoriert das Skript `.git`, `.svn` und `.hg`-Ordner. -# **Beispiel:** -# ```bash -# python updatelibrary.py --skip "^backup$" -# ``` -# _Das würde alle Dateien oder Ordner mit dem Namen "backup" ignorieren._ - -# ### **6. Hilfe anzeigen** -# Falls du nicht sicher bist, welche Optionen es noch gibt, kannst du dir die Hilfe ausgeben lassen: -# ```bash -# python updatelibrary.py --help -# ``` - -# **💡 Tipp:** -# Falls du das Skript einfach mit Standardoptionen starten möchtest, reicht dieser Befehl: -# ```bash -# python updatelibrary.py -n "Meine Bibliothek" -s "MeineBibliothek" -a D:\MeineAssets -i D:\MeineBibliothek -# ``` -# Dadurch werden die benötigten XML-Dateien erzeugt, ohne dass du viel konfigurieren musst. - - - - -import os, os.path, sys, stat, shutil, re, uuid -import xml.dom, xml.dom.minidom -import traceback -from optparse import OptionParser - - -opensim_library_root_uuid = "00000112-000f-0000-0000-000100bba000" - -# ********************************************************************** - -class AssetExistsException(Exception): - def __init__(self, value): - self.value = value - def __str__(self, value): - return repr(value) - -class InventoryException(Exception): - def __init__(self, value): - self.value = value - def __str__(self, value): - return repr(value) - -class NiniException(Exception): - def __init__(self, value): - self.value = value - def __str__(self, value): - return repr(value) - -# ********************************************************************** -class NiniSection(object): - def __init__(self, sectionname, keyvalues): - self.sectionname = sectionname - self.keyvalues = keyvalues - - def name(self): - return self.sectionname - - def keyvalues(self): - return self.keyvalues - - def value(self, key): - return self.keyvalues[key] - - def setvalue(self, key, value): - self.keyvalues[key] = value - -# ********************************************************************** - -class NiniThing(object): - def __init__(self, keynames, ninitype): - self.keynames = keynames - self.ninitype = ninitype - - def readXML(self, filename): - sections = [] - - if not os.path.exists(filename): - sys.stderr.write("WARNING: {0} file {1} doesn't exist. Returning empty list.\n". - format(self.ninitype, filename)) - return sections - - xmlblob = xml.dom.minidom.parse(filename) - topelem = xmlblob.documentElement - if topelem.tagName != "Nini": - raise NiniException("Error not a Nini file: {0}".format(filename)) - - for sec in topelem.childNodes: - if sec.nodeType == xml.dom.Node.ELEMENT_NODE: - if sec.tagName != 'Section': - raise NiniException("Error {0} file: expecting a 'Section', found a {1}". - format(self.ninitype), sec.tagName) - - item = NiniSection(sec.getAttribute("Name"), dict()) - for keyname in self.keynames: - item.setvalue(keyname, "") - - for seckey in sec.childNodes: - if seckey.nodeType == xml.dom.Node.ELEMENT_NODE: - if seckey.tagName != "Key": - raise NiniException("Error reading {0}: expecting a 'Key', found a {1}". - format(self.ninitype, seckey.tagName) ) - item.setvalue(seckey.getAttribute("Name"), seckey.getAttribute("Value")) - - sections.append(item) - - xmlblob.unlink() - - return sections - - def writeXML(self, filename, sections): - ofh = open(filename, "w") - - ofh.write("\n") - - for item in sections: - ofh.write("
\n".format(item.name())) - for keyname in self.keynames: - ofh.write(" \n".format(keyname, item.value(keyname))) - ofh.write("
\n\n") - - ofh.write("
\n") - ofh.close() - - -# ********************************************************************** - -class AssetSet(NiniThing): - def __init__(self): - super(AssetSet,self).__init__( [ "assetID", - "name", - "assetType", - "inventoryType", - "fileName" - ], - "Asset Set" ) - self.assets = [] - - def __iter__(self): - return self.assets.__iter__() - - def addasset(self, uuid, name, asstype, invtype, filename): - ass = NiniSection(filename, - { 'assetID': uuid, - 'name': name, - 'assetType': asstype, - 'inventoryType': invtype, - 'fileName': filename } - ) - self.assets.append(ass) - return ass - - def findbyfilename(self, filename): - for ass in self.assets: - if ass.value('fileName') == filename: - return ass - return None - - def findbyuuid(self, uuid): - for ass in self.assets: - if ass.value('assetID') == uuid: - return ass - return None - - def readXML(self, filename): - self.assets = super(AssetSet, self).readXML(filename) - - def writeXML(self, filename): - super(AssetSet, self).writeXML(filename, self.assets) - -# ********************************************************************** - -class InvFolders(NiniThing): - def __init__(self): - super(InvFolders, self).__init__( [ "folderID", - "parentFolderID", - "name", - "type" - ], - "Inventory Folders" ) - self.folders = [] - - def __iter__(self): - return self.folders.__iter__() - - def findbyuuid(self, uuid): - for folder in self.folders: - if folder.value('folderID') == uuid: - return folder - return None - - # NOTE -- findbyname only finds the first folder of - # the given name inside the given parent folder. - # It is possible, if perverse, to have more than - # one folder with the same name... - - def findbyname(self, name, parentid): - for folder in self.folders: - if ( ( folder.value('parentFolderID') == parentid ) and - ( folder.value('name') == name ) ): - return folder - return None - - def ensureexists(self, path, name, parentid): - if ( ( parentid != opensim_library_root_uuid ) and - ( not self.findbyuuid(parentid) ) ): - raise InventoryException("No such parent folder exists: " + str(parentid)) - - folder = self.findbyname(name, parentid) - if folder != None: - return folder - - # HACK ALERT -- I want this "type 6" to become whatever the right - # type is for no icon - # http://opensimulator.org/wiki/AssetServer/DeveloperDocs doesn't tell me enough - - folder = NiniSection(path, - { 'folderID' : uuid.uuid4(), - 'parentFolderID' : parentid, - 'name' : name, - 'type' : -1 - } - ) - self.folders.append(folder) - - return folder - - - def readXML(self, filename): - self.folders = super(InvFolders, self).readXML(filename) - - def writeXML(self, filename): - super(InvFolders, self).writeXML(filename, self.folders) - - - -# ********************************************************************** - -class InvItems(NiniThing): - def __init__(self, folders): - super(InvItems, self).__init__( [ "inventoryID", - "assetID", - "folderID", - "name", - "description", - "assetType", - "inventoryType", - "currentPermissions", - "nextPermissions", - "everyonePermissions", - "basePermissions" - ], - "Inventory Items" ) - self.folders = folders - self.items = [] - - def findbyinvid(self, uuid): - for item in self.items: - if item.value('inventoryID') == uuid: - return item - return None - - # Note -- findbyname only finds the first one of that name - # in that folder. You can have more than one thing of - # the same name in a given folder.... - - def findbyname(self, name, folderid): - for item in self.items: - if ( ( item.value('name') == name ) and - ( item.value('folderID') == folderid) ): - return item - return None - - def ensureexists(self, path, name, assetid, assettype, invtype, folderid): - if ( ( folderid != opensim_library_root_uuid) and - ( not self.folders.findbyuuid(folderid) ) ): - raise InventoryException("No such folder for item: " + str(folderid)) - - for item in self.items: - if ( ( item.value('assetID') == assetid ) and - ( item.value('folderID') == folderid ) and - ( item.value('name') == name ) ): - return item - - item = NiniSection(path, - { 'inventoryID' : uuid.uuid4(), - 'assetID' : assetid, - 'folderID' : folderid, - 'name' : name, - 'description' : "", - 'assetType' : assettype, - 'inventoryType' : invtype, - 'currentPermissions' : "2147483647", - 'nextPermissions' : "2147483647", - 'everyonePermissions' : "2147483647", - 'basePermissions' : "2147483647" - } - ) - self.items.append(item) - - return item - - - def readXML(self, filename): - self.items = super(InvItems, self).readXML(filename) - - def writeXML(self, filename): - super(InvItems, self).writeXML(filename, self.items) - - -# ********************************************************************** - -class LibraryGenerator(object): - def __init__(self, libname, shortname): - self.libname = libname - self.shortname = shortname - self.asset_dir = os.path.abspath(os.path.join("assets", "{0}AssetSet".format(shortname))) - self.inv_dir = os.path.abspath(os.path.join("inventory", shortname)) - self.asset_xmlfile = "{0}AssetSet.xml".format(shortname) - self.folders_xmlfile = "{0}InvFolders.xml".format(shortname) - self.items_xmlfile = "{0}InvItems.xml".format(shortname) - self.skips = [] - self.wipeinv = False - - self.matchj2k = re.compile("\.j2k$") - self.matchtxt = re.compile("\.txt$") - self.matchlsl = re.compile("\.lsl$") - self.endsinslash = re.compile("/$") - self.commentline = re.compile("^\s*#") - self.blankline = re.compile("^\s*$") - self.assetlistline = re.compile("^\s*([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\s*(\d+)\s*(\d+)\s*(.*\S)\s*$") - - def set_skip_patterns(self, skips): - self.skips = skips - - def set_asset_dir(self, asset_dir): - self.asset_dir = asset_dir - - def set_inv_dir(self, inv_dir): - self.inv_dir = inv_dir - - def set_asset_xmlfile(self, asset_xmlfile): - self.asset_xmlfile = asset_xmlfile - - def set_folders_xmlfile(self, folders_xmlfile): - self.folders_xmlfile = folders_xmlfile - - def set_items_xmlfile(self, items_xmlfile): - self.items_xmlfile = items_xmlfile - - def wipe_inventory(self): - self.wipeinv = True - - - def run(self): - # Create our storage for things, and read any current - # XML files if they exist - - self.assets = AssetSet() - self.assets.readXML(os.path.join(self.asset_dir, self.asset_xmlfile)) - - self.folders = InvFolders() - if not self.wipeinv: - self.folders.readXML(os.path.join(self.inv_dir, self.folders_xmlfile)) - - self.items = InvItems(self.folders) - if not self.wipeinv: - self.items.readXML(os.path.join(self.inv_dir, self.items_xmlfile)) - - - # Run the recursive directory processor thingie - - origdir = os.getcwd() - os.chdir(self.asset_dir) # Fehler? - self.process_dir(opensim_library_root_uuid, "", "") - os.chdir(origdir) - - # Write the XML files, saving copies of the old ones out of paranoia - - if os.path.exists(os.path.join(self.asset_dir, self.asset_xmlfile)): - shutil.copy2(os.path.join(self.asset_dir, self.asset_xmlfile), - os.path.join(self.asset_dir, "{0}-old".format(self.asset_xmlfile))) - - self.assets.writeXML(os.path.join(self.asset_dir, self.asset_xmlfile)) - - if os.path.exists(os.path.join(self.inv_dir, self.folders_xmlfile)): - shutil.copy2(os.path.join(self.inv_dir, self.folders_xmlfile), - os.path.join(self.inv_dir, "{0}-old".format(self.folders_xmlfile))) - - self.folders.writeXML(os.path.join(self.inv_dir, self.folders_xmlfile)) - - if os.path.exists(os.path.join(self.inv_dir, self.items_xmlfile)): - shutil.copy2(os.path.join(self.inv_dir, self.items_xmlfile), - os.path.join(self.inv_dir, "{0}-old".format(self.items_xmlfile))) - - self.items.writeXML(os.path.join(self.inv_dir, self.items_xmlfile)) - - - def process_dir(self, parent_uuid, my_name, path): - origdir = os.getcwd() - sys.stderr.write("Processing directory: {0} \n".format(os.path.join(self.asset_dir, path))) - if my_name != "": - os.chdir(my_name) - else: - my_name = self.libname - - # Create the folder XML blob if necessary - - if (path == ""): - curfolder = self.folders.ensureexists(self.libname, my_name, parent_uuid) - else: - curfolder = self.folders.ensureexists(path, my_name, parent_uuid) - my_uuid = curfolder.value("folderID") - - # Walk through this directory and process everything in it - - for fname in os.listdir("."): - statinfo = os.stat(fname) - - doskip = False - for skip in self.skips: - if re.search(skip, fname): - doskip = True - if doskip: continue - - if stat.S_ISDIR(statinfo.st_mode): - self.process_dir(my_uuid, fname, os.path.join(path, fname) ) - continue - - fpath = os.path.join(path, fname) - - if fpath == self.asset_xmlfile: - # Ignore the XML file we're working on! - continue - - elif fname == "addassets.lis": - # Ignore the file specifing pre-existing assets to add - # to inventory - continue - - elif self.matchj2k.search(fname): - asset_type = 0 - elif self.matchtxt.search(fname): - asset_type = 7 - elif self.matchlsl.search(fname): - asset_type = 10 - else: - sys.stderr.write("WARNING: Skipping unrecognized file " + - fname + "\n") - continue - - # Strip the extension for aesthetic purposes - # (Right now, that must be .txt, .lsl, or .j2k, so - # we can assume it's 4 characters long.) - - name = fname[0:-4] - - # Create the asset XML blob if necessary - - ass = self.assets.findbyfilename(fpath) - if ass == None: - ass = self.assets.addasset(uuid.uuid4(), name, asset_type, - asset_type, os.path.join(path, fname)) - - # Create the inventory XML blob if necessary - - self.items.ensureexists(os.path.join(path, name), name, ass.value('assetID'), - asset_type, asset_type, my_uuid) - - # Add pre-existing assets (to inventory only) from file addassets.lis - - if os.path.exists("addassets.lis"): - asslis = open("addassets.lis") - for line in asslis: - line.rstrip("\r\n") - if self.blankline.search(line) or self.commentline.search(line): - continue - match = self.assetlistline.search(line) - if match == None: - sys.stderr.write("WARNING : error parsing line in {0}\n". - format(os.path.join(path, "addassets.lis"))) - sys.stderr.write("Offending line: \"{0}\"\n".format(line)) - continue - self.items.ensureexists(os.path.join(path, match.group(4)), match.group(4), - match.group(1), match.group(2), match.group(3), my_uuid) - asslis.close() - - # We're done; restore the CWD to where we started and return - - os.chdir(origdir) - -# ********************************************************************** - -def main(): - - usage = """ - updatelibrary.py -n "Bibliotheksname" -s Kurzname [Optionen] - - Durchsucht das Verzeichnis mit den Asset-Sets. (Siehe unten, um es festzulegen und die Standardwerte zu erfahren.) - Findet alle .j2k-Bilder, .txt-Dateien und .lsl-Dateien in diesem Verzeichnis und seinen Unterverzeichnissen. - Generiert eine Asset-Set-XML-Datei für alle diese Dateien. - Erstellt außerdem Inventarordner- und Inventar-Item-XML-Dateien für diese Assets, - organisiert in einer Ordnerstruktur, die der Verzeichnisstruktur entspricht. - - Zusätzliche Assets, die bereits im Asset-Speicher des Grids existieren, können in jeder - Verzeichnisdatei namens "addassets.lis" angegeben werden. - - Weitere Dokumentation findest du unter http://opensimulator.org/wiki/Custom_Libraries. - - Nach der Fertigstellung musst du dein Asset-Verzeichnis manuell zur Datei AssetSets.xml von OpenSim hinzufügen - (im "assets"-Ordner unter dem OpenSim-bin-Verzeichnis), sowie deine Inventar-XML-Dateien zur Datei Libraries.xml - (im "inventory"-Ordner unter dem OpenSim-bin-Verzeichnis). - - Geschrieben 2011 von Rob Knop alias Prospero Frobozz. Ich habe dieses Skript nur unter Linux verwendet; - Ich habe keine Ahnung, wie gut es unter Windows funktioniert. Falls du Windows nutzt, empfehle ich http://www.ubuntu.com. - - Führe updatelibrary.py --help aus, um eine Liste der Optionen zu erhalten. -""" - - parser = OptionParser(usage=usage) - - parser.add_option("-n", "--lib-name", action="store", type="string", - dest="libname", default="Neue Bibliothek", - help="Der Name deiner Bibliothek, wie er im Viewer angezeigt wird.") - - parser.add_option("-s", "--short-name", action="store", type="string", - dest="shortname", default="NeueBibliothek", - help="Ein Name ohne Leerzeichen für deine Bibliothek zur Verwendung in Dateinamen.") - - parser.add_option("-w", "--wipe-inventory", action="store_true", default=False, - dest="wipeinv", - help="Inventar zurücksetzen und neu starten? (Sicher! Verwende dies, falls du Elemente entfernt hast.)") - - parser.add_option("-a", "--asset-dir", action="store", - type="string", dest="asset_dir", - default=None, - help="Verzeichnis, in dem sich deine .j2k-, .txt- und .lsl-Dateien befinden und das als Asset-Set in der OpenSim-Bibliothek dient. Standardmäßig: assets/[shortname]AssetSet.") - - parser.add_option("-i", "--inv-dir", action="store", type="string", - dest="inv_dir", default=None, - help="Verzeichnis zum Speichern der Inventar-XML-Dateien. Standardmäßig: inventory/[shortname].") - - parser.add_option("", "--asset-xmlfile", action="store", - type="string", dest="asset_xmlfile", default=None, - help="Name der Asset-XML-Datei. Standardmäßig: [shortname]AssetSet.xml.") - - parser.add_option("", "--folders-xmlfile", action="store", - type="string", dest="folders_xmlfile", default=None, - help="Name der Inventar-Ordner-XML-Datei. Standardmäßig: [shortname]InvFolders.xml.") - - parser.add_option("", "--items-xmlfile", action="store", - type="string", dest="items_xmlfile", default=None, - help="Name der Inventar-Item-XML-Datei. Standardmäßig: [shortname]InvItems.xml.") - - parser.add_option("", "--skip", action="append", type="string", - dest="skip_patterns", - default=['^\\.hg$', '^\\.git$', '^\\.svn$'], - help="Regex für Dateinamen, die nicht verarbeitet werden sollen.") - - if len(sys.argv) < 2: - parser.print_usage() - sys.exit(0) - - options, args = parser.parse_args() - - try: - generator = LibraryGenerator(options.libname, options.shortname) - - if options.skip_patterns: - generator.set_skip_patterns(options.skip_patterns) - - if options.asset_dir is not None: - generator.set_asset_dir(options.asset_dir) - - if options.inv_dir is not None: - generator.set_inv_dir(options.inv_dir) - - if options.asset_xmlfile is not None: - generator.set_asset_xmlfile(options.asset_xmlfile) - - if options.folders_xmlfile is not None: - generator.set_folders_xmlfile(options.folders_xmlfile) - - if options.items_xmlfile is not None: - generator.set_items_xmlfile(options.items_xmlfile) - - if options.wipeinv: - generator.wipe_inventory() - - generator.run() # Fehler? - - except Exception: - traceback.print_exc() - sys.exit(20) - - -if __name__ == "__main__": - sys.exit(main())