main_screen.py 17 KB

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