Switching from file-based book settings to json based, making it much easier to add...
[nqaap] / src / opt / Nqa-Audiobook-player / Player.py
1 import os
2 import threading
3 import time
4 import logging
5
6 import constants
7 import hildonize
8 import Audiobook
9 import FileStorage
10
11
12 _moduleLogger = logging.getLogger(__name__)
13
14
15 class Player(object):
16
17     def __init__(self, ui):
18         self.storage = FileStorage.FileStorage(path = constants._data_path_)
19         if hildonize.IS_HILDON_SUPPORTED and not hildonize.IS_FREMANTLE_SUPPORTED:
20             import SimpleOSSOPlayer as _SimplePlayer
21             SimplePlayer = _SimplePlayer # silence PyFlakes
22         else:
23             import SimpleGStreamer as SimplePlayer
24         self.player = SimplePlayer.SimplePlayer(self.next_chapter)
25         self.ui = ui
26         self.audiobook = None
27         self._bookDir = None
28         self._bookPaths = {}
29
30     def get_books_path(self):
31         return self._bookDir
32
33     def reload(self, booksPath):
34         if self.audiobook is not None:
35             position = self.player.elapsed()
36             self.storage.set_time(self.audiobook.current_chapter, position)
37         self.save()
38         self.load(booksPath)
39
40     def load(self, booksPath):
41         _moduleLogger.info("Loading books from %s" % booksPath)
42         self.storage.load()
43         self._bookDir = booksPath
44
45         self._bookPaths = dict(
46             (self.__format_name(bookPath), bookPath)
47             for bookPath in self._find_books()
48         )
49         if self.ui is not None:
50             bookPaths = self._bookPaths.values()
51             bookPaths.sort()
52             self.ui.set_books(bookPaths)
53
54         lastBookName = self.storage.get_selected()
55         if lastBookName is not None:
56             _moduleLogger.info("continuing book: %s" % lastBookName)
57             try:
58                 bookPath = self._bookPaths[lastBookName]
59                 self.set_book(bookPath)
60             except KeyError:
61                 _moduleLogger.exception("Audiobook was not found")
62             except IndexError:
63                 _moduleLogger.exception("Chapter was not found")
64             except IOError:
65                 _moduleLogger.exception("Audiobook could not be loaded")
66             except Exception:
67                 _moduleLogger.exception("Can you say 'confusion'?")
68
69     def save(self):
70         position = self.player.elapsed()
71         self.storage.set_time(self.audiobook.current_chapter, position)
72         self.storage.save()
73
74     @staticmethod
75     def __format_name(path):
76         if os.path.isfile(path):
77             return os.path.basename(path).rsplit(".", 1)[0]
78         else:
79             return os.path.basename(path)
80
81     def set_book(self, bookPath):
82         oldBookName = self.storage.get_selected()
83         try:
84             bookName = self.__format_name(bookPath)
85             self.storage.select_book(bookName)
86             chapter_num, _ = self.storage.get_time()
87             self.audiobook = Audiobook.Audiobook(
88                 bookPath,
89                 chapter_num
90             )
91         except:
92             self.storage.select_book(oldBookName)
93             raise
94
95         # self.player.set_file(self.audiobook.get_current_chapter())
96         # self.player.seek_time(time) 
97
98         if self.ui is not None:
99             self.ui.set_book(bookPath, self.audiobook.get_cover_img())
100             self.ui.set_chapters(self.audiobook.chapters)
101
102         chapter_title = self.audiobook.chapters[self.audiobook.current_chapter]
103         self.set_chapter(chapter_title, True)
104
105     def set_chapter(self, chapter, continuing = False):
106         _moduleLogger.info("set chapter:" + chapter + " : Continuing: " + str(continuing))
107         self.audiobook.set_chapter(chapter)
108         self.player.set_file(self.audiobook.get_current_chapter())
109         if not continuing:
110             self.storage.set_time(self.audiobook.current_chapter, 0)
111
112         if self.ui is not None:
113             self.ui.set_chapter(self.audiobook.current_chapter)
114
115     def previous_chapter(self, *args):
116         _moduleLogger.info("Going back a chapter")
117         self.player.stop()
118         next_file = self.audiobook.get_previous_chapter()
119         if next_file is not False:
120             self.set_chapter(next_file)
121             self.player.play()
122         else:                           # the book is over
123             self.storage.set_time(0, 0)
124
125     def next_chapter(self, *args):
126         _moduleLogger.info("Advancing a chapter")
127         self.player.stop()
128         next_file = self.audiobook.get_next_chapter()
129         if next_file is not False:
130             self.set_chapter(next_file)
131             self.player.play()
132         else:                           # the book is over
133             self.storage.set_time(0, 0)
134
135     def play(self):
136         if self.audiobook is not None:
137             self.player.play()
138             _, target_time = self.storage.get_time()
139             if 0 < target_time:
140                 time.sleep(1)
141                 self.player.seek_time(target_time)
142             #print self.player.elapsed()
143         else:
144             print "No book selected, find one in ", self._bookDir
145
146     def stop(self):
147         position = self.player.elapsed()
148         self.player.stop()
149
150         if self.audiobook is not None:
151             self.storage.set_time(self.audiobook.current_chapter, position)
152
153     def is_playing(self):
154         return self.player.playing
155
156     def sleeptimer(self, secs):
157         #print "sleeper", secs
158         time.sleep(secs)
159         #print "now its time to sleep"
160         self.stop()
161
162     def start_sleeptimer(self, secs):
163         #print "startin sleep"
164         sleep_thread = threading.Thread(target=self.sleeptimer, args=(secs, ))
165         sleep_thread.start()
166         #print "started sleep"
167
168     def get_percentage(self):
169         try:
170             return float(self.player.elapsed()) / float(self.player.duration())
171         except ZeroDivisionError:
172             return 0.0
173
174     def seek_percent(self, ratio):
175         try:
176             target = int(self.player.duration() * ratio) # Calculate where to seek
177             self.player.seek_time(target)      # seek
178
179             position = self.player.elapsed()
180             self.storage.set_time(self.audiobook.current_chapter, target) # save position
181             return True
182         except:
183             _moduleLogger.exception("Seek failed")
184             return False
185
186     def _find_books(self):
187         try:
188             paths = (
189                 os.path.join(self._bookDir, f)
190                 for f in os.listdir(self._bookDir)
191             )
192             return (
193                 path
194                 for path in paths
195                 if Audiobook.is_book(path)
196             )
197         except OSError:
198             return ()