← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~felipe-q/openlp/better-remote into lp:openlp

 

Felipe Polo-Wood has proposed merging lp:~felipe-q/openlp/better-remote into lp:openlp.

Requested reviews:
  OpenLP Core (openlp-core)

For more details, see:
https://code.launchpad.net/~felipe-q/openlp/better-remote/+merge/191897

-- Read titles and notes from all presentation controllers (Powerpoint, PowerpointViewer and Impress).
-- Display the slide titles (instead of filename) on the service list, remote and stage
-- Display the notes and thumbnails on the remote

Known limitation, PowerpointViewer will only get titles and notes from .pptx and not .ppt files

-- 
https://code.launchpad.net/~felipe-q/openlp/better-remote/+merge/191897
Your team OpenLP Core is requested to review the proposed merge of lp:~felipe-q/openlp/better-remote into lp:openlp.
=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py	2013-08-31 18:17:38 +0000
+++ openlp/core/lib/serviceitem.py	2013-10-19 06:01:26 +0000
@@ -107,6 +107,16 @@
     ``CanAutoStartForLive``
             The capability to ignore the do not play if display blank flag.
 
+    ``HasDisplayTitle``
+            The item contains 'displaytitle' on every frame which should be
+            preferred over 'title' when displaying the item
+
+    ``HasNotes``
+            The item contains 'notes'
+
+    ``HasThumbnails``
+            The item has related thumbnails available
+
     """
     CanPreview = 1
     CanEdit = 2
@@ -124,6 +134,9 @@
     CanWordSplit = 14
     HasBackgroundAudio = 15
     CanAutoStartForLive = 16
+    HasDisplayTitle = 17
+    HasNotes = 18
+    HasThumbnails = 19
 
 
 class ServiceItem(object):
@@ -303,7 +316,7 @@
         self._raw_frames.append({'title': title, 'raw_slide': raw_slide, 'verseTag': verse_tag})
         self._new_item()
 
-    def add_from_command(self, path, file_name, image):
+    def add_from_command(self, path, file_name, image, displaytitle=None, notes=None):
         """
         Add a slide from a command.
 
@@ -317,7 +330,8 @@
             The command of/for the slide.
         """
         self.service_item_type = ServiceItemType.Command
-        self._raw_frames.append({'title': file_name, 'image': image, 'path': path})
+        self._raw_frames.append({'title': file_name, 'image': image, 'path': path, 
+                                 'displaytitle': displaytitle, 'notes': notes})
         self._new_item()
 
     def get_service_repr(self, lite_save):
@@ -362,7 +376,8 @@
                 service_data = [slide['title'] for slide in self._raw_frames]
         elif self.service_item_type == ServiceItemType.Command:
             for slide in self._raw_frames:
-                service_data.append({'title': slide['title'], 'image': slide['image'], 'path': slide['path']})
+                service_data.append({'title': slide['title'], 'image': slide['image'], 'path': slide['path'], 
+                                        'displaytitle': slide['displaytitle'], 'notes': slide['notes']})
         return {'header': service_header, 'data': service_data}
 
     def set_from_service(self, serviceitem, path=None):
@@ -434,7 +449,8 @@
                     self.title = text_image['title']
                 if path:
                     self.has_original_files = False
-                    self.add_from_command(path, text_image['title'], text_image['image'])
+                    self.add_from_command(path, text_image['title'], text_image['image'], 
+                                          text_image['displaytitle'], text_image['notes'])
                 else:
                     self.add_from_command(text_image['path'], text_image['title'], text_image['image'])
         self._new_item()

=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py	2013-09-07 07:52:52 +0000
+++ openlp/core/ui/servicemanager.py	2013-10-19 06:01:26 +0000
@@ -1185,7 +1185,14 @@
             # Add the children to their parent treewidgetitem.
             for count, frame in enumerate(serviceitem.get_frames()):
                 child = QtGui.QTreeWidgetItem(treewidgetitem)
-                text = frame['title'].replace('\n', ' ')
+                # prefer to use a displaytitle
+                if serviceitem.is_capable(ItemCapabilities.HasDisplayTitle):
+                    text = frame['displaytitle'].replace('\n',' ')
+                    # oops, it is missing, let's make one up
+                    if len(text.strip()) == 0:
+                        text = '[slide ' + str(count+1) + ']'
+                else:
+                    text = frame['title'].replace('\n', ' ')
                 child.setText(0, text[:40])
                 child.setData(0, QtCore.Qt.UserRole, count)
                 if service_item == item_count:

=== modified file 'openlp/plugins/presentations/lib/impresscontroller.py'
--- openlp/plugins/presentations/lib/impresscontroller.py	2013-08-31 18:17:38 +0000
+++ openlp/plugins/presentations/lib/impresscontroller.py	2013-10-19 06:01:26 +0000
@@ -60,7 +60,7 @@
 
 from openlp.core.lib import ScreenList
 from openlp.core.utils import delete_file, get_uno_command, get_uno_instance
-from .presentationcontroller import PresentationController, PresentationDocument
+from .presentationcontroller import PresentationController, PresentationDocument, TextType
 
 
 log = logging.getLogger(__name__)
@@ -252,6 +252,7 @@
         self.presentation.Display = ScreenList().current['number'] + 1
         self.control = None
         self.create_thumbnails()
+        self.create_titles_and_notes()
         return True
 
     def create_thumbnails(self):
@@ -447,9 +448,9 @@
         ``slide_no``
             The slide the notes are required for, starting at 1
         """
-        return self.__get_text_from_page(slide_no, True)
+        return self.__get_text_from_page(slide_no, TextType.Notes)
 
-    def __get_text_from_page(self, slide_no, notes=False):
+    def __get_text_from_page(self, slide_no, text_type=TextType.SlideText):
         """
         Return any text extracted from the presentation page.
 
@@ -459,10 +460,36 @@
         text = ''
         pages = self.document.getDrawPages()
         page = pages.getByIndex(slide_no - 1)
-        if notes:
+        if text_type==TextType.Notes:
             page = page.getNotesPage()
         for index in range(page.getCount()):
             shape = page.getByIndex(index)
+            shapeType = shape.getShapetype()
             if shape.supportsService("com.sun.star.drawing.Text"):
-                text += shape.getString() + '\n'
+                # if they requested title, make sure it is the title
+                if text_type!=TextType.Title or shapeType == "com.sun.star.presentation.TitleTextShape":
+                    text += shape.getString() + '\n'
         return text
+
+    def create_titles_and_notes(self):
+        """
+        Writes the list of titles (one per slide) 
+        to 'titles.txt' 
+        and the notes to 'slideNotes[x].txt'
+        in the thumbnails directory
+        """
+        titles = []
+        pages = self.document.getDrawPages()
+        for slideIndex in range(pages.getCount()):
+            titles.append( self.__get_text_from_page(slideIndex,TextType.Title).replace('\n',' ') + '\n')
+            notes = self.__get_text_from_page(slideIndex,TextType.Notes)
+            if len(notes) > 0:
+                notesfile = os.path.join(self.get_thumbnail_folder(), 'slideNotes%d.txt' % (num))
+                with open(notesfile, mode='w') as fn:
+                    fn.write(notes)
+
+        titlesfile = os.path.join(self.get_thumbnail_folder(), 'titles.txt')
+        with open(titlesfile, mode='w') as fo:
+            fo.writelines(titles)
+        return
+

=== modified file 'openlp/plugins/presentations/lib/mediaitem.py'
--- openlp/plugins/presentations/lib/mediaitem.py	2013-10-02 21:07:20 +0000
+++ openlp/plugins/presentations/lib/mediaitem.py	2013-10-19 06:01:26 +0000
@@ -250,6 +250,7 @@
                 return False
         service_item.processor = self.display_type_combo_box.currentText()
         service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
+        service_item.add_capability(ItemCapabilities.HasThumbnails)
         if not self.display_type_combo_box.currentText():
             return False
         for bitem in items:
@@ -263,13 +264,24 @@
                         return False
                 controller = self.controllers[service_item.processor]
                 doc = controller.add_document(filename)
+                titles, notes = doc.get_titles_and_notes()
+                if len(titles) > 0:
+                    service_item.add_capability(ItemCapabilities.HasDisplayTitle)
+                if len(notes) > 0:
+                    service_item.add_capability(ItemCapabilities.HasNotes)
                 if doc.get_thumbnail_path(1, True) is None:
                     doc.load_presentation()
                 i = 1
                 img = doc.get_thumbnail_path(i, True)
                 if img:
                     while img:
-                        service_item.add_from_command(path, name, img)
+                        title = name
+                        if i <= len(titles):
+                            title = titles[i-1] 
+                        note = ''
+                        if i <= len(notes):
+                            note = notes[i-1]
+                        service_item.add_from_command(path, name, img, title, note)
                         i += 1
                         img = doc.get_thumbnail_path(i, True)
                     doc.close_presentation()

=== modified file 'openlp/plugins/presentations/lib/messagelistener.py'
--- openlp/plugins/presentations/lib/messagelistener.py	2013-08-31 18:17:38 +0000
+++ openlp/plugins/presentations/lib/messagelistener.py	2013-10-19 06:01:26 +0000
@@ -316,7 +316,7 @@
         hide_mode = message[2]
         file = item.get_frame_path()
         self.handler = item.processor
-        if self.handler == self.media_item.Automatic:
+        if self.handler == self.media_item.automatic:
             self.handler = self.media_item.findControllerByType(file)
             if not self.handler:
                 return

=== modified file 'openlp/plugins/presentations/lib/powerpointcontroller.py'
--- openlp/plugins/presentations/lib/powerpointcontroller.py	2013-08-31 18:17:38 +0000
+++ openlp/plugins/presentations/lib/powerpointcontroller.py	2013-10-19 06:01:26 +0000
@@ -132,6 +132,7 @@
             return False
         self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count)
         self.create_thumbnails()
+        self.create_titles_and_notes()
         return True
 
     def create_thumbnails(self):
@@ -316,6 +317,32 @@
         """
         return _get_text_from_shapes(self.presentation.Slides(slide_no).NotesPage.Shapes)
 
+    def create_titles_and_notes(self):
+        """
+        Writes the list of titles (one per slide) 
+        to 'titles.txt' 
+        and the notes to 'slideNotes[x].txt'
+        in the thumbnails directory
+        """
+        titles = []
+        num = 0
+        for slide in self.presentation.Slides:
+            try:
+                text = slide.Shapes.Title.TextFrame.TextRange.Text
+                titles.append(text.replace('\n',' ').replace('\x0b',' ') + '\n')
+                num += 1
+                notes = _get_text_from_shapes(slide.NotesPage.Shapes)
+                if len(notes) > 0:
+                    notesfile = os.path.join(self.get_thumbnail_folder(), 'slideNotes%d.txt' % (num))
+                    with open(notesfile, mode='w') as fn:
+                        fn.write(notes)
+            except Exception as e:
+                log.exception(e)
+                titles.append('\n')
+        titlesfile = os.path.join(self.get_thumbnail_folder(), 'titles.txt')
+        with open(titlesfile, mode='w') as fo:
+            fo.writelines(titles)
+        return
 
 def _get_text_from_shapes(shapes):
     """
@@ -325,8 +352,8 @@
         A set of shapes to search for text.
     """
     text = ''
-    for index in range(shapes.Count):
-        shape = shapes(index + 1)
-        if shape.HasTextFrame:
+    for shape in shapes:
+        if shape.PlaceholderFormat.Type == 2 and shape.HasTextFrame and shape.TextFrame.HasText:
             text += shape.TextFrame.TextRange.Text + '\n'
     return text
+

=== modified file 'openlp/plugins/presentations/lib/pptviewcontroller.py'
--- openlp/plugins/presentations/lib/pptviewcontroller.py	2013-08-31 18:17:38 +0000
+++ openlp/plugins/presentations/lib/pptviewcontroller.py	2013-10-19 06:01:26 +0000
@@ -29,6 +29,7 @@
 
 import os
 import logging
+import zipfile
 
 if os.name == 'nt':
     from ctypes import cdll
@@ -146,6 +147,88 @@
             path = '%s\\slide%s.bmp' % (self.get_temp_folder(), str(idx + 1))
             self.convert_thumbnail(path, idx + 1)
 
+    def create_titles_and_notes(self):
+        """
+        Extracts the titles and notes from the zipped file
+        and writes the list of titles (one per slide) 
+        to 'titles.txt' 
+        and the notes to 'slideNotes[x].txt'
+        in the thumbnails directory
+        """
+        # let's make sure we have a valid zipped presentation
+        if zipfile.is_zipfile(filename):
+            namespaces = {"p": "http://schemas.openxmlformats.org/presentationml/2006/main";, 
+                            "a": "http://schemas.openxmlformats.org/drawingml/2006/main"}
+
+            # open the file
+            with zipfile.ZipFile(filename) as zip_file:
+
+                # find the presentation.xml to get the slide count
+                with zip_file.open('ppt/presentation.xml') as pres:
+                    tree = ElementTree.parse(pres)
+                nodes = tree.getroot().findall(".//p:sldIdLst/p:sldId", namespaces=namespaces)
+                print ("slide count: " + str(len(nodes)))
+
+                # initialize the lists
+                titles = ['' for i in range(len(nodes))]
+                notes = ['' for i in range(len(nodes))]
+
+                # loop thru the file list to find slides and notes
+                for zip_info in zip_file.infolist():
+                    nodeType = ''
+                    index = -1
+                    listToAdd = None
+
+                    # check if it is a slide
+                    match = re.search("slides/slide(.+)\.xml", zip_info.filename)
+                    if match:
+                        index = int(match.group(1))-1
+                        nodeType = 'ctrTitle'
+                        listToAdd = titles
+
+                    # or a note
+                    match = re.search("notesSlides/notesSlide(.+)\.xml", zip_info.filename)
+                    if match:
+                        index = int(match.group(1))-1
+                        nodeType = 'body'
+                        listToAdd = notes
+
+                    # if it is one of our files, index shouldn't be -1
+                    if index >= 0:
+                        with zip_file.open(zip_info) as zipped_file:
+                            tree = ElementTree.parse(zipped_file)
+
+                        text = ''
+                        nodes = tree.getroot().findall(".//p:ph[@type='" + nodeType + "']../../..//p:txBody//a:t", 
+                                                       namespaces=namespaces)
+                        # if we found any content
+                        if nodes and len(nodes)>0:
+                            for node in nodes:
+                                if len(text) > 0:
+                                    text += '\n' 
+                                text += node.text
+                        print( 'slide file: ' + zip_info.filename + ' ' + text )
+                        
+                        # let's remove the \n from the titles and just add one at the end
+                        if nodeType == 'ctrTitle':
+                            text = text.replace('\n',' ').replace('\x0b', ' ') + '\n'
+                        listToAdd[index] = text
+
+                print( titles )
+                print( notes )
+
+        # now let's write the files
+        titlesfile = os.path.join(self.get_thumbnail_folder(), 'titles.txt')
+        with open(titlesfile, mode='w') as fo:
+            fo.writelines(titles)
+        for num in range(len(notes)):
+            notesfile = os.path.join(self.get_thumbnail_folder(), 'slideNotes%d.txt' % (num+1))
+            with open(notesfile, mode='w') as fn:
+                fn.write(notes)
+        return
+
+
+
     def close_presentation(self):
         """
         Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being

=== modified file 'openlp/plugins/presentations/lib/pptviewlib/ppttest.py'
--- openlp/plugins/presentations/lib/pptviewlib/ppttest.py	2013-08-31 18:17:38 +0000
+++ openlp/plugins/presentations/lib/pptviewlib/ppttest.py	2013-10-19 06:01:26 +0000
@@ -28,6 +28,9 @@
 ###############################################################################
 
 import sys
+import zipfile
+import re
+from xml.etree import ElementTree
 from PyQt4 import QtGui, QtCore
 from ctypes import *
 from ctypes.wintypes import RECT
@@ -174,6 +177,51 @@
             int(self.widthEdit.text()), int(self.heightEdit.text()))
         filename = str(self.pptEdit.text().replace('/', '\\'))
         folder = str(self.folderEdit.text().replace('/', '\\'))
+
+        if zipfile.is_zipfile(filename):
+            namespaces = {"p": "http://schemas.openxmlformats.org/presentationml/2006/main";, 
+                            "a": "http://schemas.openxmlformats.org/drawingml/2006/main"}
+            with zipfile.ZipFile(filename) as zip_file:
+                with zip_file.open('ppt/presentation.xml') as pres:
+                    tree = ElementTree.parse(pres)
+                nodes = tree.getroot().findall(".//p:sldIdLst/p:sldId", namespaces=namespaces)
+                print ("slide count: " + str(len(nodes)))
+                titles = [None for i in range(len(nodes))]
+                notes = [None for i in range(len(nodes))]
+
+                for zip_info in zip_file.infolist():
+                    nodeType = ''
+                    index = -1
+                    listToAdd = None
+                    match = re.search("slides/slide(.+)\.xml", zip_info.filename)
+                    if match:
+                        index = int(match.group(1))-1
+                        nodeType = 'ctrTitle'
+                        listToAdd = titles
+                    match = re.search("notesSlides/notesSlide(.+)\.xml", zip_info.filename)
+                    if match:
+                        index = int(match.group(1))-1
+                        nodeType = 'body'
+                        listToAdd = notes
+
+                    if len(nodeType)>0:
+                        with zip_file.open(zip_info) as zipped_file:
+                            tree = ElementTree.parse(zipped_file)
+                        text = ''
+
+                        nodes = tree.getroot().findall(".//p:ph[@type='" + nodeType + "']../../..//p:txBody//a:t", 
+                                                       namespaces=namespaces)
+                        if nodes and len(nodes)>0:
+                            for node in nodes: 
+                                if len(text) > 0:
+                                    text += '\n'
+                                text += node.text 
+                        print( 'slide file: ' + zip_info.filename + ' ' + text )
+                        listToAdd[index] = text
+
+                print( titles )
+                print( notes )
+
         print(filename, folder)
         self.pptid = self.pptdll.OpenPPT(filename, None, rect, folder)
         print('id: ' + str(self.pptid))

=== added file 'openlp/plugins/presentations/lib/pptviewlib/test.pptx'
Binary files openlp/plugins/presentations/lib/pptviewlib/test.pptx	1970-01-01 00:00:00 +0000 and openlp/plugins/presentations/lib/pptviewlib/test.pptx	2013-10-19 06:01:26 +0000 differ
=== modified file 'openlp/plugins/presentations/lib/presentationcontroller.py'
--- openlp/plugins/presentations/lib/presentationcontroller.py	2013-08-31 18:17:38 +0000
+++ openlp/plugins/presentations/lib/presentationcontroller.py	2013-10-19 06:01:26 +0000
@@ -289,6 +289,30 @@
         """
         return ''
 
+    def get_titles_and_notes(self):
+        """
+        Reads the titles from the titles file and 
+        the notes files and returns the contents
+        in a two lists
+        """
+        titles = []
+        notes = []
+        titlesfile = os.path.join(self.get_thumbnail_folder(), 'titles.txt')
+        with open(titlesfile) as fi:
+            titles = fi.readlines()
+        for index in range(len(titles)):
+            notesfile = os.path.join(self.get_thumbnail_folder(), 'slideNotes%d.txt' % (index + 1))
+            note = ''
+            try:
+                if os.path.exists(notesfile):
+                    with open(notesfile) as fn:
+                        note = fn.read()
+            except:
+                note = ''
+            notes.append(note)
+        return titles, notes
+
+
 
 class PresentationController(object):
     """
@@ -435,3 +459,11 @@
         return self._plugin_manager
 
     plugin_manager = property(_get_plugin_manager)
+
+class TextType(object):
+    """
+    Type Enumeration for Types of Text to request
+    """
+    Title = 0
+    SlideText = 1
+    Notes = 2

=== modified file 'openlp/plugins/remotes/html/openlp.js'
--- openlp/plugins/remotes/html/openlp.js	2013-09-14 18:46:49 +0000
+++ openlp/plugins/remotes/html/openlp.js	2013-10-19 06:01:26 +0000
@@ -87,13 +87,21 @@
         var ul = $("#slide-controller > div[data-role=content] > ul[data-role=listview]");
         ul.html("");
         for (idx in data.results.slides) {
-          var text = data.results.slides[idx]["tag"];
+          var slide = data.results.slides[idx];
+          var text = slide["tag"];
           if (text != "") text = text + ": ";
-          text = text + data.results.slides[idx]["text"];
+          if (slide["title"])
+            text += slide["title"]
+          else
+            text += slide["text"];
+          if (slide["notes"])
+            text += ("<div style='font-size:smaller;font-weight:normal'>" + slide["notes"] + "</div>");
           text = text.replace(/\n/g, '<br />');
+          if (slide["img"])
+            text += "<img src='" + slide["img"] + "'>";
           var li = $("<li data-icon=\"false\">").append(
             $("<a href=\"#\">").attr("value", parseInt(idx, 10)).html(text));
-          if (data.results.slides[idx]["selected"]) {
+          if (slide["selected"]) {
             li.attr("data-theme", "e");
           }
           li.children("a").click(OpenLP.setSlide);

=== modified file 'openlp/plugins/remotes/lib/httprouter.py'
--- openlp/plugins/remotes/lib/httprouter.py	2013-09-28 05:10:44 +0000
+++ openlp/plugins/remotes/lib/httprouter.py	2013-10-19 06:01:26 +0000
@@ -125,7 +125,7 @@
 from mako.template import Template
 from PyQt4 import QtCore
 
-from openlp.core.lib import Registry, Settings, PluginStatus, StringContent, image_to_byte
+from openlp.core.lib import Registry, Settings, PluginStatus, StringContent, image_to_byte, resize_image, ItemCapabilities
 from openlp.core.utils import AppLocation, translate
 
 log = logging.getLogger(__name__)
@@ -151,6 +151,7 @@
             ('^/(stage)$', {'function': self.serve_file, 'secure': False}),
             ('^/(main)$', {'function': self.serve_file, 'secure': False}),
             (r'^/files/(.*)$', {'function': self.serve_file, 'secure': False}),
+            (r'^/(.*)/thumbnails/(.*)$', {'function': self.serve_thumbnail, 'secure': False}),
             (r'^/api/poll$', {'function': self.poll, 'secure': False}),
             (r'^/main/poll$', {'function': self.main_poll, 'secure': False}),
             (r'^/main/image$', {'function': self.main_image, 'secure': False}),
@@ -347,12 +348,30 @@
         path = os.path.normpath(os.path.join(self.html_dir, file_name))
         if not path.startswith(self.html_dir):
             return self.do_not_found()
+        html = None
+        if self.send_appropriate_header(file_name) == '.html':
+            variables = self.template_vars
+            html = Template(filename=path, input_encoding='utf-8', output_encoding='utf-8').render(**variables)
+        file_handle = None
+        try:
+            if html:
+                content = html
+            else:
+                file_handle = open(path, 'rb')
+                log.debug('Opened %s' % path)
+                content = file_handle.read()
+        except IOError:
+            log.exception('Failed to open %s' % path)
+            return self.do_not_found()
+        finally:
+            if file_handle:
+                file_handle.close()
+        return content
+
+    def send_appropriate_header(self, file_name):
         ext = os.path.splitext(file_name)[1]
-        html = None
         if ext == '.html':
             self.send_header('Content-type', 'text/html')
-            variables = self.template_vars
-            html = Template(filename=path, input_encoding='utf-8', output_encoding='utf-8').render(**variables)
         elif ext == '.css':
             self.send_header('Content-type', 'text/css')
         elif ext == '.js':
@@ -367,20 +386,24 @@
             self.send_header('Content-type', 'image/png')
         else:
             self.send_header('Content-type', 'text/plain')
-        file_handle = None
-        try:
-            if html:
-                content = html
-            else:
-                file_handle = open(path, 'rb')
-                log.debug('Opened %s' % path)
-                content = file_handle.read()
-        except IOError:
-            log.exception('Failed to open %s' % path)
-            return self.do_not_found()
-        finally:
-            if file_handle:
-                file_handle.close()
+        return ext
+
+    def serve_thumbnail(self, controller_name=None, file_name=None):
+        """
+        Serve an image file. If not found return 404.
+        """
+        log.debug('serve thumbnail %s/thumbnails/%s' % (controller_name, file_name))
+        content = ''
+        full_path = os.path.join(AppLocation.get_section_data_path(controller_name), 
+                                'thumbnails/' + file_name.replace('/','\\') )
+        full_path = urllib.parse.unquote(full_path)
+        
+        if os.path.exists(full_path):
+            self.send_appropriate_header(full_path)
+            file_handle = open(full_path, 'rb')
+            content = file_handle.read()
+        else:
+            content = self.do_not_found()
         return content
 
     def poll(self):
@@ -470,9 +493,20 @@
                     item['html'] = str(frame['html'])
                 else:
                     item['tag'] = str(index + 1)
+                    if current_item.is_capable(ItemCapabilities.HasDisplayTitle):
+                        item['title'] = str(frame['displaytitle'])
+                    if current_item.is_capable(ItemCapabilities.HasNotes):
+                        item['notes'] = str(frame['notes'])
+                    if current_item.is_capable(ItemCapabilities.HasThumbnails):
+                        # if the file is under our app directory tree send the portion after the match
+                        if frame['image'][0:len(AppLocation.get_data_path())] == AppLocation.get_data_path():
+                            item['img'] = frame['image'][len(AppLocation.get_data_path()):]
+                        #'data:image/png;base64,' + str(image_to_byte(resize_image(frame['image'],80,80)))
                     item['text'] = str(frame['title'])
                     item['html'] = str(frame['title'])
                 item['selected'] = (self.live_controller.selected_row == index)
+                if current_item.notes:
+                    item['notes'] = item.get('notes','') + '\n' + current_item.notes
                 data.append(item)
         json_data = {'results': {'slides': data}}
         if current_item:


Follow ups