From a08ec0bd2e212e3da7b56b6d02a1920c74535064 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 27 Dec 2010 18:40:52 -0600 Subject: [PATCH] Changing whitespace and fixing a bug with reconfiguring --- src/opt/Nqa-Audiobook-player/Audiobook.py | 304 +++--- src/opt/Nqa-Audiobook-player/Browser.py | 2 +- src/opt/Nqa-Audiobook-player/FileStorage.py | 182 ++-- src/opt/Nqa-Audiobook-player/Gui.py | 1170 +++++++++++----------- src/opt/Nqa-Audiobook-player/Player.py | 365 +++---- src/opt/Nqa-Audiobook-player/SimpleGStreamer.py | 190 ++-- src/opt/Nqa-Audiobook-player/SimpleOSSOPlayer.py | 204 ++-- src/opt/Nqa-Audiobook-player/nqaap_gtk.py | 28 +- support/builddeb.py | 194 ++-- 9 files changed, 1320 insertions(+), 1319 deletions(-) diff --git a/src/opt/Nqa-Audiobook-player/Audiobook.py b/src/opt/Nqa-Audiobook-player/Audiobook.py index 179340f..cd28e1b 100644 --- a/src/opt/Nqa-Audiobook-player/Audiobook.py +++ b/src/opt/Nqa-Audiobook-player/Audiobook.py @@ -10,167 +10,167 @@ _moduleLogger = logging.getLogger(__name__) class Audiobook(object): - def __init__(self, path, current_chapter = 0): - self.title = "" - self._coverPath = "" - self._chapterPaths = [] - self.current_chapter = current_chapter - - if is_playlist_book(path): - self._scan_index(path) - elif is_dir_book(path): - self._scan_dir(path) - elif is_single_chapter(path): - self._scan_chapter(path) - else: - _moduleLogger.info("Audiobook not found in path: " + path) - raise IOError("Audiobook directory not found") - - if len(self._chapterPaths) <= self.current_chapter: - _moduleLogger.warning( - "Audiobook chapter out of range (%s/%s)" % ( - self.current_chapter, len(self._chapterPaths) - ) - ) - self.current_chapter = 0 - - @property - def chapters(self): - return self._chapterPaths - - def get_current_chapter(self): - return self._chapterPaths[self.current_chapter] - - def set_chapter(self, chapter): - if chapter in self.chapters: - self.current_chapter = self.chapters.index(chapter) - else: - raise Exception("Unknown chapter set") - - def get_previous_chapter(self): - """ - @returns the file name for the next chapter, without path - """ - if 0 == self.current_chapter: - return False - else: - self.current_chapter -= 1 - return self._chapterPaths[self.current_chapter] - - def get_next_chapter(self): - """ - @returns the file name for the next chapter, without path - """ - if len(self._chapterPaths) == self.current_chapter: - return False - else: - self.current_chapter += 1 - return self._chapterPaths[self.current_chapter] - - def get_cover_img(self): - if self._coverPath: - return self._coverPath - else: - return "%s/NoCover.png" % os.path.dirname(__file__) - - def _scan_dir(self, root): - self.title = os.path.split(root)[-1] - dirContent = ( - os.path.join(root, f) - for f in os.listdir(root) - if not f.startswith(".") - ) - - files = [ - path - for path in dirContent - if os.path.isfile(os.path.join(root, path)) - ] - - images = [ - path - for path in files - if path.rsplit(".", 1)[-1] in ["png", "gif", "jpg", "jpeg"] - ] - if 0 < len(images): - self._coverPath = images[0] - - self._chapterPaths = [ - path - for path in files - if is_single_chapter(path) - ] - self._chapterPaths.sort() - - def _scan_chapter(self, file): - self._chapterPaths = [file] - - def _scan_playlist(self, file): - root = os.path.dirname(file) - self.title = os.path.basename(file).rsplit(".")[0] - - with open(file, 'r') as f: - for line in f: - if line.startswith("#"): - continue - path = line - if not os.path.isabs(path): - path = os.path.normpath(os.path.join(root, path)) - self._chapterPaths.append(path) - # Not sorting, assuming the file is in the desired order - - def _scan_index(self, file): - import unicodedata - - # Reading file - looking_for_title = False - looking_for_cover = False - looking_for_chapters = False - - with open(file, 'r') as f: - for line in f: - # title - ascii = unicodedata.normalize('NFKD', unicode(line, "latin-1")).encode('ascii', 'ignore') - print line[:-1], "PIC\n" in line, line in "#PIC" - if "#BOOK" in line: - looking_for_title = True - continue - if looking_for_title: - self.title = line[:-1] - looking_for_title = False - if "#PIC" in line: - looking_for_cover = True - continue - if looking_for_cover: - self.cover = line[:-1] - looking_for_cover = False - if "#TRACKS" in line: - looking_for_chapters = True - continue - if looking_for_chapters: - if "#CHAPTERS" in line: - break # no further information needed - self.chapters.append(line.split(':')[0]) + def __init__(self, path, current_chapter = 0): + self.title = "" + self._coverPath = "" + self._chapterPaths = [] + self.current_chapter = current_chapter + + if is_playlist_book(path): + self._scan_index(path) + elif is_dir_book(path): + self._scan_dir(path) + elif is_single_chapter(path): + self._scan_chapter(path) + else: + _moduleLogger.info("Audiobook not found in path: " + path) + raise IOError("Audiobook directory not found") + + if len(self._chapterPaths) <= self.current_chapter: + _moduleLogger.warning( + "Audiobook chapter out of range (%s/%s)" % ( + self.current_chapter, len(self._chapterPaths) + ) + ) + self.current_chapter = 0 + + @property + def chapters(self): + return self._chapterPaths + + def get_current_chapter(self): + return self._chapterPaths[self.current_chapter] + + def set_chapter(self, chapter): + if chapter in self.chapters: + self.current_chapter = self.chapters.index(chapter) + else: + raise Exception("Unknown chapter set") + + def get_previous_chapter(self): + """ + @returns the file name for the next chapter, without path + """ + if 0 == self.current_chapter: + return False + else: + self.current_chapter -= 1 + return self._chapterPaths[self.current_chapter] + + def get_next_chapter(self): + """ + @returns the file name for the next chapter, without path + """ + if len(self._chapterPaths) == self.current_chapter: + return False + else: + self.current_chapter += 1 + return self._chapterPaths[self.current_chapter] + + def get_cover_img(self): + if self._coverPath: + return self._coverPath + else: + return "%s/NoCover.png" % os.path.dirname(__file__) + + def _scan_dir(self, root): + self.title = os.path.split(root)[-1] + dirContent = ( + os.path.join(root, f) + for f in os.listdir(root) + if not f.startswith(".") + ) + + files = [ + path + for path in dirContent + if os.path.isfile(os.path.join(root, path)) + ] + + images = [ + path + for path in files + if path.rsplit(".", 1)[-1] in ["png", "gif", "jpg", "jpeg"] + ] + if 0 < len(images): + self._coverPath = images[0] + + self._chapterPaths = [ + path + for path in files + if is_single_chapter(path) + ] + self._chapterPaths.sort() + + def _scan_chapter(self, file): + self._chapterPaths = [file] + + def _scan_playlist(self, file): + root = os.path.dirname(file) + self.title = os.path.basename(file).rsplit(".")[0] + + with open(file, 'r') as f: + for line in f: + if line.startswith("#"): + continue + path = line + if not os.path.isabs(path): + path = os.path.normpath(os.path.join(root, path)) + self._chapterPaths.append(path) + # Not sorting, assuming the file is in the desired order + + def _scan_index(self, file): + import unicodedata + + # Reading file + looking_for_title = False + looking_for_cover = False + looking_for_chapters = False + + with open(file, 'r') as f: + for line in f: + # title + ascii = unicodedata.normalize('NFKD', unicode(line, "latin-1")).encode('ascii', 'ignore') + print line[:-1], "PIC\n" in line, line in "#PIC" + if "#BOOK" in line: + looking_for_title = True + continue + if looking_for_title: + self.title = line[:-1] + looking_for_title = False + if "#PIC" in line: + looking_for_cover = True + continue + if looking_for_cover: + self.cover = line[:-1] + looking_for_cover = False + if "#TRACKS" in line: + looking_for_chapters = True + continue + if looking_for_chapters: + if "#CHAPTERS" in line: + break # no further information needed + self.chapters.append(line.split(':')[0]) def is_dir_book(path): - return os.path.isdir(path) + return os.path.isdir(path) def is_playlist_book(path): - return path.rsplit(".", 1)[-1] in ["m3u"] + return path.rsplit(".", 1)[-1] in ["m3u"] def is_single_chapter(path): - return path.rsplit(".", 1)[-1] in ["awb", "mp3", "spx", "ogg", "ac3", "wav"] + return path.rsplit(".", 1)[-1] in ["awb", "mp3", "spx", "ogg", "ac3", "wav"] def is_book(path): - if is_dir_book(path): - return True - elif is_playlist_book(path): - return True - elif is_single_chapter(path): - return True - else: - return False + if is_dir_book(path): + return True + elif is_playlist_book(path): + return True + elif is_single_chapter(path): + return True + else: + return False diff --git a/src/opt/Nqa-Audiobook-player/Browser.py b/src/opt/Nqa-Audiobook-player/Browser.py index 36d4e34..31ce7d6 100644 --- a/src/opt/Nqa-Audiobook-player/Browser.py +++ b/src/opt/Nqa-Audiobook-player/Browser.py @@ -2,4 +2,4 @@ import os def open(url): - os.system('dbus-send --system --type=method_call --dest="com.nokia.osso_browser" /com/nokia/osso_browser/request com.nokia.osso_browser.load_url string:"%s"' % url) + os.system('dbus-send --system --type=method_call --dest="com.nokia.osso_browser" /com/nokia/osso_browser/request com.nokia.osso_browser.load_url string:"%s"' % url) diff --git a/src/opt/Nqa-Audiobook-player/FileStorage.py b/src/opt/Nqa-Audiobook-player/FileStorage.py index 8536f8a..af19c9d 100644 --- a/src/opt/Nqa-Audiobook-player/FileStorage.py +++ b/src/opt/Nqa-Audiobook-player/FileStorage.py @@ -13,94 +13,94 @@ _moduleLogger = logging.getLogger(__name__) class FileStorage(object): - def __init__(self, path="~/.SornPlayer/"): - # Setup dir - _moduleLogger.info("init filestorage") - self.path = path - self.books_path = os.path.join(self.path, "books.json") - self.selected = None - self._books = {} - - def load(self): - if not os.path.isdir(self.path): - os.makedirs(self.path) - - try: - with open(self.books_path, "r") as settingsFile: - settings = simplejson.load(settingsFile) - except IOError, e: - _moduleLogger.info("No settings") - settings = {} - except ValueError: - _moduleLogger.info("Settings were corrupt") - settings = {} - - if settings: - self._books = settings["books"] - self.selected = settings["selected"] - else: - _moduleLogger.info("Falling back to old settings format") - self._load_old_settings() - - def save(self): - settings = { - "selected": self.selected, - "books": self._books, - } - with open(self.books_path, "w") as settingsFile: - simplejson.dump(settings, settingsFile) - - def get_selected(self): - """returns the currently selected book""" - return self.selected - - def select_book(self, bookName): - """ Sets the book as the currently playing, and adds it to the - database if it is not already there""" - book_file = os.path.join(self.books_path, bookName) - if bookName not in self._books: - self._books[bookName] = { - "chapter": 0, - "position": 0, - } - - self.selected = bookName - - def set_time(self, chapter, position): - """ Sets the current time for the book that is currently selected""" - bookInfo = self._books[self.selected] - bookInfo["chapter"] = chapter - bookInfo["position"] = position - - def get_time(self): - """Returns the current saved time for the current selected book""" - bookInfo = self._books[self.selected] - return bookInfo["chapter"], bookInfo["position"] - - def _load_old_settings(self): - conf = os.path.join(self.path, "current") - - try: - with open(conf) as f: - self.selected = f.readline() - - books_path = os.path.join(self.path, "books/") - for book in os.listdir(books_path): - book_file = os.path.join(books_path, book) - with open(book_file, 'r') as f: - chapter = int(f.readline()) - position = int(f.readline()) - self._books[book] = { - "chapter": chapter, - "position": position, - } - except IOError, e: - if e.errno == 2: - pass - else: - raise - except OSError, e: - if e.errno == 2: - pass - else: - raise + def __init__(self, path="~/.SornPlayer/"): + # Setup dir + _moduleLogger.info("init filestorage") + self.path = path + self.books_path = os.path.join(self.path, "books.json") + self.selected = None + self._books = {} + + def load(self): + if not os.path.isdir(self.path): + os.makedirs(self.path) + + try: + with open(self.books_path, "r") as settingsFile: + settings = simplejson.load(settingsFile) + except IOError, e: + _moduleLogger.info("No settings") + settings = {} + except ValueError: + _moduleLogger.info("Settings were corrupt") + settings = {} + + if settings: + self._books = settings["books"] + self.selected = settings["selected"] + else: + _moduleLogger.info("Falling back to old settings format") + self._load_old_settings() + + def save(self): + settings = { + "selected": self.selected, + "books": self._books, + } + with open(self.books_path, "w") as settingsFile: + simplejson.dump(settings, settingsFile) + + def get_selected(self): + """returns the currently selected book""" + return self.selected + + def select_book(self, bookName): + """ Sets the book as the currently playing, and adds it to the + database if it is not already there""" + book_file = os.path.join(self.books_path, bookName) + if bookName not in self._books: + self._books[bookName] = { + "chapter": 0, + "position": 0, + } + + self.selected = bookName + + def set_time(self, chapter, position): + """ Sets the current time for the book that is currently selected""" + bookInfo = self._books[self.selected] + bookInfo["chapter"] = chapter + bookInfo["position"] = position + + def get_time(self): + """Returns the current saved time for the current selected book""" + bookInfo = self._books[self.selected] + return bookInfo["chapter"], bookInfo["position"] + + def _load_old_settings(self): + conf = os.path.join(self.path, "current") + + try: + with open(conf) as f: + self.selected = f.readline() + + books_path = os.path.join(self.path, "books/") + for book in os.listdir(books_path): + book_file = os.path.join(books_path, book) + with open(book_file, 'r') as f: + chapter = int(f.readline()) + position = int(f.readline()) + self._books[book] = { + "chapter": chapter, + "position": position, + } + except IOError, e: + if e.errno == 2: + pass + else: + raise + except OSError, e: + if e.errno == 2: + pass + else: + raise diff --git a/src/opt/Nqa-Audiobook-player/Gui.py b/src/opt/Nqa-Audiobook-player/Gui.py index 9eecb2c..5b0ff29 100644 --- a/src/opt/Nqa-Audiobook-player/Gui.py +++ b/src/opt/Nqa-Audiobook-player/Gui.py @@ -15,8 +15,8 @@ import CallMonitor import settings if hildonize.IS_FREMANTLE_SUPPORTED: - # I don't normally do this but I want to error as loudly as possibly when an issue arises - import hildon + # I don't normally do this but I want to error as loudly as possibly when an issue arises + import hildon _moduleLogger = logging.getLogger(__name__) @@ -24,599 +24,599 @@ _moduleLogger = logging.getLogger(__name__) class Gui(object): - # @todo Jump straight to book selection on first launch? - # @todo Absolute increments (+/-5 seconds) - # @todo Show elapsed time / time lef - # @todo Volume control when screen is off - # @todo Variable speed - # http://scaletempo.sourceforge.net/0/task-list.html - # http://www.gstreamer.net/data/doc/gstreamer/head/gst-plugins-bad-plugins/html/gst-plugins-bad-plugins-scaletempo.html - - def __init__(self): - _moduleLogger.info("Starting GUI") - self._clipboard = gtk.clipboard_get() - self._callMonitor = CallMonitor.CallMonitor() - self.__settingsWindow = None - self.__settingsManager = None - self._bookSelection = [] - self._bookSelectionIndex = -1 - self._chapterSelection = [] - self._chapterSelectionIndex = -1 - self._sleepSelection = ["0", "1", "10", "20", "30", "60"] - self._sleepSelectionIndex = 0 - - self.__window_in_fullscreen = False #The window isn't in full screen mode initially. - self.__isPortrait = False - - self.controller = None - self.sleep_timer = None - - # set up gui - self.setup() - self._callMonitor.connect("call_start", self.__on_call_started) - self._callMonitor.start() - - def setup(self): - self._app = hildonize.get_app_class()() - self.win = gtk.Window() - self.win = hildonize.hildonize_window(self._app, self.win) - self.win.set_title(constants.__pretty_app_name__) - - # Cover image - self.cover = gtk.Image() - - # Controls: - - # Label that hold the title of the book,and maybe the chapter - self.title = gtk.Label() - self.title.set_justify(gtk.JUSTIFY_CENTER) - self._set_display_title("Select a book to start listening") - - self.chapter = gtk.Label() - self.chapter.set_justify(gtk.JUSTIFY_CENTER) - - # Seekbar - if hildonize.IS_FREMANTLE_SUPPORTED: - self.seek = hildon.Seekbar() - self.seek.set_range(0.0, 100) - self.seek.set_draw_value(False) - self.seek.set_update_policy(gtk.UPDATE_DISCONTINUOUS) - self.seek.connect('change-value', self.seek_changed) # event - # self.seek.connect('value-changed',self.seek_changed) # event - else: - adjustment = gtk.Adjustment(0, 0, 101, 1, 5, 1) - self.seek = gtk.HScale(adjustment) - self.seek.set_draw_value(False) - self.seek.connect('change-value', self.seek_changed) # event - - # Pause button - if hildonize.IS_FREMANTLE_SUPPORTED: - self.backButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) - image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PREVIOUS, gtk.HILDON_SIZE_FINGER_HEIGHT) - self.backButton.set_image(image) - - self.button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) - - self.forwardButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) - image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_NEXT, gtk.HILDON_SIZE_FINGER_HEIGHT) - self.forwardButton.set_image(image) - else: - self.backButton = gtk.Button(stock=gtk.STOCK_MEDIA_PREVIOUS) - self.button = gtk.Button() - self.forwardButton = gtk.Button(stock=gtk.STOCK_MEDIA_NEXT) - self.set_button_text("Play", "Start playing the audiobook") - self.backButton.connect('clicked', self._on_previous_chapter) - self.button.connect('clicked', self.play_pressed) # event - self.forwardButton.connect('clicked', self._on_next_chapter) - - self._toolbar = gtk.HBox() - self._toolbar.pack_start(self.backButton, False, False, 0) - self._toolbar.pack_start(self.button, True, True, 0) - self._toolbar.pack_start(self.forwardButton, False, False, 0) - - # Box to hold the controls: - self._controlLayout = gtk.VBox() - self._controlLayout.pack_start(gtk.Label(), True, True, 0) - self._controlLayout.pack_start(self.title, False, True, 0) - self._controlLayout.pack_start(self.chapter, False, True, 0) - self._controlLayout.pack_start(gtk.Label(), True, True, 0) - self._controlLayout.pack_start(self.seek, False, True, 0) - self._controlLayout.pack_start(self._toolbar, False, True, 0) - - #Box that divides the layout in two: cover on the lefta - #and controls on the right - self._viewLayout = gtk.HBox() - self._viewLayout.pack_start(self.cover, True, True, 0) - self._viewLayout.add(self._controlLayout) - - self._menuBar = gtk.MenuBar() - self._menuBar.show() - - self._mainLayout = gtk.VBox() - self._mainLayout.pack_start(self._menuBar, False, False, 0) - self._mainLayout.pack_start(self._viewLayout) - - # Add hbox to the window - self.win.add(self._mainLayout) - - #Menu: - # Create menu - self._populate_menu() - - self.win.connect("delete_event", self.quit) # Add shutdown event - self.win.connect("key-press-event", self.on_key_press) - self.win.connect("window-state-event", self._on_window_state_change) - - self.win.show_all() - - # Run update timer - self.setup_timers() - - def _populate_menu(self): - self._menuBar = hildonize.hildonize_menu( - self.win, - self._menuBar, - ) - if hildonize.IS_FREMANTLE_SUPPORTED: - # Create a picker button - self.book_button = hildon.Button(gtk.HILDON_SIZE_AUTO, - hildon.BUTTON_ARRANGEMENT_VERTICAL) - self.book_button.set_title("Audiobook") # Set a title to the button - self.book_button.connect("clicked", self._on_select_audiobook) - - # Create a picker button - self.chapter_button = hildon.Button(gtk.HILDON_SIZE_AUTO, - hildon.BUTTON_ARRANGEMENT_VERTICAL) - self.chapter_button.set_title("Chapter") # Set a title to the button - self.chapter_button.connect("clicked", self._on_select_chapter) - - # Create a picker button - self.sleeptime_button = hildon.Button(gtk.HILDON_SIZE_AUTO, - hildon.BUTTON_ARRANGEMENT_VERTICAL) - self.sleeptime_button.set_title("Sleeptimer") # Set a title to the button - self.sleeptime_button.connect("clicked", self._on_select_sleep) - - settings_button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL) - settings_button.set_label("Settings") - settings_button.connect("clicked", self._on_settings) - - about_button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL) - about_button.set_label("About") - about_button.connect("clicked", self._on_about_activate) - - help_button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL) - help_button.set_label("Help") - help_button.connect("clicked", self.get_help) - - self._menuBar.append(self.book_button) # Add the button to menu - self._menuBar.append(self.chapter_button) # Add the button to menu - self._menuBar.append(self.sleeptime_button) # Add the button to menu - self._menuBar.append(settings_button) - self._menuBar.append(help_button) - self._menuBar.append(about_button) - self._menuBar.show_all() - else: - self._audiobookMenuItem = gtk.MenuItem("Audiobook: ") - self._audiobookMenuItem.connect("activate", self._on_select_audiobook) - - self._chapterMenuItem = gtk.MenuItem("Chapter: ") - self._chapterMenuItem.connect("activate", self._on_select_chapter) - - self._sleepMenuItem = gtk.MenuItem("Sleeptimer: 0") - self._sleepMenuItem.connect("activate", self._on_select_sleep) - - settingsMenuItem = gtk.MenuItem("Settings") - settingsMenuItem.connect("activate", self._on_settings) - - aboutMenuItem = gtk.MenuItem("About") - aboutMenuItem.connect("activate", self._on_about_activate) - - helpMenuItem = gtk.MenuItem("Help") - helpMenuItem.connect("activate", self.get_help) - - booksMenu = gtk.Menu() - booksMenu.append(self._audiobookMenuItem) - booksMenu.append(self._chapterMenuItem) - booksMenu.append(self._sleepMenuItem) - booksMenu.append(settingsMenuItem) - booksMenu.append(helpMenuItem) - booksMenu.append(aboutMenuItem) - - booksMenuItem = gtk.MenuItem("Books") - booksMenuItem.show() - booksMenuItem.set_submenu(booksMenu) - self._menuBar.append(booksMenuItem) - self._menuBar.show_all() - - def setup_timers(self): - self.seek_timer = timeout_add_seconds(3, self.update_seek) - - def save_settings(self): - config = ConfigParser.SafeConfigParser() - self._save_settings(config) - with open(constants._user_settings_, "wb") as configFile: - config.write(configFile) - self.controller.save() - - def _save_settings(self, config): - config.add_section(constants.__pretty_app_name__) - config.set(constants.__pretty_app_name__, "portrait", str(self.__isPortrait)) - config.set(constants.__pretty_app_name__, "fullscreen", str(self.__window_in_fullscreen)) - config.set(constants.__pretty_app_name__, "audiopath", self.controller.get_books_path()) - - def load_settings(self): - config = ConfigParser.SafeConfigParser() - config.read(constants._user_settings_) - self._load_settings(config) - - def _load_settings(self, config): - isPortrait = False - window_in_fullscreen = False - booksPath = constants._default_book_path_ - try: - isPortrait = config.getboolean(constants.__pretty_app_name__, "portrait") - window_in_fullscreen = config.getboolean(constants.__pretty_app_name__, "fullscreen") - booksPath = config.get(constants.__pretty_app_name__, "audiopath") - except ConfigParser.NoSectionError, e: - _moduleLogger.info( - "Settings file %s is missing section %s" % ( - constants._user_settings_, - e.section, - ) - ) - - if isPortrait ^ self.__isPortrait: - if isPortrait: - orientation = gtk.ORIENTATION_VERTICAL - else: - orientation = gtk.ORIENTATION_HORIZONTAL - self.set_orientation(orientation) - - self.__window_in_fullscreen = window_in_fullscreen - if self.__window_in_fullscreen: - self.win.fullscreen() - else: - self.win.unfullscreen() - - self.controller.load(booksPath) - - @staticmethod - def __format_name(path): - if os.path.isfile(path): - return os.path.basename(path).rsplit(".", 1)[0] - else: - return os.path.basename(path) - - @gtk_toolbox.log_exception(_moduleLogger) - def _on_select_audiobook(self, *args): - if not self._bookSelection: - return - index = hildonize.touch_selector( - self.win, - "Audiobook", - (self.__format_name(bookPath) for bookPath in self._bookSelection), - self._bookSelectionIndex if 0 <= self._bookSelectionIndex else 0, - ) - self._bookSelectionIndex = index - bookName = self._bookSelection[index] - self.controller.set_book(bookName) - self.set_button_text("Play", "Start playing the audiobook") # reset button - - @gtk_toolbox.log_exception(_moduleLogger) - def _on_select_chapter(self, *args): - if not self._chapterSelection: - return - index = hildonize.touch_selector( - self.win, - "Chapter", - (self.__format_name(chapterPath) for chapterPath in self._chapterSelection), - self._chapterSelectionIndex if 0 <= self._chapterSelectionIndex else 0, - ) - self._chapterSelectionIndex = index - chapterName = self._chapterSelection[index] - self.controller.set_chapter(chapterName) - self.set_button_text("Play", "Start playing the audiobook") # reset button - - @gtk_toolbox.log_exception(_moduleLogger) - def _on_select_sleep(self, *args): - if self.sleep_timer is not None: - gobject.source_remove(self.sleep_timer) - - try: - index = hildonize.touch_selector( - self.win, - "Sleeptimer", - self._sleepSelection, - self._sleepSelectionIndex if 0 <= self._sleepSelectionIndex else 0, - ) - except RuntimeError: - _moduleLogger.exception("Handling as if user cancelled") - hildonize.show_information_banner(self.win, "Sleep timer canceled") - index = 0 - - self._sleepSelectionIndex = index - sleepName = self._sleepSelection[index] - - time_out = int(sleepName) - if 0 < time_out: - timeout_add_seconds(time_out * 60, self.sleep) - - if hildonize.IS_FREMANTLE_SUPPORTED: - self.sleeptime_button.set_text("Sleeptimer", sleepName) - else: - self._sleepMenuItem.get_child().set_text("Sleeptimer: %s" % (sleepName, )) - - @gtk_toolbox.log_exception(_moduleLogger) - def __on_call_started(self, callMonitor): - self.pause() - - @gtk_toolbox.log_exception(_moduleLogger) - def _on_settings(self, *args): - if self.__settingsWindow is None: - vbox = gtk.VBox() - self.__settingsManager = settings.SettingsDialog(vbox) - - self.__settingsWindow = gtk.Window() - self.__settingsWindow.add(vbox) - self.__settingsWindow = hildonize.hildonize_window(self._app, self.__settingsWindow) - self.__settingsManager.window = self.__settingsWindow - - self.__settingsWindow.set_title("Settings") - self.__settingsWindow.set_transient_for(self.win) - self.__settingsWindow.set_default_size(*self.win.get_size()) - self.__settingsWindow.connect("delete-event", self._on_settings_delete) - self.__settingsManager.set_portrait_state(self.__isPortrait) - self.__settingsManager.set_audiobook_path(self.controller.get_books_path()) - self.__settingsWindow.set_modal(True) - self.__settingsWindow.show_all() - - @gtk_toolbox.log_exception(_moduleLogger) - def _on_settings_delete(self, *args): - self.__settingsWindow.emit_stop_by_name("delete-event") - self.__settingsWindow.hide() - self.__settingsWindow.set_modal(False) - - isPortrait = self.__settingsManager.is_portrait() - if isPortrait ^ self.__isPortrait: - if isPortrait: - orientation = gtk.ORIENTATION_VERTICAL - else: - orientation = gtk.ORIENTATION_HORIZONTAL - self.set_orientation(orientation) - if self.__settingsManager.get_audiobook_path() != self.controller.get_books_path(): - self.controller.reload(self.__settingsManager.get_audiobook_path()) - - return True - - @gtk_toolbox.log_exception(_moduleLogger) - def update_seek(self): - #print self.controller.get_percentage() - if self.controller.is_playing(): - gtk.gdk.threads_enter() - self.seek.set_value(self.controller.get_percentage() * 100) - gtk.gdk.threads_leave() - #self.controller.get_percentage() - return True # run again - - @gtk_toolbox.log_exception(_moduleLogger) - def sleep(self): - _moduleLogger.info("sleep time timeout") - hildonize.show_information_banner(self.win, "Sleep timer") - self.controller.stop() - self.set_button_text("Resume", "Resume playing the audiobook") - return False # do not repeat - - @gtk_toolbox.log_exception(_moduleLogger) - def get_help(self, button): - Browser.open("file:///opt/Nqa-Audiobook-player/Help/nqaap.html") - - @gtk_toolbox.log_exception(_moduleLogger) - def seek_changed(self, seek, scroll , value): - # print "sok", scroll - self.controller.seek_percent(seek.get_value() / 100.0) - - @gtk_toolbox.log_exception(_moduleLogger) - def _on_next_chapter(self, *args): - self.controller.next_chapter() - - @gtk_toolbox.log_exception(_moduleLogger) - def _on_previous_chapter(self, *args): - self.controller.previous_chapter() - - @gtk_toolbox.log_exception(_moduleLogger) - def play_pressed(self, button): - if self.controller.is_playing(): - self.pause() - else: - self.play() - - @gtk_toolbox.log_exception(_moduleLogger) - def on_key_press(self, widget, event, *args): - RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter) - isCtrl = bool(event.get_state() & gtk.gdk.CONTROL_MASK) - if ( - event.keyval == gtk.keysyms.F6 or - event.keyval in RETURN_TYPES and isCtrl - ): - # The "Full screen" hardware key has been pressed - if self.__window_in_fullscreen: - self.win.unfullscreen () - else: - self.win.fullscreen () - return True - elif event.keyval == gtk.keysyms.o and isCtrl: - self._toggle_rotate() - return True - elif ( - event.keyval in (gtk.keysyms.w, gtk.keysyms.q) and - event.get_state() & gtk.gdk.CONTROL_MASK - ): - self.quit() - elif event.keyval == gtk.keysyms.l and event.get_state() & gtk.gdk.CONTROL_MASK: - with open(constants._user_logpath_, "r") as f: - logLines = f.xreadlines() - log = "".join(logLines) - self._clipboard.set_text(str(log)) - return True - elif event.keyval in RETURN_TYPES: - if self.controller.is_playing(): - self.pause() - else: - self.play() - return True - elif event.keyval == gtk.keysyms.Left: - self.controller.previous_chapter() - return True - elif event.keyval == gtk.keysyms.Right: - self.controller.next_chapter() - return True - - @gtk_toolbox.log_exception(_moduleLogger) - def _on_window_state_change(self, widget, event, *args): - if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN: - self.__window_in_fullscreen = True - else: - self.__window_in_fullscreen = False - - @gtk_toolbox.log_exception(_moduleLogger) - def quit(self, *args): # what are the arguments? - _moduleLogger.info("Shutting down") - try: - self.save_settings() - self.controller.stop() # to save the state - finally: - gtk.main_quit() - - @gtk_toolbox.log_exception(_moduleLogger) - def _on_about_activate(self, *args): - dlg = gtk.AboutDialog() - dlg.set_name(constants.__pretty_app_name__) - dlg.set_version("%s-%d" % (constants.__version__, constants.__build__)) - dlg.set_copyright("Copyright 2010") - dlg.set_comments("") - dlg.set_website("http://nqaap.garage.maemo.org/") - dlg.set_authors(["Pengman ", "Ed Page "]) - dlg.run() - dlg.destroy() - - # Actions: - - def play(self): - self.set_button_text("Stop", "Stop playing the audiobook") - self.controller.play() - - def pause(self): - self.set_button_text("Resume", "Resume playing the audiobook") - self.controller.stop() - - def set_orientation(self, orientation): - if orientation == gtk.ORIENTATION_VERTICAL: - if self.__isPortrait: - return - hildonize.window_to_portrait(self.win) - self.__isPortrait = True - - self._viewLayout.remove(self._controlLayout) - self._mainLayout.add(self._controlLayout) - elif orientation == gtk.ORIENTATION_HORIZONTAL: - if not self.__isPortrait: - return - hildonize.window_to_landscape(self.win) - self.__isPortrait = False - - self._mainLayout.remove(self._controlLayout) - self._viewLayout.add(self._controlLayout) - else: - raise NotImplementedError(orientation) - - def get_orientation(self): - return gtk.ORIENTATION_VERTICAL if self.__isPortrait else gtk.ORIENTATION_HORIZONTAL - - def _toggle_rotate(self): - if self.__isPortrait: - self.set_orientation(gtk.ORIENTATION_HORIZONTAL) - else: - self.set_orientation(gtk.ORIENTATION_VERTICAL) - - def change_chapter(self, chapterName): - if chapterName is None: - _moduleLogger.debug("chapter selection canceled.") - return True # this should end the function and indicate it has been handled - - _moduleLogger.debug("chapter changed (by controller) to: %s" % chapterName) - - def set_button_text(self, title, text): - if hildonize.IS_FREMANTLE_SUPPORTED: - self.button.set_text(title, text) - else: - self.button.set_label("%s" % (title, )) - - def set_books(self, books): - _moduleLogger.debug("new books") - del self._bookSelection[:] - self._bookSelection.extend(books) - if len(books) == 0 and self.controller is not None: - hildonize.show_information_banner(self.win, "No audiobooks found. \nPlease place your audiobooks in the directory %s" % self.controller.get_books_path()) - - def set_book(self, bookPath, cover): - bookName = self.__format_name(bookPath) - - self.set_button_text("Play", "Start playing the audiobook") # reset button - self._set_display_title(bookName) - if hildonize.IS_FREMANTLE_SUPPORTED: - self.book_button.set_text("Audiobook", bookName) - else: - self._audiobookMenuItem.get_child().set_text("Audiobook: %s" % (bookName, )) - if cover != "": - self.cover.set_from_file(cover) - - def set_chapter(self, chapterIndex): - ''' - Called from controller whenever a new chapter is started - - chapter parameter is supposed to be the index for the chapter, not the name - ''' - self.auto_chapter_selected = True - self._set_display_chapter(str(chapterIndex + 1)) - if hildonize.IS_FREMANTLE_SUPPORTED: - self.chapter_button.set_text("Chapter", str(chapterIndex)) - else: - self._chapterMenuItem.get_child().set_text("Chapter: %s" % (chapterIndex+1, )) - - def set_chapters(self, chapters): - _moduleLogger.debug("setting chapters" ) - del self._chapterSelection[:] - self._chapterSelection.extend(chapters) - - def set_sleep_timer(self, mins): - pass - - # Utils - def set_selected_value(self, button, value): - i = button.get_selector().get_model(0).index[value] # get index of value from list - button.set_active(i) # set active index to that index - - def _set_display_title(self, title): - self.title.set_markup("%s" % title) - - def _set_display_chapter(self, chapter): - self.chapter.set_markup("Chapter %s" % chapter) + # @todo Jump straight to book selection on first launch? + # @todo Absolute increments (+/-5 seconds) + # @todo Show elapsed time / time lef + # @todo Volume control when screen is off + # @todo Variable speed + # http://scaletempo.sourceforge.net/0/task-list.html + # http://www.gstreamer.net/data/doc/gstreamer/head/gst-plugins-bad-plugins/html/gst-plugins-bad-plugins-scaletempo.html + + def __init__(self): + _moduleLogger.info("Starting GUI") + self._clipboard = gtk.clipboard_get() + self._callMonitor = CallMonitor.CallMonitor() + self.__settingsWindow = None + self.__settingsManager = None + self._bookSelection = [] + self._bookSelectionIndex = -1 + self._chapterSelection = [] + self._chapterSelectionIndex = -1 + self._sleepSelection = ["0", "1", "10", "20", "30", "60"] + self._sleepSelectionIndex = 0 + + self.__window_in_fullscreen = False #The window isn't in full screen mode initially. + self.__isPortrait = False + + self.controller = None + self.sleep_timer = None + + # set up gui + self.setup() + self._callMonitor.connect("call_start", self.__on_call_started) + self._callMonitor.start() + + def setup(self): + self._app = hildonize.get_app_class()() + self.win = gtk.Window() + self.win = hildonize.hildonize_window(self._app, self.win) + self.win.set_title(constants.__pretty_app_name__) + + # Cover image + self.cover = gtk.Image() + + # Controls: + + # Label that hold the title of the book,and maybe the chapter + self.title = gtk.Label() + self.title.set_justify(gtk.JUSTIFY_CENTER) + self._set_display_title("Select a book to start listening") + + self.chapter = gtk.Label() + self.chapter.set_justify(gtk.JUSTIFY_CENTER) + + # Seekbar + if hildonize.IS_FREMANTLE_SUPPORTED: + self.seek = hildon.Seekbar() + self.seek.set_range(0.0, 100) + self.seek.set_draw_value(False) + self.seek.set_update_policy(gtk.UPDATE_DISCONTINUOUS) + self.seek.connect('change-value', self.seek_changed) # event + # self.seek.connect('value-changed',self.seek_changed) # event + else: + adjustment = gtk.Adjustment(0, 0, 101, 1, 5, 1) + self.seek = gtk.HScale(adjustment) + self.seek.set_draw_value(False) + self.seek.connect('change-value', self.seek_changed) # event + + # Pause button + if hildonize.IS_FREMANTLE_SUPPORTED: + self.backButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) + image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PREVIOUS, gtk.HILDON_SIZE_FINGER_HEIGHT) + self.backButton.set_image(image) + + self.button = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) + + self.forwardButton = hildon.Button(gtk.HILDON_SIZE_AUTO_WIDTH | gtk.HILDON_SIZE_FINGER_HEIGHT, hildon.BUTTON_ARRANGEMENT_VERTICAL) + image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_NEXT, gtk.HILDON_SIZE_FINGER_HEIGHT) + self.forwardButton.set_image(image) + else: + self.backButton = gtk.Button(stock=gtk.STOCK_MEDIA_PREVIOUS) + self.button = gtk.Button() + self.forwardButton = gtk.Button(stock=gtk.STOCK_MEDIA_NEXT) + self.set_button_text("Play", "Start playing the audiobook") + self.backButton.connect('clicked', self._on_previous_chapter) + self.button.connect('clicked', self.play_pressed) # event + self.forwardButton.connect('clicked', self._on_next_chapter) + + self._toolbar = gtk.HBox() + self._toolbar.pack_start(self.backButton, False, False, 0) + self._toolbar.pack_start(self.button, True, True, 0) + self._toolbar.pack_start(self.forwardButton, False, False, 0) + + # Box to hold the controls: + self._controlLayout = gtk.VBox() + self._controlLayout.pack_start(gtk.Label(), True, True, 0) + self._controlLayout.pack_start(self.title, False, True, 0) + self._controlLayout.pack_start(self.chapter, False, True, 0) + self._controlLayout.pack_start(gtk.Label(), True, True, 0) + self._controlLayout.pack_start(self.seek, False, True, 0) + self._controlLayout.pack_start(self._toolbar, False, True, 0) + + #Box that divides the layout in two: cover on the lefta + #and controls on the right + self._viewLayout = gtk.HBox() + self._viewLayout.pack_start(self.cover, True, True, 0) + self._viewLayout.add(self._controlLayout) + + self._menuBar = gtk.MenuBar() + self._menuBar.show() + + self._mainLayout = gtk.VBox() + self._mainLayout.pack_start(self._menuBar, False, False, 0) + self._mainLayout.pack_start(self._viewLayout) + + # Add hbox to the window + self.win.add(self._mainLayout) + + #Menu: + # Create menu + self._populate_menu() + + self.win.connect("delete_event", self.quit) # Add shutdown event + self.win.connect("key-press-event", self.on_key_press) + self.win.connect("window-state-event", self._on_window_state_change) + + self.win.show_all() + + # Run update timer + self.setup_timers() + + def _populate_menu(self): + self._menuBar = hildonize.hildonize_menu( + self.win, + self._menuBar, + ) + if hildonize.IS_FREMANTLE_SUPPORTED: + # Create a picker button + self.book_button = hildon.Button(gtk.HILDON_SIZE_AUTO, + hildon.BUTTON_ARRANGEMENT_VERTICAL) + self.book_button.set_title("Audiobook") # Set a title to the button + self.book_button.connect("clicked", self._on_select_audiobook) + + # Create a picker button + self.chapter_button = hildon.Button(gtk.HILDON_SIZE_AUTO, + hildon.BUTTON_ARRANGEMENT_VERTICAL) + self.chapter_button.set_title("Chapter") # Set a title to the button + self.chapter_button.connect("clicked", self._on_select_chapter) + + # Create a picker button + self.sleeptime_button = hildon.Button(gtk.HILDON_SIZE_AUTO, + hildon.BUTTON_ARRANGEMENT_VERTICAL) + self.sleeptime_button.set_title("Sleeptimer") # Set a title to the button + self.sleeptime_button.connect("clicked", self._on_select_sleep) + + settings_button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL) + settings_button.set_label("Settings") + settings_button.connect("clicked", self._on_settings) + + about_button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL) + about_button.set_label("About") + about_button.connect("clicked", self._on_about_activate) + + help_button = hildon.Button(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_VERTICAL) + help_button.set_label("Help") + help_button.connect("clicked", self.get_help) + + self._menuBar.append(self.book_button) # Add the button to menu + self._menuBar.append(self.chapter_button) # Add the button to menu + self._menuBar.append(self.sleeptime_button) # Add the button to menu + self._menuBar.append(settings_button) + self._menuBar.append(help_button) + self._menuBar.append(about_button) + self._menuBar.show_all() + else: + self._audiobookMenuItem = gtk.MenuItem("Audiobook: ") + self._audiobookMenuItem.connect("activate", self._on_select_audiobook) + + self._chapterMenuItem = gtk.MenuItem("Chapter: ") + self._chapterMenuItem.connect("activate", self._on_select_chapter) + + self._sleepMenuItem = gtk.MenuItem("Sleeptimer: 0") + self._sleepMenuItem.connect("activate", self._on_select_sleep) + + settingsMenuItem = gtk.MenuItem("Settings") + settingsMenuItem.connect("activate", self._on_settings) + + aboutMenuItem = gtk.MenuItem("About") + aboutMenuItem.connect("activate", self._on_about_activate) + + helpMenuItem = gtk.MenuItem("Help") + helpMenuItem.connect("activate", self.get_help) + + booksMenu = gtk.Menu() + booksMenu.append(self._audiobookMenuItem) + booksMenu.append(self._chapterMenuItem) + booksMenu.append(self._sleepMenuItem) + booksMenu.append(settingsMenuItem) + booksMenu.append(helpMenuItem) + booksMenu.append(aboutMenuItem) + + booksMenuItem = gtk.MenuItem("Books") + booksMenuItem.show() + booksMenuItem.set_submenu(booksMenu) + self._menuBar.append(booksMenuItem) + self._menuBar.show_all() + + def setup_timers(self): + self.seek_timer = timeout_add_seconds(3, self.update_seek) + + def save_settings(self): + config = ConfigParser.SafeConfigParser() + self._save_settings(config) + with open(constants._user_settings_, "wb") as configFile: + config.write(configFile) + self.controller.save() + + def _save_settings(self, config): + config.add_section(constants.__pretty_app_name__) + config.set(constants.__pretty_app_name__, "portrait", str(self.__isPortrait)) + config.set(constants.__pretty_app_name__, "fullscreen", str(self.__window_in_fullscreen)) + config.set(constants.__pretty_app_name__, "audiopath", self.controller.get_books_path()) + + def load_settings(self): + config = ConfigParser.SafeConfigParser() + config.read(constants._user_settings_) + self._load_settings(config) + + def _load_settings(self, config): + isPortrait = False + window_in_fullscreen = False + booksPath = constants._default_book_path_ + try: + isPortrait = config.getboolean(constants.__pretty_app_name__, "portrait") + window_in_fullscreen = config.getboolean(constants.__pretty_app_name__, "fullscreen") + booksPath = config.get(constants.__pretty_app_name__, "audiopath") + except ConfigParser.NoSectionError, e: + _moduleLogger.info( + "Settings file %s is missing section %s" % ( + constants._user_settings_, + e.section, + ) + ) + + if isPortrait ^ self.__isPortrait: + if isPortrait: + orientation = gtk.ORIENTATION_VERTICAL + else: + orientation = gtk.ORIENTATION_HORIZONTAL + self.set_orientation(orientation) + + self.__window_in_fullscreen = window_in_fullscreen + if self.__window_in_fullscreen: + self.win.fullscreen() + else: + self.win.unfullscreen() + + self.controller.load(booksPath) + + @staticmethod + def __format_name(path): + if os.path.isfile(path): + return os.path.basename(path).rsplit(".", 1)[0] + else: + return os.path.basename(path) + + @gtk_toolbox.log_exception(_moduleLogger) + def _on_select_audiobook(self, *args): + if not self._bookSelection: + return + index = hildonize.touch_selector( + self.win, + "Audiobook", + (self.__format_name(bookPath) for bookPath in self._bookSelection), + self._bookSelectionIndex if 0 <= self._bookSelectionIndex else 0, + ) + self._bookSelectionIndex = index + bookName = self._bookSelection[index] + self.controller.set_book(bookName) + self.set_button_text("Play", "Start playing the audiobook") # reset button + + @gtk_toolbox.log_exception(_moduleLogger) + def _on_select_chapter(self, *args): + if not self._chapterSelection: + return + index = hildonize.touch_selector( + self.win, + "Chapter", + (self.__format_name(chapterPath) for chapterPath in self._chapterSelection), + self._chapterSelectionIndex if 0 <= self._chapterSelectionIndex else 0, + ) + self._chapterSelectionIndex = index + chapterName = self._chapterSelection[index] + self.controller.set_chapter(chapterName) + self.set_button_text("Play", "Start playing the audiobook") # reset button + + @gtk_toolbox.log_exception(_moduleLogger) + def _on_select_sleep(self, *args): + if self.sleep_timer is not None: + gobject.source_remove(self.sleep_timer) + + try: + index = hildonize.touch_selector( + self.win, + "Sleeptimer", + self._sleepSelection, + self._sleepSelectionIndex if 0 <= self._sleepSelectionIndex else 0, + ) + except RuntimeError: + _moduleLogger.exception("Handling as if user cancelled") + hildonize.show_information_banner(self.win, "Sleep timer canceled") + index = 0 + + self._sleepSelectionIndex = index + sleepName = self._sleepSelection[index] + + time_out = int(sleepName) + if 0 < time_out: + timeout_add_seconds(time_out * 60, self.sleep) + + if hildonize.IS_FREMANTLE_SUPPORTED: + self.sleeptime_button.set_text("Sleeptimer", sleepName) + else: + self._sleepMenuItem.get_child().set_text("Sleeptimer: %s" % (sleepName, )) + + @gtk_toolbox.log_exception(_moduleLogger) + def __on_call_started(self, callMonitor): + self.pause() + + @gtk_toolbox.log_exception(_moduleLogger) + def _on_settings(self, *args): + if self.__settingsWindow is None: + vbox = gtk.VBox() + self.__settingsManager = settings.SettingsDialog(vbox) + + self.__settingsWindow = gtk.Window() + self.__settingsWindow.add(vbox) + self.__settingsWindow = hildonize.hildonize_window(self._app, self.__settingsWindow) + self.__settingsManager.window = self.__settingsWindow + + self.__settingsWindow.set_title("Settings") + self.__settingsWindow.set_transient_for(self.win) + self.__settingsWindow.set_default_size(*self.win.get_size()) + self.__settingsWindow.connect("delete-event", self._on_settings_delete) + self.__settingsManager.set_portrait_state(self.__isPortrait) + self.__settingsManager.set_audiobook_path(self.controller.get_books_path()) + self.__settingsWindow.set_modal(True) + self.__settingsWindow.show_all() + + @gtk_toolbox.log_exception(_moduleLogger) + def _on_settings_delete(self, *args): + self.__settingsWindow.emit_stop_by_name("delete-event") + self.__settingsWindow.hide() + self.__settingsWindow.set_modal(False) + + isPortrait = self.__settingsManager.is_portrait() + if isPortrait ^ self.__isPortrait: + if isPortrait: + orientation = gtk.ORIENTATION_VERTICAL + else: + orientation = gtk.ORIENTATION_HORIZONTAL + self.set_orientation(orientation) + if self.__settingsManager.get_audiobook_path() != self.controller.get_books_path(): + self.controller.reload(self.__settingsManager.get_audiobook_path()) + + return True + + @gtk_toolbox.log_exception(_moduleLogger) + def update_seek(self): + #print self.controller.get_percentage() + if self.controller.is_playing(): + gtk.gdk.threads_enter() + self.seek.set_value(self.controller.get_percentage() * 100) + gtk.gdk.threads_leave() + #self.controller.get_percentage() + return True # run again + + @gtk_toolbox.log_exception(_moduleLogger) + def sleep(self): + _moduleLogger.info("sleep time timeout") + hildonize.show_information_banner(self.win, "Sleep timer") + self.controller.stop() + self.set_button_text("Resume", "Resume playing the audiobook") + return False # do not repeat + + @gtk_toolbox.log_exception(_moduleLogger) + def get_help(self, button): + Browser.open("file:///opt/Nqa-Audiobook-player/Help/nqaap.html") + + @gtk_toolbox.log_exception(_moduleLogger) + def seek_changed(self, seek, scroll , value): + # print "sok", scroll + self.controller.seek_percent(seek.get_value() / 100.0) + + @gtk_toolbox.log_exception(_moduleLogger) + def _on_next_chapter(self, *args): + self.controller.next_chapter() + + @gtk_toolbox.log_exception(_moduleLogger) + def _on_previous_chapter(self, *args): + self.controller.previous_chapter() + + @gtk_toolbox.log_exception(_moduleLogger) + def play_pressed(self, button): + if self.controller.is_playing(): + self.pause() + else: + self.play() + + @gtk_toolbox.log_exception(_moduleLogger) + def on_key_press(self, widget, event, *args): + RETURN_TYPES = (gtk.keysyms.Return, gtk.keysyms.ISO_Enter, gtk.keysyms.KP_Enter) + isCtrl = bool(event.get_state() & gtk.gdk.CONTROL_MASK) + if ( + event.keyval == gtk.keysyms.F6 or + event.keyval in RETURN_TYPES and isCtrl + ): + # The "Full screen" hardware key has been pressed + if self.__window_in_fullscreen: + self.win.unfullscreen () + else: + self.win.fullscreen () + return True + elif event.keyval == gtk.keysyms.o and isCtrl: + self._toggle_rotate() + return True + elif ( + event.keyval in (gtk.keysyms.w, gtk.keysyms.q) and + event.get_state() & gtk.gdk.CONTROL_MASK + ): + self.quit() + elif event.keyval == gtk.keysyms.l and event.get_state() & gtk.gdk.CONTROL_MASK: + with open(constants._user_logpath_, "r") as f: + logLines = f.xreadlines() + log = "".join(logLines) + self._clipboard.set_text(str(log)) + return True + elif event.keyval in RETURN_TYPES: + if self.controller.is_playing(): + self.pause() + else: + self.play() + return True + elif event.keyval == gtk.keysyms.Left: + self.controller.previous_chapter() + return True + elif event.keyval == gtk.keysyms.Right: + self.controller.next_chapter() + return True + + @gtk_toolbox.log_exception(_moduleLogger) + def _on_window_state_change(self, widget, event, *args): + if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN: + self.__window_in_fullscreen = True + else: + self.__window_in_fullscreen = False + + @gtk_toolbox.log_exception(_moduleLogger) + def quit(self, *args): # what are the arguments? + _moduleLogger.info("Shutting down") + try: + self.save_settings() + self.controller.stop() # to save the state + finally: + gtk.main_quit() + + @gtk_toolbox.log_exception(_moduleLogger) + def _on_about_activate(self, *args): + dlg = gtk.AboutDialog() + dlg.set_name(constants.__pretty_app_name__) + dlg.set_version("%s-%d" % (constants.__version__, constants.__build__)) + dlg.set_copyright("Copyright 2010") + dlg.set_comments("") + dlg.set_website("http://nqaap.garage.maemo.org/") + dlg.set_authors(["Pengman ", "Ed Page "]) + dlg.run() + dlg.destroy() + + # Actions: + + def play(self): + self.set_button_text("Stop", "Stop playing the audiobook") + self.controller.play() + + def pause(self): + self.set_button_text("Resume", "Resume playing the audiobook") + self.controller.stop() + + def set_orientation(self, orientation): + if orientation == gtk.ORIENTATION_VERTICAL: + if self.__isPortrait: + return + hildonize.window_to_portrait(self.win) + self.__isPortrait = True + + self._viewLayout.remove(self._controlLayout) + self._mainLayout.add(self._controlLayout) + elif orientation == gtk.ORIENTATION_HORIZONTAL: + if not self.__isPortrait: + return + hildonize.window_to_landscape(self.win) + self.__isPortrait = False + + self._mainLayout.remove(self._controlLayout) + self._viewLayout.add(self._controlLayout) + else: + raise NotImplementedError(orientation) + + def get_orientation(self): + return gtk.ORIENTATION_VERTICAL if self.__isPortrait else gtk.ORIENTATION_HORIZONTAL + + def _toggle_rotate(self): + if self.__isPortrait: + self.set_orientation(gtk.ORIENTATION_HORIZONTAL) + else: + self.set_orientation(gtk.ORIENTATION_VERTICAL) + + def change_chapter(self, chapterName): + if chapterName is None: + _moduleLogger.debug("chapter selection canceled.") + return True # this should end the function and indicate it has been handled + + _moduleLogger.debug("chapter changed (by controller) to: %s" % chapterName) + + def set_button_text(self, title, text): + if hildonize.IS_FREMANTLE_SUPPORTED: + self.button.set_text(title, text) + else: + self.button.set_label("%s" % (title, )) + + def set_books(self, books): + _moduleLogger.debug("new books") + del self._bookSelection[:] + self._bookSelection.extend(books) + if len(books) == 0 and self.controller is not None: + hildonize.show_information_banner(self.win, "No audiobooks found. \nPlease place your audiobooks in the directory %s" % self.controller.get_books_path()) + + def set_book(self, bookPath, cover): + bookName = self.__format_name(bookPath) + + self.set_button_text("Play", "Start playing the audiobook") # reset button + self._set_display_title(bookName) + if hildonize.IS_FREMANTLE_SUPPORTED: + self.book_button.set_text("Audiobook", bookName) + else: + self._audiobookMenuItem.get_child().set_text("Audiobook: %s" % (bookName, )) + if cover != "": + self.cover.set_from_file(cover) + + def set_chapter(self, chapterIndex): + ''' + Called from controller whenever a new chapter is started + + chapter parameter is supposed to be the index for the chapter, not the name + ''' + self.auto_chapter_selected = True + self._set_display_chapter(str(chapterIndex + 1)) + if hildonize.IS_FREMANTLE_SUPPORTED: + self.chapter_button.set_text("Chapter", str(chapterIndex)) + else: + self._chapterMenuItem.get_child().set_text("Chapter: %s" % (chapterIndex+1, )) + + def set_chapters(self, chapters): + _moduleLogger.debug("setting chapters" ) + del self._chapterSelection[:] + self._chapterSelection.extend(chapters) + + def set_sleep_timer(self, mins): + pass + + # Utils + def set_selected_value(self, button, value): + i = button.get_selector().get_model(0).index[value] # get index of value from list + button.set_active(i) # set active index to that index + + def _set_display_title(self, title): + self.title.set_markup("%s" % title) + + def _set_display_chapter(self, chapter): + self.chapter.set_markup("Chapter %s" % chapter) def _old_timeout_add_seconds(timeout, callback): - return gobject.timeout_add(timeout * 1000, callback) + return gobject.timeout_add(timeout * 1000, callback) def _timeout_add_seconds(timeout, callback): - return gobject.timeout_add_seconds(timeout, callback) + return gobject.timeout_add_seconds(timeout, callback) try: - gobject.timeout_add_seconds - timeout_add_seconds = _timeout_add_seconds + gobject.timeout_add_seconds + timeout_add_seconds = _timeout_add_seconds except AttributeError: - timeout_add_seconds = _old_timeout_add_seconds + timeout_add_seconds = _old_timeout_add_seconds if __name__ == "__main__": - g = Gui(None) + g = Gui(None) diff --git a/src/opt/Nqa-Audiobook-player/Player.py b/src/opt/Nqa-Audiobook-player/Player.py index 8535a43..6293ce5 100644 --- a/src/opt/Nqa-Audiobook-player/Player.py +++ b/src/opt/Nqa-Audiobook-player/Player.py @@ -14,185 +14,186 @@ _moduleLogger = logging.getLogger(__name__) class Player(object): - def __init__(self, ui): - self.storage = FileStorage.FileStorage(path = constants._data_path_) - if hildonize.IS_HILDON_SUPPORTED and not hildonize.IS_FREMANTLE_SUPPORTED: - import SimpleOSSOPlayer as _SimplePlayer - SimplePlayer = _SimplePlayer # silence PyFlakes - else: - import SimpleGStreamer as SimplePlayer - self.player = SimplePlayer.SimplePlayer(self.next_chapter) - self.ui = ui - self.audiobook = None - self._bookDir = None - self._bookPaths = {} - - def get_books_path(self): - return self._bookDir - - def reload(self, booksPath): - if self.audiobook is not None: - position = self.player.elapsed() - self.storage.set_time(self.audiobook.current_chapter, position) - self.save() - self.load(booksPath) - - def load(self, booksPath): - _moduleLogger.info("Loading books from %s" % booksPath) - self.storage.load() - self._bookDir = booksPath - - self._bookPaths = dict( - (self.__format_name(bookPath), bookPath) - for bookPath in self._find_books() - ) - if self.ui is not None: - bookPaths = self._bookPaths.values() - bookPaths.sort() - self.ui.set_books(bookPaths) - - lastBookName = self.storage.get_selected() - if lastBookName is not None: - _moduleLogger.info("continuing book: %s" % lastBookName) - try: - bookPath = self._bookPaths[lastBookName] - self.set_book(bookPath) - except KeyError: - _moduleLogger.exception("Audiobook was not found") - except IndexError: - _moduleLogger.exception("Chapter was not found") - except IOError: - _moduleLogger.exception("Audiobook could not be loaded") - except Exception: - _moduleLogger.exception("Can you say 'confusion'?") - - def save(self): - position = self.player.elapsed() - self.storage.set_time(self.audiobook.current_chapter, position) - self.storage.save() - - @staticmethod - def __format_name(path): - if os.path.isfile(path): - return os.path.basename(path).rsplit(".", 1)[0] - else: - return os.path.basename(path) - - def set_book(self, bookPath): - oldBookName = self.storage.get_selected() - try: - bookName = self.__format_name(bookPath) - self.storage.select_book(bookName) - chapter_num, _ = self.storage.get_time() - self.audiobook = Audiobook.Audiobook( - bookPath, - chapter_num - ) - except: - self.storage.select_book(oldBookName) - raise - - # self.player.set_file(self.audiobook.get_current_chapter()) - # self.player.seek_time(time) - - if self.ui is not None: - self.ui.set_book(bookPath, self.audiobook.get_cover_img()) - self.ui.set_chapters(self.audiobook.chapters) - - chapter_title = self.audiobook.chapters[self.audiobook.current_chapter] - self.set_chapter(chapter_title, True) - - def set_chapter(self, chapter, continuing = False): - _moduleLogger.info("set chapter:" + chapter + " : Continuing: " + str(continuing)) - self.audiobook.set_chapter(chapter) - self.player.set_file(self.audiobook.get_current_chapter()) - if not continuing: - self.storage.set_time(self.audiobook.current_chapter, 0) - - if self.ui is not None: - self.ui.set_chapter(self.audiobook.current_chapter) - - def previous_chapter(self, *args): - _moduleLogger.info("Going back a chapter") - self.player.stop() - next_file = self.audiobook.get_previous_chapter() - if next_file is not False: - self.set_chapter(next_file) - self.player.play() - else: # the book is over - self.storage.set_time(0, 0) - - def next_chapter(self, *args): - _moduleLogger.info("Advancing a chapter") - self.player.stop() - next_file = self.audiobook.get_next_chapter() - if next_file is not False: - self.set_chapter(next_file) - self.player.play() - else: # the book is over - self.storage.set_time(0, 0) - - def play(self): - if self.audiobook is not None: - self.player.play() - _, target_time = self.storage.get_time() - if 0 < target_time: - time.sleep(1) - self.player.seek_time(target_time) - #print self.player.elapsed() - else: - print "No book selected, find one in ", self._bookDir - - def stop(self): - position = self.player.elapsed() - self.player.stop() - - if self.audiobook is not None: - self.storage.set_time(self.audiobook.current_chapter, position) - - def is_playing(self): - return self.player.playing - - def sleeptimer(self, secs): - #print "sleeper", secs - time.sleep(secs) - #print "now its time to sleep" - self.stop() - - def start_sleeptimer(self, secs): - #print "startin sleep" - sleep_thread = threading.Thread(target=self.sleeptimer, args=(secs, )) - sleep_thread.start() - #print "started sleep" - - def get_percentage(self): - try: - return float(self.player.elapsed()) / float(self.player.duration()) - except ZeroDivisionError: - return 0.0 - - def seek_percent(self, ratio): - try: - target = int(self.player.duration() * ratio) # Calculate where to seek - self.player.seek_time(target) # seek - - position = self.player.elapsed() - self.storage.set_time(self.audiobook.current_chapter, target) # save position - return True - except: - _moduleLogger.exception("Seek failed") - return False - - def _find_books(self): - try: - paths = ( - os.path.join(self._bookDir, f) - for f in os.listdir(self._bookDir) - ) - return ( - path - for path in paths - if Audiobook.is_book(path) - ) - except OSError: - return () + def __init__(self, ui): + self.storage = FileStorage.FileStorage(path = constants._data_path_) + if hildonize.IS_HILDON_SUPPORTED and not hildonize.IS_FREMANTLE_SUPPORTED: + import SimpleOSSOPlayer as _SimplePlayer + SimplePlayer = _SimplePlayer # silence PyFlakes + else: + import SimpleGStreamer as SimplePlayer + self.player = SimplePlayer.SimplePlayer(self.next_chapter) + self.ui = ui + self.audiobook = None + self._bookDir = None + self._bookPaths = {} + + def get_books_path(self): + return self._bookDir + + def reload(self, booksPath): + if self.audiobook is not None: + position = self.player.elapsed() + self.storage.set_time(self.audiobook.current_chapter, position) + self.save() + self.load(booksPath) + + def load(self, booksPath): + _moduleLogger.info("Loading books from %s" % booksPath) + self.storage.load() + self._bookDir = booksPath + + self._bookPaths = dict( + (self.__format_name(bookPath), bookPath) + for bookPath in self._find_books() + ) + if self.ui is not None: + bookPaths = self._bookPaths.values() + bookPaths.sort() + self.ui.set_books(bookPaths) + + lastBookName = self.storage.get_selected() + if lastBookName is not None: + _moduleLogger.info("continuing book: %s" % lastBookName) + try: + bookPath = self._bookPaths[lastBookName] + self.set_book(bookPath) + except KeyError: + _moduleLogger.exception("Audiobook was not found") + except IndexError: + _moduleLogger.exception("Chapter was not found") + except IOError: + _moduleLogger.exception("Audiobook could not be loaded") + except Exception: + _moduleLogger.exception("Can you say 'confusion'?") + + def save(self): + position = self.player.elapsed() + if self.audiobook is not None: + self.storage.set_time(self.audiobook.current_chapter, position) + self.storage.save() + + @staticmethod + def __format_name(path): + if os.path.isfile(path): + return os.path.basename(path).rsplit(".", 1)[0] + else: + return os.path.basename(path) + + def set_book(self, bookPath): + oldBookName = self.storage.get_selected() + try: + bookName = self.__format_name(bookPath) + self.storage.select_book(bookName) + chapter_num, _ = self.storage.get_time() + self.audiobook = Audiobook.Audiobook( + bookPath, + chapter_num + ) + except: + self.storage.select_book(oldBookName) + raise + + # self.player.set_file(self.audiobook.get_current_chapter()) + # self.player.seek_time(time) + + if self.ui is not None: + self.ui.set_book(bookPath, self.audiobook.get_cover_img()) + self.ui.set_chapters(self.audiobook.chapters) + + chapter_title = self.audiobook.chapters[self.audiobook.current_chapter] + self.set_chapter(chapter_title, True) + + def set_chapter(self, chapter, continuing = False): + _moduleLogger.info("set chapter:" + chapter + " : Continuing: " + str(continuing)) + self.audiobook.set_chapter(chapter) + self.player.set_file(self.audiobook.get_current_chapter()) + if not continuing: + self.storage.set_time(self.audiobook.current_chapter, 0) + + if self.ui is not None: + self.ui.set_chapter(self.audiobook.current_chapter) + + def previous_chapter(self, *args): + _moduleLogger.info("Going back a chapter") + self.player.stop() + next_file = self.audiobook.get_previous_chapter() + if next_file is not False: + self.set_chapter(next_file) + self.player.play() + else: # the book is over + self.storage.set_time(0, 0) + + def next_chapter(self, *args): + _moduleLogger.info("Advancing a chapter") + self.player.stop() + next_file = self.audiobook.get_next_chapter() + if next_file is not False: + self.set_chapter(next_file) + self.player.play() + else: # the book is over + self.storage.set_time(0, 0) + + def play(self): + if self.audiobook is not None: + self.player.play() + _, target_time = self.storage.get_time() + if 0 < target_time: + time.sleep(1) + self.player.seek_time(target_time) + #print self.player.elapsed() + else: + print "No book selected, find one in ", self._bookDir + + def stop(self): + position = self.player.elapsed() + self.player.stop() + + if self.audiobook is not None: + self.storage.set_time(self.audiobook.current_chapter, position) + + def is_playing(self): + return self.player.playing + + def sleeptimer(self, secs): + #print "sleeper", secs + time.sleep(secs) + #print "now its time to sleep" + self.stop() + + def start_sleeptimer(self, secs): + #print "startin sleep" + sleep_thread = threading.Thread(target=self.sleeptimer, args=(secs, )) + sleep_thread.start() + #print "started sleep" + + def get_percentage(self): + try: + return float(self.player.elapsed()) / float(self.player.duration()) + except ZeroDivisionError: + return 0.0 + + def seek_percent(self, ratio): + try: + target = int(self.player.duration() * ratio) # Calculate where to seek + self.player.seek_time(target) # seek + + position = self.player.elapsed() + self.storage.set_time(self.audiobook.current_chapter, target) # save position + return True + except: + _moduleLogger.exception("Seek failed") + return False + + def _find_books(self): + try: + paths = ( + os.path.join(self._bookDir, f) + for f in os.listdir(self._bookDir) + ) + return ( + path + for path in paths + if Audiobook.is_book(path) + ) + except OSError: + return () diff --git a/src/opt/Nqa-Audiobook-player/SimpleGStreamer.py b/src/opt/Nqa-Audiobook-player/SimpleGStreamer.py index d5f19a4..5d91edd 100644 --- a/src/opt/Nqa-Audiobook-player/SimpleGStreamer.py +++ b/src/opt/Nqa-Audiobook-player/SimpleGStreamer.py @@ -11,98 +11,98 @@ _moduleLogger = logging.getLogger(__name__) class SimplePlayer(object): - # @todo Add pitch/speed control - # http://github.com/jwagner/playitslowly/blob/master/playitslowly/pipeline.py - - def __init__(self, on_playing_done = None): - #Fields - self.playing = False - self.__filename = "" - self.__elapsed = 0 - self.__duration = 0 - - #Event callbacks - self.on_playing_done = on_playing_done - - #Set up GStreamer - self.player = gst.element_factory_make("playbin2", "player") - fakesink = gst.element_factory_make("fakesink", "fakesink") - self.player.set_property("video-sink", fakesink) - bus = self.player.get_bus() - bus.add_signal_watch() - bus.connect("message", self.on_message) - - #Constants - self.time_format = gst.Format(gst.FORMAT_TIME) - self.seek_flag = gst.SEEK_FLAG_FLUSH - - @property - def has_file(self): - return 0 < len(self.__filename) - - @gtk_toolbox.log_exception(_moduleLogger) - def on_message(self, bus, message): - t = message.type - if t == gst.MESSAGE_EOS: # End-Of-Stream - self.player.set_state(gst.STATE_NULL) - self.playing = False - if self.on_playing_done is not None: # event callback - self.on_playing_done(self) - elif t == gst.MESSAGE_ERROR: - self.player.set_state(gst.STATE_NULL) - err, debug = message.parse_error() - #print "Error: %s" % err, debug - _moduleLogger.error("Error: %s, (%s)" % (err, debug)) - self.playing = False - - def set_file(self, file): - _moduleLogger.info("set file: %s", file) - if os.path.isfile(file): - if self.__filename != file: - self._invalidate_cache() - if self.playing: - self.stop() - - file = os.path.abspath(file) # ensure absolute path - _moduleLogger.debug("set file (absolute path): %s "%file) - self.player.set_property("uri", "file://" + file) - self.__filename = file - else: - _moduleLogger.error("File: %s not found" % file) - - def play(self): - _moduleLogger.info("Started playing") - self.player.set_state(gst.STATE_PLAYING) - self.playing = True - - def stop(self): - self.player.set_state(gst.STATE_NULL) - self.playing = False - _moduleLogger.info("Stopped playing") - - def elapsed(self): - try: - self.__elapsed = self.player.query_position(self.time_format, None)[0] - except: - pass - return self.__elapsed - - def duration(self): - try: - self.__duration = self.player.query_duration(self.time_format, None)[0] - except: - _moduleLogger.exception("Query failed") - pass - return self.__duration - - def seek_time(self, ns): - _moduleLogger.debug("Seeking to: %s", ns) - self.player.seek_simple(self.time_format, self.seek_flag, ns) - - def _invalidate_cache(self): - self.__elapsed = 0 - self.__duration = 0 - - def __seek_percent(self, percent): - format = gst.Format(gst.FORMAT_PERCENT) - self.player.seek_simple(format, self.seek_flag, percent) + # @todo Add pitch/speed control + # http://github.com/jwagner/playitslowly/blob/master/playitslowly/pipeline.py + + def __init__(self, on_playing_done = None): + #Fields + self.playing = False + self.__filename = "" + self.__elapsed = 0 + self.__duration = 0 + + #Event callbacks + self.on_playing_done = on_playing_done + + #Set up GStreamer + self.player = gst.element_factory_make("playbin2", "player") + fakesink = gst.element_factory_make("fakesink", "fakesink") + self.player.set_property("video-sink", fakesink) + bus = self.player.get_bus() + bus.add_signal_watch() + bus.connect("message", self.on_message) + + #Constants + self.time_format = gst.Format(gst.FORMAT_TIME) + self.seek_flag = gst.SEEK_FLAG_FLUSH + + @property + def has_file(self): + return 0 < len(self.__filename) + + @gtk_toolbox.log_exception(_moduleLogger) + def on_message(self, bus, message): + t = message.type + if t == gst.MESSAGE_EOS: # End-Of-Stream + self.player.set_state(gst.STATE_NULL) + self.playing = False + if self.on_playing_done is not None: # event callback + self.on_playing_done(self) + elif t == gst.MESSAGE_ERROR: + self.player.set_state(gst.STATE_NULL) + err, debug = message.parse_error() + #print "Error: %s" % err, debug + _moduleLogger.error("Error: %s, (%s)" % (err, debug)) + self.playing = False + + def set_file(self, file): + _moduleLogger.info("set file: %s", file) + if os.path.isfile(file): + if self.__filename != file: + self._invalidate_cache() + if self.playing: + self.stop() + + file = os.path.abspath(file) # ensure absolute path + _moduleLogger.debug("set file (absolute path): %s "%file) + self.player.set_property("uri", "file://" + file) + self.__filename = file + else: + _moduleLogger.error("File: %s not found" % file) + + def play(self): + _moduleLogger.info("Started playing") + self.player.set_state(gst.STATE_PLAYING) + self.playing = True + + def stop(self): + self.player.set_state(gst.STATE_NULL) + self.playing = False + _moduleLogger.info("Stopped playing") + + def elapsed(self): + try: + self.__elapsed = self.player.query_position(self.time_format, None)[0] + except: + pass + return self.__elapsed + + def duration(self): + try: + self.__duration = self.player.query_duration(self.time_format, None)[0] + except: + _moduleLogger.exception("Query failed") + pass + return self.__duration + + def seek_time(self, ns): + _moduleLogger.debug("Seeking to: %s", ns) + self.player.seek_simple(self.time_format, self.seek_flag, ns) + + def _invalidate_cache(self): + self.__elapsed = 0 + self.__duration = 0 + + def __seek_percent(self, percent): + format = gst.Format(gst.FORMAT_PERCENT) + self.player.seek_simple(format, self.seek_flag, percent) diff --git a/src/opt/Nqa-Audiobook-player/SimpleOSSOPlayer.py b/src/opt/Nqa-Audiobook-player/SimpleOSSOPlayer.py index 9129f1a..698e0db 100644 --- a/src/opt/Nqa-Audiobook-player/SimpleOSSOPlayer.py +++ b/src/opt/Nqa-Audiobook-player/SimpleOSSOPlayer.py @@ -11,105 +11,105 @@ _moduleLogger = logging.getLogger(__name__) class SimplePlayer(object): - SERVICE_NAME = "com.nokia.osso_media_server" - OBJECT_PATH = "/com/nokia/osso_media_server" - AUDIO_INTERFACE_NAME = "com.nokia.osso_media_server.music" - - def __init__(self, on_playing_done = None): - #Fields - self.has_file = False - self.playing = False - self.__elapsed = 0 - - #Event callbacks - self.on_playing_done = on_playing_done - - session_bus = dbus.SessionBus() - - # Get the osso-media-player proxy object - oms_object = session_bus.get_object( - self.SERVICE_NAME, - self.OBJECT_PATH, - introspect=False, - follow_name_owner_changes=True, - ) - # Use the audio interface - oms_audio_interface = dbus.Interface( - oms_object, - self.AUDIO_INTERFACE_NAME, - ) - self._audioProxy = oms_audio_interface - - self._audioProxy.connect_to_signal("state_changed", self._on_state_changed) - self._audioProxy.connect_to_signal("end_of_stream", self._on_end_of_stream) - - error_signals = [ - "no_media_selected", - "file_not_found", - "type_not_found", - "unsupported_type", - "gstreamer", - "dsp", - "device_unavailable", - "corrupted_file", - "out_of_memory", - "audio_codec_not_supported", - ] - for error in error_signals: - self._audioProxy.connect_to_signal(error, self._on_error) - - @gtk_toolbox.log_exception(_moduleLogger) - def _on_error(self, *args): - self.playing = False - - @gtk_toolbox.log_exception(_moduleLogger) - def _on_end_of_stream(self, *args): - self.playing = False - if self.on_playing_done is not None: # event callback - self.on_playing_done(self) - - @gtk_toolbox.log_exception(_moduleLogger) - def _on_state_changed(self, state): - _moduleLogger.info("State: %s", state) - - def set_file(self, file): - _moduleLogger.info("set file: %s", file) - if os.path.isfile(file): - if self.playing: - self.stop() - - uri = "file://" + file - self._audioProxy.set_media_location(uri) - self.has_file = True - else: - _moduleLogger.error("File: %s not found" % file) - - def play(self): - _moduleLogger.info("Started playing") - self._audioProxy.play() - self.playing = True - - def stop(self): - self._audioProxy.stop() - self.playing = False - _moduleLogger.info("Stopped playing") - - def elapsed(self): - pos_info = self._audioProxy.get_position() - if isinstance(pos_info, tuple): - pos, _ = pos_info - return pos - else: - return 0 - - def duration(self): - pos_info = self._audioProxy.get_position() - if isinstance(pos_info, tuple): - _, dur = pos_info - return dur - else: - return 0 - - def seek_time(self, ns): - _moduleLogger.debug("Seeking to: %s", ns) - self._audioProxy.seek( dbus.Int32(1), dbus.Int32(ns) ) + SERVICE_NAME = "com.nokia.osso_media_server" + OBJECT_PATH = "/com/nokia/osso_media_server" + AUDIO_INTERFACE_NAME = "com.nokia.osso_media_server.music" + + def __init__(self, on_playing_done = None): + #Fields + self.has_file = False + self.playing = False + self.__elapsed = 0 + + #Event callbacks + self.on_playing_done = on_playing_done + + session_bus = dbus.SessionBus() + + # Get the osso-media-player proxy object + oms_object = session_bus.get_object( + self.SERVICE_NAME, + self.OBJECT_PATH, + introspect=False, + follow_name_owner_changes=True, + ) + # Use the audio interface + oms_audio_interface = dbus.Interface( + oms_object, + self.AUDIO_INTERFACE_NAME, + ) + self._audioProxy = oms_audio_interface + + self._audioProxy.connect_to_signal("state_changed", self._on_state_changed) + self._audioProxy.connect_to_signal("end_of_stream", self._on_end_of_stream) + + error_signals = [ + "no_media_selected", + "file_not_found", + "type_not_found", + "unsupported_type", + "gstreamer", + "dsp", + "device_unavailable", + "corrupted_file", + "out_of_memory", + "audio_codec_not_supported", + ] + for error in error_signals: + self._audioProxy.connect_to_signal(error, self._on_error) + + @gtk_toolbox.log_exception(_moduleLogger) + def _on_error(self, *args): + self.playing = False + + @gtk_toolbox.log_exception(_moduleLogger) + def _on_end_of_stream(self, *args): + self.playing = False + if self.on_playing_done is not None: # event callback + self.on_playing_done(self) + + @gtk_toolbox.log_exception(_moduleLogger) + def _on_state_changed(self, state): + _moduleLogger.info("State: %s", state) + + def set_file(self, file): + _moduleLogger.info("set file: %s", file) + if os.path.isfile(file): + if self.playing: + self.stop() + + uri = "file://" + file + self._audioProxy.set_media_location(uri) + self.has_file = True + else: + _moduleLogger.error("File: %s not found" % file) + + def play(self): + _moduleLogger.info("Started playing") + self._audioProxy.play() + self.playing = True + + def stop(self): + self._audioProxy.stop() + self.playing = False + _moduleLogger.info("Stopped playing") + + def elapsed(self): + pos_info = self._audioProxy.get_position() + if isinstance(pos_info, tuple): + pos, _ = pos_info + return pos + else: + return 0 + + def duration(self): + pos_info = self._audioProxy.get_position() + if isinstance(pos_info, tuple): + _, dur = pos_info + return dur + else: + return 0 + + def seek_time(self, ns): + _moduleLogger.debug("Seeking to: %s", ns) + self._audioProxy.seek( dbus.Int32(1), dbus.Int32(ns) ) diff --git a/src/opt/Nqa-Audiobook-player/nqaap_gtk.py b/src/opt/Nqa-Audiobook-player/nqaap_gtk.py index 3dc6e4f..90e812f 100755 --- a/src/opt/Nqa-Audiobook-player/nqaap_gtk.py +++ b/src/opt/Nqa-Audiobook-player/nqaap_gtk.py @@ -17,22 +17,22 @@ _moduleLogger = logging.getLogger(__name__) def run(): - l = dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) - gobject.threads_init() - gtk.gdk.threads_init() + l = dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + gobject.threads_init() + gtk.gdk.threads_init() - if hildonize.IS_FREMANTLE_SUPPORTED: - hildonize.set_application_name("FMRadio") - else: - hildonize.set_application_name(constants.__pretty_app_name__) - gui = Gui() - controller = Player(ui = gui) - gui.controller = controller - gui.load_settings() + if hildonize.IS_FREMANTLE_SUPPORTED: + hildonize.set_application_name("FMRadio") + else: + hildonize.set_application_name(constants.__pretty_app_name__) + gui = Gui() + controller = Player(ui = gui) + gui.controller = controller + gui.load_settings() - gtk.main() + gtk.main() if __name__ == "__main__": - logging.basicConfig(level=logging.DEBUG) - run() + logging.basicConfig(level=logging.DEBUG) + run() diff --git a/support/builddeb.py b/support/builddeb.py index 8c327bd..ab31cb5 100755 --- a/support/builddeb.py +++ b/support/builddeb.py @@ -19,115 +19,115 @@ import py2deb def build_package(distribution): - py2deb.Py2deb.SECTIONS = py2deb.SECTIONS_BY_POLICY[distribution] - try: - os.chdir(os.path.dirname(sys.argv[0])) - except: - pass + py2deb.Py2deb.SECTIONS = py2deb.SECTIONS_BY_POLICY[distribution] + try: + os.chdir(os.path.dirname(sys.argv[0])) + except: + pass - p=py2deb.Py2deb("nqaap") #This is the package name and MUST be in - #lowercase! (using e.g. "mClock" fails - #miserably...) - p.prettyName="NQA Audiobook Player" - p.description="""Very simple Audiobook player. + p=py2deb.Py2deb("nqaap") #This is the package name and MUST be in + #lowercase! (using e.g. "mClock" fails + #miserably...) + p.prettyName="NQA Audiobook Player" + p.description="""Very simple Audiobook player. Supports playing, pausing, seeking (sort of) and saving state when changing book/closing. Plays books arranged as dirs under myDocs/Audiobooks . Homepage: http://wiki.maemo.org/Nqaap""" - p.author="Soeren 'Pengman' Pedersen" - p.mail="pengmeister@gmail.com" - p.license = "lgpl" - p.depends = ", ".join([ - "python2.6 | python2.5", - "python-gtk2 | python2.5-gtk2", - "python-dbus | python2.5-dbus", - "python-telepathy | python2.5-telepathy", - "python-gobject | python2.5-gobject", - "python-simplejson", - ]) - maemoSpecificDepends = ", python-osso | python2.5-osso, python-hildon | python2.5-hildon" - p.depends += { - "debian": ", python-gst0.10", - "diablo": maemoSpecificDepends, - "fremantle": maemoSpecificDepends + ", python-gst0.10", - }[distribution] - p.section = { - "debian": "sound", - "diablo": "user/multimedia", - "fremantle": "user/multimedia", - }[distribution] - p.icon = "src/usr/share/icons/hicolor/48x48/hildon/nqaap.png" - p.arch="all" #should be all for python, any for all arch - p.urgency="low" #not used in maemo onl for deb os - p.distribution=distribution - p.repository="extras" - p.bugTracker="https://bugs.maemo.org/enter_bug.cgi?product=nQa%%20Audiobook%%20Player" - p.postinstall="""#!/bin/sh + p.author="Soeren 'Pengman' Pedersen" + p.mail="pengmeister@gmail.com" + p.license = "lgpl" + p.depends = ", ".join([ + "python2.6 | python2.5", + "python-gtk2 | python2.5-gtk2", + "python-dbus | python2.5-dbus", + "python-telepathy | python2.5-telepathy", + "python-gobject | python2.5-gobject", + "python-simplejson", + ]) + maemoSpecificDepends = ", python-osso | python2.5-osso, python-hildon | python2.5-hildon" + p.depends += { + "debian": ", python-gst0.10", + "diablo": maemoSpecificDepends, + "fremantle": maemoSpecificDepends + ", python-gst0.10", + }[distribution] + p.section = { + "debian": "sound", + "diablo": "user/multimedia", + "fremantle": "user/multimedia", + }[distribution] + p.icon = "src/usr/share/icons/hicolor/48x48/hildon/nqaap.png" + p.arch="all" #should be all for python, any for all arch + p.urgency="low" #not used in maemo onl for deb os + p.distribution=distribution + p.repository="extras" + p.bugTracker="https://bugs.maemo.org/enter_bug.cgi?product=nQa%%20Audiobook%%20Player" + p.postinstall="""#!/bin/sh rm -f ~/.nqaap/nqaap.log """ - # p.postremove="""#!/bin/sh - # chmod +x /usr/bin/mclock.py""" #Set here your post remove script - # p.preinstall="""#!/bin/sh - # chmod +x /usr/bin/mclock.py""" #Set here your pre install script - # p.preremove="""#!/bin/sh - # chmod +x /usr/bin/mclock.py""" #Set here your pre remove script - version = "0.8.6" #Version of your software, e.g. "1.2.0" or "0.8.2" - build = "0" #Build number, e.g. "1" for the first build of this - #version of your software. Increment - #for later re-builds of the same - #version of your software. Text with - #changelog information to be displayed - #in the package "Details" tab of the - #Maemo Application Manager - changeloginformation = """ + # p.postremove="""#!/bin/sh + # chmod +x /usr/bin/mclock.py""" #Set here your post remove script + # p.preinstall="""#!/bin/sh + # chmod +x /usr/bin/mclock.py""" #Set here your pre install script + # p.preremove="""#!/bin/sh + # chmod +x /usr/bin/mclock.py""" #Set here your pre remove script + version = "0.8.6" #Version of your software, e.g. "1.2.0" or "0.8.2" + build = "0" #Build number, e.g. "1" for the first build of this + #version of your software. Increment + #for later re-builds of the same + #version of your software. Text with + #changelog information to be displayed + #in the package "Details" tab of the + #Maemo Application Manager + changeloginformation = """ * About window as requested by magnuslu * Switched how storage of active book, chapter, and chapter position is stored """.strip() - dir_name = "src" #Name of the subfolder containing your package - #source files - #(e.g. usr\share\icons\hicolor\scalable\myappicon.svg, - #usr\lib\myapp\somelib.py). We suggest - #to leave it named src in all projects - #and will refer to that in the wiki - #article on maemo.org - #Thanks to DareTheHair from talk.maemo.org for this snippet that - #recursively builds the file list - for root, dirs, files in os.walk(dir_name): - if any(f.startswith(".") for f in root.split(os.sep)): - continue # avoid hidden folders, esp svn ones + dir_name = "src" #Name of the subfolder containing your package + #source files + #(e.g. usr\share\icons\hicolor\scalable\myappicon.svg, + #usr\lib\myapp\somelib.py). We suggest + #to leave it named src in all projects + #and will refer to that in the wiki + #article on maemo.org + #Thanks to DareTheHair from talk.maemo.org for this snippet that + #recursively builds the file list + for root, dirs, files in os.walk(dir_name): + if any(f.startswith(".") for f in root.split(os.sep)): + continue # avoid hidden folders, esp svn ones - real_dir = root[len(dir_name):] - fake_file = [] - for f in files: - fake_file.append(root + os.sep + f + "|" + f) - if len(fake_file) > 0: - p[real_dir] = fake_file + real_dir = root[len(dir_name):] + fake_file = [] + for f in files: + fake_file.append(root + os.sep + f + "|" + f) + if len(fake_file) > 0: + p[real_dir] = fake_file - print p - if distribution == "debian": - print p.generate( - version="%s-%s" % (version, build), - changelog=changeloginformation, - build=True, - tar=False, - changes=False, - dsc=False, - ) - else: - print p.generate( - version="%s-%s" % (version, build), - changelog=changeloginformation, - build=False, - tar=True, - changes=True, - dsc=True, - ) - print "Building for %s finished" % distribution + print p + if distribution == "debian": + print p.generate( + version="%s-%s" % (version, build), + changelog=changeloginformation, + build=True, + tar=False, + changes=False, + dsc=False, + ) + else: + print p.generate( + version="%s-%s" % (version, build), + changelog=changeloginformation, + build=False, + tar=True, + changes=True, + dsc=True, + ) + print "Building for %s finished" % distribution if __name__ == "__main__": - if len(sys.argv) == 1: - distribution = "fremantle" - else: - distribution = sys.argv[1] - build_package(distribution) + if len(sys.argv) == 1: + distribution = "fremantle" + else: + distribution = sys.argv[1] + build_package(distribution) -- 1.7.9.5