#!/usr/bin/env python # -*- coding: utf-8 -*- import pygtk pygtk.require('2.0') import gtk import tempfile, zipfile import sys, os, errno, time from lxml import etree import gettext import webbrowser # setup translation support LOCALE_DOMAIN = 'simple-scorm-player' LOCALE_DIR = os.path.join(sys.prefix, 'share', 'locale') gettext.bindtextdomain(LOCALE_DOMAIN, LOCALE_DIR) gettext.textdomain(LOCALE_DOMAIN) gettext.install(LOCALE_DOMAIN) _ = gettext.gettext class SCORMPlayer(): def __init__(self): self.filename = '' self.orig_dir = os.getcwd() self.scorm_dir = os.path.expanduser('~/.scorm') #create ~/.scorm directory if it doesn't exist try: os.makedirs(self.scorm_dir) except OSError as exc: if exc.errno == errno.EEXIST: pass else: raise #prefix temporary dirs tempfile.tempdir = self.scorm_dir self.temp_dir = tempfile.mkdtemp() def select_dialog(self): dialog = gtk.FileChooserDialog(_("Select a SCORM package..."), None, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) dialog.set_default_response(gtk.RESPONSE_OK) filter = gtk.FileFilter() filter.set_name(_("SCORM packages")) filter.add_pattern("*.zip") dialog.add_filter(filter) filter = gtk.FileFilter() filter.set_name(_("All files")) filter.add_pattern("*") dialog.add_filter(filter) response = dialog.run() if response == gtk.RESPONSE_OK: self.filename = dialog.get_filename() dialog.destroy() if response == gtk.RESPONSE_CANCEL: sys.exit(0) def is_SCORM(self): if zipfile.is_zipfile(self.filename): zf = zipfile.ZipFile(self.filename) else: return False return 'imsmanifest.xml' in zf.namelist() #all SCORM must have this file def cancel_dialog(self): dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_YES_NO) dialog.set_markup(_("Selected file is not a SCORM package")) dialog.format_secondary_markup(_("Do you want to try selecting another file?")) response = dialog.run() dialog.destroy() if response == gtk.RESPONSE_NO: sys.exit(0) def select(self): if len(sys.argv)>1: self.filename = sys.argv[1] while True: if self.filename == '': self.select_dialog() while gtk.events_pending(): gtk.main_iteration(block=False) if self.is_SCORM(): return else: self.cancel_dialog() self.filename = '' def extract(self): zf = zipfile.ZipFile(self.filename) zf.extractall(self.temp_dir) os.chdir(self.temp_dir) #eXe SCORM adds this JS library for online LMS but fails in local mode if os.path.exists('APIWrapper.js'): os.remove('APIWrapper.js') def generate_index(self): with open('imsmanifest.xml') as f: doc = etree.parse(f) namespace = doc.getroot().tag[1:].split("}")[0] #extract namespace from xml file #only a simple plain index by now (no hierarchy or sequences) title = doc.find(".//{%s}title" % namespace).text items = doc.findall(".//{%s}item" % namespace) resources = doc.findall(".//{%s}resource" % namespace) if len(resources) == 1: self.target = resources[0].get('href') return #if there's only one resource there's no need to generate an index else: self.target = 'scorm-idx.html' resdict = {} for r in resources: resdict[r.get('identifier')] = r.get('href') first_item = resdict[items[0].get('identifierref')] html_list = '

%s

\n\n' header = ''' %s - Simple SCORM Player %s
''' % (title, _("Menu").encode('utf-8')) footer = '''

%s

''' % (_("Hide the menu").encode('utf-8'), first_item) contents = header+html_list+footer with open('scorm-idx.html','w') as htmlfile: htmlfile.write(contents.encode('utf-8')) def launch(self): webbrowser.open(self.target) def clean(self): os.chdir(self.orig_dir) #check which dirs are older than age secs_per_hour = 3600 numhours = 24 age = numhours * secs_per_hour now = time.time() for r, d, f in os.walk(self.scorm_dir): for dir in d: timestamp = os.path.getmtime(os.path.join(r, dir)) if now - age > timestamp: try: print 'Removing ', os.path.join(r, dir) os.removedirs(os.path.join(r, dir)) except Exception, e: print e pass else: print os.path.join(r, dir), ' removed.' if __name__ == '__main__': player = SCORMPlayer() player.select() player.extract() player.generate_index() player.launch() player.clean() sys.exit(0)