main_screen.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. import hashlib
  2. import json
  3. import logging
  4. import os
  5. import time
  6. import urllib
  7. import urllib2
  8. from threading import Thread
  9. from base_screen import BaseScreen
  10. import mopidy.core
  11. import pygame
  12. from ..graphic_utils import ImageView, Progressbar, \
  13. ScreenObjectsManager, TextItem, TouchAndTextItem
  14. from ..graphic_utils.view_pager import ViewPager
  15. from ..input import InputManager
  16. logger = logging.getLogger(__name__)
  17. class MainScreen(BaseScreen):
  18. def __init__(self, size, base_size, manager, fonts, cache, core,
  19. background):
  20. BaseScreen.__init__(self, size, base_size, manager, fonts)
  21. self.core = core
  22. self.track = None
  23. self.previous_track = None
  24. self.next_track = None
  25. self.cache = cache
  26. self.image = None
  27. self.artists = None
  28. self.update_next_frame = True
  29. self.background = background
  30. self.update_keys = []
  31. self.current_track_pos = 0
  32. self.track_duration = "00:00"
  33. self.view_pager = ViewPager(self.size, self)
  34. self.touch_text_manager = ScreenObjectsManager()
  35. current_track = self.core.playback.current_track.get()
  36. if current_track is None:
  37. self.track_playback_ended(None, None)
  38. else:
  39. self.track_started(current_track)
  40. # Top bar
  41. self.top_bar = pygame.Surface((self.size[0], self.base_size),
  42. pygame.SRCALPHA)
  43. self.top_bar.fill((0, 0, 0, 128))
  44. # Play/pause
  45. button = TouchAndTextItem(self.fonts['icon'], u"\ue615 ",
  46. (0, 0), None)
  47. self.touch_text_manager.set_touch_object("pause_play", button)
  48. x = button.get_right_pos()
  49. # Mute
  50. button = TouchAndTextItem(self.fonts['icon'], u"\ue61f ",
  51. (x, 0), None)
  52. self.touch_text_manager.set_touch_object("mute", button)
  53. x = button.get_right_pos()
  54. # Volume
  55. progress = Progressbar(self.fonts['base'], "100", (x, 0),
  56. (self.size[0] - x, self.base_size),
  57. 100, True)
  58. self.touch_text_manager.set_touch_object("volume", progress)
  59. progress.set_value(self.core.playback.volume.get())
  60. self.progress_show = False
  61. def should_update(self, rects):
  62. update = self.view_pager.should_update()
  63. if update is not BaseScreen.no_update:
  64. return update
  65. elif len(self.update_keys) > 0:
  66. return BaseScreen.update_partial
  67. else:
  68. if self.progress_show:
  69. track_pos_millis = self.core.playback.time_position.get()
  70. new_track_pos = track_pos_millis / 1000
  71. if new_track_pos != self.current_track_pos:
  72. return BaseScreen.update_partial
  73. else:
  74. return BaseScreen.no_update
  75. else:
  76. return BaseScreen.no_update
  77. def set_update_rects(self, rects):
  78. progress = self.update_progress()
  79. if progress is not None:
  80. self.update_keys.append("time_progress")
  81. rects.append(progress.rect_in_pos)
  82. def update(self, screen, update_type):
  83. if update_type == BaseScreen.update_all:
  84. screen.blit(self.top_bar, (0, 0))
  85. self.update_progress()
  86. self.touch_text_manager.render(screen)
  87. self.view_pager.render(screen, update_type)
  88. if update_type == BaseScreen.update_partial \
  89. and self.track is not None:
  90. for key in self.update_keys:
  91. object = self.touch_text_manager.get_object(key)
  92. object.update()
  93. object.render(screen)
  94. self.view_pager.render(screen, update_type)
  95. def update_progress(self):
  96. if self.progress_show:
  97. track_pos_millis = self.core.playback.time_position.get()
  98. new_track_pos = track_pos_millis / 1000
  99. if new_track_pos != self.current_track_pos:
  100. progress = self.touch_text_manager.get_touch_object(
  101. "time_progress")
  102. progress.set_value(track_pos_millis)
  103. self.current_track_pos = new_track_pos
  104. progress.set_text(
  105. time.strftime('%M:%S', time.gmtime(
  106. self.current_track_pos)) +
  107. "/" + self.track_duration)
  108. return progress
  109. return None
  110. def create_objects_manager(self):
  111. touch_text_manager = ScreenObjectsManager()
  112. x = self.size[1] - self.base_size * 3
  113. width = self.size[0] - self.base_size / 2 - x
  114. # Track name
  115. label = TextItem(self.fonts['base'],
  116. "",
  117. (x, (self.size[1]-self.base_size*3)/2
  118. - self.base_size*0.5),
  119. (width, -1))
  120. touch_text_manager.set_object("track_name", label)
  121. # Album name
  122. label = TextItem(self.fonts['base'],
  123. "",
  124. (x, (self.size[1]-self.base_size*3)/2
  125. + self.base_size*0.5),
  126. (width, -1))
  127. touch_text_manager.set_object("album_name", label)
  128. # Artist
  129. label = TextItem(self.fonts['base'],
  130. "",
  131. (x, (self.size[1]-self.base_size*3)/2
  132. + self.base_size*1.5),
  133. (width, -1))
  134. touch_text_manager.set_object("artist_name", label)
  135. # Cover image
  136. pos = (self.base_size / 2, self.base_size +
  137. self.base_size / 2)
  138. size = self.size[1] - self.base_size * 4
  139. cover = ImageView(pos, (size, size))
  140. touch_text_manager.set_object("cover_image", cover)
  141. return touch_text_manager
  142. def set_page_values(self, screen_objects_manager, pos):
  143. if pos == 0:
  144. track = self.previous_track
  145. elif pos == 1:
  146. track = self.track
  147. elif pos == 2:
  148. track = self.next_track
  149. if track is None:
  150. return False
  151. else:
  152. screen_objects_manager.get_object("track_name").set_horizontal_shift(200)
  153. screen_objects_manager.get_object("track_name").set_text(MainScreen.get_track_name(track), False)
  154. screen_objects_manager.get_object("album_name").set_text(MainScreen.get_track_album_name(track), False)
  155. screen_objects_manager.get_object("artist_name").set_text(MainScreen.get_artist_string(track), False)
  156. return True
  157. def track_started(self, track):
  158. self.next_track = track
  159. self.update_keys = []
  160. self.image = None
  161. self.view_pager.notify_changed()
  162. if self.previous_track is not None and track.uri == self.previous_track.uri:
  163. logger.error("sartu naiz")
  164. self.view_pager.change_to_page(-1)
  165. image_view = self.view_pager.objets_manager[0].get_object("cover_image")
  166. else:
  167. self.view_pager.change_to_page(1)
  168. image_view = self.view_pager.objets_manager[2].get_object("cover_image")
  169. image_view.set_image(None)
  170. self.previous_track = self.track
  171. self.track = track
  172. # Previous track button
  173. button = TouchAndTextItem(self.fonts['icon'], u"\ue61c",
  174. (0, self.size[1] - self.base_size * 2), None)
  175. self.touch_text_manager.set_touch_object("previous", button)
  176. size_1 = button.get_right_pos()
  177. size_2 = self.fonts['icon'].size(u"\ue61d")[0]
  178. button = TouchAndTextItem(self.fonts['icon'], u"\ue61d",
  179. (self.size[0] - size_2,
  180. self.size[1] - self.base_size * 2),
  181. None)
  182. self.touch_text_manager.set_touch_object("next", button)
  183. if track.length:
  184. self.track_duration = time.strftime('%M:%S', time.gmtime(
  185. track.length / 1000))
  186. # Progress
  187. progress = Progressbar(self.fonts['base'],
  188. time.strftime('%M:%S', time.gmtime(
  189. 0)) + "/" + time.strftime(
  190. '%M:%S', time.gmtime(0)),
  191. (size_1, self.size[1] - self.base_size * 2),
  192. (
  193. self.size[0] - size_1 - size_2,
  194. self.base_size),
  195. track.length, False)
  196. self.touch_text_manager.set_touch_object("time_progress",
  197. progress)
  198. self.progress_show = True
  199. else:
  200. self.progress_show = False
  201. self.touch_text_manager.delete_touch_object("time_progress")
  202. # Load all artists
  203. self.artists = []
  204. for artist in track.artists:
  205. self.artists.append(artist)
  206. if not self.is_image_in_cache():
  207. thread = Thread(target=self.download_image, args=[image_view])
  208. thread.start()
  209. else:
  210. thread = Thread(target=self.load_image, args=[image_view])
  211. thread.start()
  212. def stream_title_changed(self, title):
  213. self.touch_text_manager.get_object("track_name").set_text(title, False)
  214. def get_image_file_name(self):
  215. name = MainScreen.get_track_album_name(
  216. self.track) + '-' + self.get_artist_string(self.track)
  217. md5name = hashlib.md5(name.encode('utf-8')).hexdigest()
  218. return md5name
  219. def get_cover_folder(self):
  220. if not os.path.isdir(self.cache + "/covers"):
  221. os.makedirs(self.cache + "/covers")
  222. return self.cache + "/covers/"
  223. def is_image_in_cache(self):
  224. self.get_cover_folder()
  225. return os.path.isfile(
  226. self.get_cover_folder() + self.get_image_file_name())
  227. def download_image(self, image_view):
  228. image_uris = self.core.library.get_images(
  229. {self.track.uri}).get()[self.track.uri]
  230. if len(image_uris) > 0:
  231. urllib.urlretrieve(image_uris[0].uri,
  232. self.get_cover_folder() +
  233. self.get_image_file_name())
  234. self.load_image(image_view)
  235. else:
  236. self.download_image_last_fm(0, image_view)
  237. def download_image_last_fm(self, artist_index, image_view):
  238. if artist_index < len(self.artists):
  239. try:
  240. safe_artist = urllib.quote_plus(
  241. self.artists[artist_index].name)
  242. safe_album = urllib.quote_plus(
  243. MainScreen.get_track_album_name(self.track))
  244. url = "http://ws.audioscrobbler.com/2.0/?"
  245. params = "method=album.getinfo&" + \
  246. "api_key=59a04c6a73fb99d6e8996e01db306829&" \
  247. + "artist=" \
  248. + safe_artist + "&album=" + safe_album + \
  249. "&format=json"
  250. response = urllib2.urlopen(url + params)
  251. data = json.load(response)
  252. image = data['album']['image'][-1]['#text']
  253. urllib.urlretrieve(image,
  254. self.get_cover_folder() +
  255. self.get_image_file_name())
  256. self.load_image(image_view)
  257. except:
  258. self.download_image_last_fm(artist_index + 1, image_view)
  259. else:
  260. logger.info("Cover could not be downloaded")
  261. def track_playback_ended(self, tl_track, time_position):
  262. self.background.set_background_image(None)
  263. self.image = None
  264. self.track_duration = "00:00"
  265. def load_image(self, image_view):
  266. size = self.size[1] - self.base_size * 4
  267. image_original = pygame.image.load(
  268. self.get_cover_folder() +
  269. self.get_image_file_name())
  270. image = pygame.transform.scale(image_original, (size, size))
  271. image = image.convert()
  272. image_view.set_image(image)
  273. self.background.set_background_image(image_original)
  274. def touch_event(self, event):
  275. if event.type == InputManager.click:
  276. objects = \
  277. self.touch_text_manager.get_touch_objects_in_pos(
  278. event.current_pos)
  279. if objects is not None:
  280. self.click_on_objects(objects, event)
  281. elif event.type == InputManager.swipe:
  282. if event.direction == InputManager.left:
  283. self.core.playback.next()
  284. elif event.direction == InputManager.right:
  285. self.core.playback.previous()
  286. elif event.direction == InputManager.up:
  287. volume = self.core.playback.volume.get() + 10
  288. if volume > 100:
  289. volume = 100
  290. self.core.mixer.set_volume(volume)
  291. elif event.direction == InputManager.down:
  292. volume = self.core.playback.volume.get() - 10
  293. if volume < 0:
  294. volume = 0
  295. self.core.mixer.set_volume(volume)
  296. elif event.type == InputManager.key:
  297. if event.direction == InputManager.enter:
  298. self.click_on_objects(["pause_play"], None)
  299. elif event.direction == InputManager.up:
  300. vol = self.core.mixer.get_volume().get()
  301. vol += 3
  302. if vol > 100:
  303. vol = 100
  304. self.core.mixer.set_volume(vol)
  305. elif event.direction == InputManager.down:
  306. vol = self.core.mixer.get_volume().get()
  307. vol -= 3
  308. if vol < 0:
  309. vol = 0
  310. self.core.mixer.set_volume(vol)
  311. elif event.longpress:
  312. if event.direction == InputManager.left:
  313. self.click_on_objects(["previous"], None)
  314. elif event.direction == InputManager.right:
  315. self.click_on_objects(["next"], None)
  316. def click_on_objects(self, objects, event):
  317. if objects is not None:
  318. for key in objects:
  319. if key == "time_progress":
  320. value = self.touch_text_manager.get_touch_object(
  321. key).get_pos_value(
  322. event.current_pos)
  323. self.core.playback.seek(value)
  324. elif key == "previous":
  325. self.core.playback.previous()
  326. elif key == "next":
  327. self.core.playback.next()
  328. elif key == "volume":
  329. self.change_volume(event)
  330. elif key == "pause_play":
  331. if self.core.playback.state.get() == \
  332. mopidy.core.PlaybackState.PLAYING:
  333. self.core.playback.pause()
  334. else:
  335. self.core.playback.play()
  336. elif key == "mute":
  337. mute = not self.core.playback.mute.get()
  338. self.core.playback.set_mute(mute)
  339. self.mute_changed(mute)
  340. def change_volume(self, event):
  341. manager = self.touch_text_manager
  342. volume = manager.get_touch_object("volume")
  343. pos = event.current_pos
  344. value = volume.get_pos_value(pos)
  345. self.core.mixer.set_volume(value)
  346. def playback_state_changed(self, old_state, new_state):
  347. if new_state == mopidy.core.PlaybackState.PLAYING:
  348. self.touch_text_manager.get_touch_object(
  349. "pause_play").set_text(u"\ue616", False)
  350. else:
  351. self.touch_text_manager.get_touch_object(
  352. "pause_play").set_text(u"\ue615", False)
  353. def volume_changed(self, volume):
  354. if not self.core.playback.mute.get():
  355. if volume > 80:
  356. self.touch_text_manager.get_touch_object(
  357. "mute").set_text(
  358. u"\ue61f", False)
  359. elif volume > 50:
  360. self.touch_text_manager.get_touch_object(
  361. "mute").set_text(
  362. u"\ue620", False)
  363. elif volume > 20:
  364. self.touch_text_manager.get_touch_object(
  365. "mute").set_text(
  366. u"\ue621", False)
  367. else:
  368. self.touch_text_manager.get_touch_object(
  369. "mute").set_text(
  370. u"\ue622", False)
  371. self.touch_text_manager.get_touch_object("volume").set_value(
  372. volume)
  373. def mute_changed(self, mute):
  374. self.touch_text_manager.get_touch_object("mute").set_active(
  375. not mute)
  376. if mute:
  377. self.touch_text_manager.get_touch_object("mute").set_text(
  378. u"\ue623", False)
  379. else:
  380. self.volume_changed(self.core.playback.volume.get())
  381. @staticmethod
  382. def get_track_name(track):
  383. if track is None:
  384. return ""
  385. elif track.name is None:
  386. return track.uri
  387. else:
  388. return track.name
  389. @staticmethod
  390. def get_track_album_name(track):
  391. if track is None:
  392. return ""
  393. if track.album is not None and track.album.name is not None \
  394. and len(track.album.name) > 0:
  395. return track.album.name
  396. else:
  397. return "Unknow Album"
  398. @staticmethod
  399. def get_artist_string(track):
  400. artists_string = ''
  401. if track is None:
  402. return artists_string
  403. for artist in track.artists:
  404. artists_string += artist.name + ', '
  405. if len(artists_string) > 2:
  406. artists_string = artists_string[:-2]
  407. elif len(artists_string) == 0:
  408. artists_string = "Unknow Artist"
  409. return artists_string