Django Library PyGal
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. from django.shortcuts import render
  2. from django.utils.translation import gettext as _
  3. import geo
  4. import mimetypes
  5. from ..models import get_item_type, get_item_by_rel_path, TYPE_AUDIO, TYPE_FOLDER, TYPE_IMAGE, TYPE_OTHER, TYPE_VIDEO
  6. import os
  7. import pygal
  8. from ..queries import search_result_query
  9. import random
  10. import stringtools
  11. import themes
  12. ADDTAG_ENTRY = 'addtag-main'
  13. INFOVIEW_ENTRY = 'infoview-main'
  14. DOWNLOAD_ENTRY = 'download-main'
  15. DOWNLOAD_FLAT_ENTRY = 'download-flat-sub'
  16. DOWNLOAD_STRUCT_ENTRY = 'download-struct-sub'
  17. FAVOURITE_ENTRY = 'favourite-main'
  18. REPEAT_ENTRY = 'repeat-main'
  19. SHUFFLE_ENTRY = 'shuffle-main'
  20. def get_wrapper_class(full_path):
  21. return {
  22. TYPE_FOLDER: folder_view,
  23. TYPE_IMAGE: image_view,
  24. TYPE_VIDEO: video_view,
  25. TYPE_AUDIO: audio_view,
  26. TYPE_OTHER: other_view,
  27. }.get(get_item_type(full_path), base_view)
  28. def get_wrapper_instance(full_path, request, is_nxt_prv_item=False):
  29. return get_wrapper_class(full_path)(request, full_path, is_nxt_prv_item)
  30. def random_copy(request, lst):
  31. shuffle_id = pygal.get_shuffle_id(request)
  32. #
  33. rv = lst[:]
  34. if shuffle_id is not None:
  35. r = random.Random()
  36. r.seed(shuffle_id)
  37. r.shuffle(rv)
  38. return rv
  39. class base_view(object):
  40. def __init__(self, request, full_path, is_nxt_prv_item):
  41. self.request = request
  42. self.full_path = full_path
  43. self.is_nxt_prv_item = is_nxt_prv_item
  44. #
  45. self.item = get_item_by_rel_path(pygal.get_rel_path(full_path))
  46. if self.item is None:
  47. raise LookupError("Error while initialising base_view, but item for %s does not exist." % full_path)
  48. self.is_item = self.item.type != TYPE_FOLDER
  49. #
  50. self.mime_type = mimetypes.types_map.get(os.path.splitext(self.full_path)[1])
  51. self.template = 'pygal/empty.html'
  52. @property
  53. def tags(self):
  54. return self.item.tag_set.all()
  55. def may_read(self):
  56. return self.item.may_read(self.request.user)
  57. def may_modify(self):
  58. return self.item.may_modify(self.request.user)
  59. def context_adaption(self, context):
  60. context['item'] = self
  61. context['thumbnail_size'] = pygal.get_thumbnail_size(self.request)
  62. context['title'] = self.name or ''
  63. context[context.ACTIONBAR].extend(self.actionbar)
  64. @property
  65. def heading(self):
  66. return '%s' % self.item.name
  67. @property
  68. def general_information(self):
  69. rv = []
  70. full_path = pygal.get_full_path(self.item.rel_path)
  71. rv.append({'description': _('Name'), 'data': os.path.basename(full_path), 'url': None})
  72. rv.append({'description': _('Creation Time'), 'data': self.item.item_data.formatted_datetime, 'url': None})
  73. rv.append({'description': _('Path'), 'data': os.path.dirname(full_path), 'url': None})
  74. rv.append({'description': _('Size'), 'data': stringtools.physical_value_repr(self.item.item_data.size, 'B'), 'url': None})
  75. rv.append({'description': _('UID'), 'data': self.item.current_uid(), 'url': None})
  76. return rv
  77. def __actionbar__(self):
  78. b = themes.bar()
  79. if self.is_item:
  80. if self.is_my_favourite:
  81. b.append_entry(
  82. FAVOURITE_ENTRY,
  83. '',
  84. themes.color_icon_url(self.request, 'is_favourite.png'),
  85. pygal.url_favourite_unset(self.request, self.item.rel_path),
  86. True,
  87. False
  88. )
  89. else:
  90. b.append_entry(
  91. FAVOURITE_ENTRY,
  92. '',
  93. themes.color_icon_url(self.request, 'favourite.png'),
  94. pygal.url_favourite_set(self.request, self.item.rel_path),
  95. True,
  96. False
  97. )
  98. if pygal.is_infoview(self.request):
  99. b.append_entry(
  100. INFOVIEW_ENTRY,
  101. _('Info'),
  102. themes.color_icon_url(self.request, 'info.png'),
  103. pygal.url_infoview(self.request, self.item.rel_path),
  104. True,
  105. True
  106. )
  107. else:
  108. b.append_entry(
  109. INFOVIEW_ENTRY,
  110. _('Info'),
  111. themes.color_icon_url(self.request, 'info.png'),
  112. pygal.url_infoview(self.request, self.item.rel_path, search=pygal.SEARCH_KEEP),
  113. True,
  114. False
  115. )
  116. b.append_entry(
  117. DOWNLOAD_ENTRY,
  118. _('Download'),
  119. themes.color_icon_url(self.request, 'download.png'),
  120. pygal.url_download(self.request, self.item.rel_path),
  121. True,
  122. False
  123. )
  124. if type(self) is not folder_view:
  125. if pygal.is_repeatview(self.request):
  126. b.append_entry(
  127. REPEAT_ENTRY,
  128. _('Repeat'),
  129. themes.color_icon_url(self.request, 'stop.png'),
  130. pygal.url_userview(self.request, self.item.rel_path, search=pygal.SEARCH_KEEP),
  131. True,
  132. True
  133. )
  134. else:
  135. b.append_entry(
  136. REPEAT_ENTRY,
  137. _('Repeat'),
  138. themes.color_icon_url(self.request, 'play.png'),
  139. pygal.url_repeatview(self.request, self.item.rel_path),
  140. True,
  141. False
  142. )
  143. if pygal.get_shuffle_id(self.request) is None:
  144. b.append_entry(
  145. SHUFFLE_ENTRY,
  146. _('Shuffle'),
  147. themes.color_icon_url(self.request, 'shuffle.png'),
  148. pygal.url_userview(self.request, self.item.rel_path, shuffle=pygal.SHUFFLE_ENABLE, search=pygal.SEARCH_KEEP),
  149. True,
  150. False
  151. )
  152. else:
  153. b.append_entry(
  154. SHUFFLE_ENTRY,
  155. _('Shuffle'),
  156. themes.color_icon_url(self.request, 'shuffle.png'),
  157. pygal.url_userview(self.request, self.item.rel_path, shuffle=pygal.SHUFFLE_DISABLE, search=pygal.SEARCH_KEEP),
  158. True,
  159. True
  160. )
  161. if self.is_item and self.may_modify():
  162. b.append_entry(
  163. ADDTAG_ENTRY,
  164. _('Add Tag'),
  165. themes.color_icon_url(self.request, 'edit2.png'),
  166. pygal.url_addtag(self.request, self.item.rel_path),
  167. True,
  168. False
  169. )
  170. return b
  171. @property
  172. def is_my_favourite(self):
  173. return self.request.user in self.item.favourite_of.all()
  174. @property
  175. def actionbar(self):
  176. return self.__actionbar__()
  177. def __tagbar__(self):
  178. b = themes.bar(self.request)
  179. i = 0
  180. for t in self.item.tag_set.all():
  181. i += 1
  182. edit_url = pygal.url_tagedit(self.request, t.id) if self.may_modify() else None
  183. search_query = 'tag:%s' % t.text
  184. search_url = pygal.url_userview(self.request, '', search=search_query)
  185. b.append_entry('tag-%d' % i, t.text, themes.color_icon_url(self.request, '%d.png' % (i % 10)), edit_url, True, False)
  186. if pygal.get_search_query(self.request) != search_query:
  187. b.append_entry_to_entry('tag-%d' % i, 'tagedit-%d' % i, _('Edit'), themes.gray_icon_url(self.request, 'edit2.png'), edit_url, True, False)
  188. b.append_entry_to_entry('tag-%d' % i, 'tagsearch-%d' % i, _('Search'), themes.gray_icon_url(self.request, 'search.png'), search_url, True, False)
  189. return b
  190. @property
  191. def tagbar(self):
  192. return self.__tagbar__()
  193. @property
  194. def duration(self):
  195. return self.item.item_data.duration + 1.5
  196. @property
  197. def url_item(self):
  198. return pygal.url_item(self.request, self.item.rel_path)
  199. @property
  200. def url_repeatview(self):
  201. return pygal.url_repeatview(self.request, self.item.rel_path)
  202. @property
  203. def is_repeatview(self):
  204. return pygal.is_repeatview(self.request)
  205. @property
  206. def url_thumbnail(self):
  207. return pygal.url_thumbnail(self.request, self.item.rel_path)
  208. @property
  209. def url_userview(self):
  210. if self.is_nxt_prv_item:
  211. return pygal.url_userview(self.request, self.item.rel_path, search=pygal.SEARCH_KEEP, repeat=pygal.REPEAT_KEEP)
  212. else:
  213. return pygal.url_userview(self.request, self.item.rel_path, search=pygal.SEARCH_KEEP, repeat=pygal.REPEAT_DISABLE)
  214. @property
  215. def url_webnail(self):
  216. return pygal.url_webnail(self.request, self.item.rel_path)
  217. @property
  218. def name(self):
  219. return self.item.name
  220. @property
  221. def date_txt(self):
  222. return self.item.item_data.formatted_datetime
  223. def __parent__(self):
  224. if pygal.is_searchview(self.request) or pygal.is_favouriteview(self.request):
  225. return query_view(self.request, search_result_query(self.request))
  226. else:
  227. return get_wrapper_instance(os.path.dirname(self.full_path), self.request)
  228. def __nxt_prv__(self, direction):
  229. if direction not in [-1, 1]:
  230. raise ValueError("Parameter direction is incorrect: %s" % repr(direction))
  231. fp_il = random_copy(self.request, self.__parent__().sorted_fullpathlist())
  232. i = fp_il.index(self.full_path)
  233. lgt = len(fp_il)
  234. for i in range(i + direction, i + direction * lgt, direction):
  235. full_path = fp_il[i % lgt]
  236. if get_item_type(full_path) != TYPE_FOLDER:
  237. return get_wrapper_instance(full_path, self.request, is_nxt_prv_item=True)
  238. return self
  239. @property
  240. def nxt(self):
  241. return self.__nxt_prv__(1)
  242. @property
  243. def prv(self):
  244. return self.__nxt_prv__(-1)
  245. def render(self, context):
  246. return render(self.request, self.template, context=context)
  247. class query_view(object):
  248. def __init__(self, request, query):
  249. self.request = request
  250. self.query = query
  251. #
  252. self.template = 'pygal/overview.html'
  253. #
  254. self.__item_list__ = None
  255. def context_adaption(self, context):
  256. context['item'] = self
  257. context['thumbnail_size'] = pygal.get_thumbnail_size(self.request)
  258. context['title'] = self.name or ''
  259. context[context.ACTIONBAR].extend(self.actionbar)
  260. @property
  261. def name(self):
  262. if pygal.is_favouriteview(self.request):
  263. return _('My Favourites')
  264. elif pygal.is_searchview(self.request):
  265. return _('Search: "%s"' % pygal.get_search_query(self.request))
  266. else:
  267. return '-'
  268. def __actionbar__(self):
  269. b = themes.bar()
  270. if pygal.is_infoview(self.request):
  271. b.append_entry(
  272. INFOVIEW_ENTRY,
  273. _('Info'),
  274. themes.color_icon_url(self.request, 'info.png'),
  275. pygal.url_infoview(self.request, ''),
  276. True,
  277. True
  278. )
  279. else:
  280. b.append_entry(
  281. INFOVIEW_ENTRY,
  282. _('Info'),
  283. themes.color_icon_url(self.request, 'info.png'),
  284. pygal.url_infoview(self.request, ''),
  285. True,
  286. False
  287. )
  288. b.append_entry(
  289. DOWNLOAD_ENTRY,
  290. _('Download'),
  291. themes.color_icon_url(self.request, 'download.png'),
  292. None,
  293. True,
  294. False
  295. )
  296. b.append_entry_to_entry(
  297. DOWNLOAD_ENTRY,
  298. DOWNLOAD_STRUCT_ENTRY,
  299. _('Download (Structured)'),
  300. themes.gray_icon_url(self.request, 'download.png'),
  301. pygal.url_download(self.request, ''),
  302. True,
  303. False
  304. )
  305. b.append_entry_to_entry(
  306. DOWNLOAD_ENTRY,
  307. DOWNLOAD_FLAT_ENTRY,
  308. _('Download (Flat)'),
  309. themes.gray_icon_url(self.request, 'download.png'),
  310. pygal.url_download(self.request, '', flat=True),
  311. True,
  312. False
  313. )
  314. if pygal.get_shuffle_id(self.request) is None:
  315. b.append_entry(
  316. SHUFFLE_ENTRY,
  317. _('Shuffle'),
  318. themes.color_icon_url(self.request, 'shuffle.png'),
  319. pygal.url_userview(self.request, '', shuffle=pygal.SHUFFLE_ENABLE, search=pygal.SEARCH_KEEP),
  320. True,
  321. False
  322. )
  323. else:
  324. b.append_entry(
  325. SHUFFLE_ENTRY,
  326. _('Shuffle'),
  327. themes.color_icon_url(self.request, 'shuffle.png'),
  328. pygal.url_userview(self.request, '', shuffle=pygal.SHUFFLE_DISABLE, search=pygal.SEARCH_KEEP),
  329. True,
  330. True
  331. )
  332. return b
  333. @property
  334. def actionbar(self):
  335. return self.__actionbar__()
  336. def sorted_fullpathlist(self):
  337. il = []
  338. for item_model in self.query:
  339. if item_model.may_read(self.request.user):
  340. il.append(item_model)
  341. il.sort(key=lambda entry: entry.sort_string(), reverse=True)
  342. return [pygal.get_full_path(i.rel_path) for i in il]
  343. def __init_itemlist__(self):
  344. if self.__item_list__ is None:
  345. self.__item_list__ = []
  346. for full_path in self.sorted_fullpathlist():
  347. w = get_wrapper_instance(full_path, self.request)
  348. self.__item_list__.append(w)
  349. @property
  350. def item_list(self):
  351. self.__init_itemlist__()
  352. return random_copy(self.request, self.__item_list__)
  353. def render(self, context):
  354. return render(self.request, self.template, context=context)
  355. class folder_view(base_view):
  356. def __init__(self, *args, **kwargs):
  357. base_view.__init__(self, *args, **kwargs)
  358. #
  359. self.template = 'pygal/overview.html'
  360. #
  361. self.__item_list__ = None
  362. def __actionbar__(self):
  363. b = base_view.__actionbar__(self)
  364. b.append_entry_to_entry(
  365. DOWNLOAD_ENTRY,
  366. DOWNLOAD_STRUCT_ENTRY,
  367. _('Download (Structured)'),
  368. themes.gray_icon_url(self.request, 'download.png'),
  369. pygal.url_download(self.request, self.item.rel_path),
  370. True,
  371. False
  372. )
  373. b.append_entry_to_entry(
  374. DOWNLOAD_ENTRY,
  375. DOWNLOAD_FLAT_ENTRY,
  376. _('Download (Flat)'),
  377. themes.gray_icon_url(self.request, 'download.png'),
  378. pygal.url_download(self.request, self.item.rel_path, flat=True),
  379. True,
  380. False
  381. )
  382. return b
  383. def sorted_fullpathlist(self):
  384. return [pygal.get_full_path(i.rel_path) for i in self.item.sorted_itemlist()]
  385. def __init_fullpathlist__(self):
  386. if self.__item_list__ is None:
  387. self.__item_list__ = []
  388. for full_path in self.sorted_fullpathlist():
  389. w = get_wrapper_instance(full_path, self.request)
  390. self.__item_list__.append(w)
  391. @property
  392. def item_list(self):
  393. self.__init_fullpathlist__()
  394. return random_copy(self.request, self.__item_list__)
  395. @property
  396. def url_thumbnail(self):
  397. return pygal.url_thumbnail(self. request, self.item.thumbnail_item().rel_path)
  398. class image_view(base_view):
  399. def __init__(self, *args, **kwargs):
  400. base_view.__init__(self, *args, **kwargs)
  401. #
  402. self.template = 'pygal/image.html'
  403. def __actionbar__(self):
  404. b = base_view.__actionbar__(self)
  405. gps_data = self.item.item_data.gps
  406. if gps_data is not None:
  407. b.append_entry(
  408. 'actionbar-gpslink',
  409. _('GPS'),
  410. themes.color_icon_url(self.request, 'gps.png'),
  411. geo.osm.landmark_link(geo.gps.coordinate(**gps_data)),
  412. True,
  413. False
  414. )
  415. return b
  416. @property
  417. def width(self):
  418. if self.item.item_data.orientation in [5, 6, 7, 8]:
  419. return self.item.item_data.height
  420. else:
  421. return self.item.item_data.width
  422. @property
  423. def height(self):
  424. if self.item.item_data.orientation in [5, 6, 7, 8]:
  425. return self.item.item_data.width
  426. else:
  427. return self.item.item_data.height
  428. class video_view(base_view):
  429. def __init__(self, *args, **kwargs):
  430. base_view.__init__(self, *args, **kwargs)
  431. #
  432. self.template = 'pygal/video.html'
  433. @property
  434. def use_internal_player(self):
  435. return self.mime_type in [mimetypes.types_map.get(ext) for ext in ['.mp4', '.webm', '.ogv', '.flv', '.3gp', ]]
  436. @property
  437. def webnail_width(self):
  438. w, h = self.item.item_data.width, self.item.item_data.height
  439. return int(pygal.get_webnail_size(self.request) * w / max(w, h))
  440. @property
  441. def webnail_height(self):
  442. w, h = self.item.item_data.width, self.item.item_data.height
  443. return int(pygal.get_webnail_size(self.request) * h / max(w, h))
  444. class audio_view(base_view):
  445. def __init__(self, *args, **kwargs):
  446. base_view.__init__(self, *args, **kwargs)
  447. #
  448. self.template = 'pygal/audio.html'
  449. @property
  450. def heading(self):
  451. return '%02d - %s' % (self.item.item_data.track, self.item.item_data.title)
  452. @property
  453. def audio_information(self):
  454. rv = []
  455. entries = (
  456. (_('Artist'), self.item.artist_c),
  457. (_('Album'), self.item.album_c),
  458. (_('Year'), self.item.year_c),
  459. (_('Title'), self.item.title_c),
  460. (_('Duration'), stringtools.time_repr(self.item.duration_c)),
  461. )
  462. for description, data in entries:
  463. if data is not None:
  464. rv.append({'description': description, 'data': data, 'url': None})
  465. return rv
  466. class other_view(base_view):
  467. def __init__(self, *args, **kwargs):
  468. base_view.__init__(self, *args, **kwargs)
  469. #
  470. self.template = 'pygal/other.html'