main_screen.py 19 KB

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