main_screen.py 18 KB

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