main_screen.py 19 KB

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