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.

448 lines
12 KiB

10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
9 years ago
10 years ago
10 years ago
9 years ago
  1. #!/usr/bin/env python2
  2. # -*- coding: utf-8 -*-
  3. """Youtubedlg module that contains util functions.
  4. Attributes:
  5. _RANDOM_OBJECT (object): Object that it's used as a default parameter.
  6. YOUTUBEDL_BIN (string): Youtube-dl binary filename.
  7. """
  8. from __future__ import unicode_literals
  9. import os
  10. import sys
  11. import locale
  12. import subprocess
  13. from .info import __appname__
  14. _RANDOM_OBJECT = object()
  15. YOUTUBEDL_BIN = 'youtube-dl'
  16. if os.name == 'nt':
  17. YOUTUBEDL_BIN += '.exe'
  18. def remove_shortcuts(path):
  19. """Return given path after removing the shortcuts. """
  20. path = path.replace('~', os.path.expanduser('~'))
  21. return path
  22. def absolute_path(filename):
  23. """Return absolute path to the given file. """
  24. path = os.path.realpath(os.path.abspath(filename))
  25. return os.path.dirname(path).decode(get_encoding(), 'ignore')
  26. def get_lib_path():
  27. """Return path to the current file. """
  28. return os.path.dirname(__file__).decode(get_encoding(), 'ignore')
  29. def open_dir(path):
  30. """Open path using default file navigator.
  31. Return True if path exists else False. """
  32. path = remove_shortcuts(path)
  33. if not os.path.exists(path):
  34. return False
  35. if os.name == 'nt':
  36. os.startfile(path)
  37. else:
  38. subprocess.call(('xdg-open', path))
  39. return True
  40. def encode_tuple(tuple_to_encode):
  41. """Turn size tuple into string. """
  42. return '%s/%s' % (tuple_to_encode[0], tuple_to_encode[1])
  43. def decode_tuple(encoded_tuple):
  44. """Turn tuple string back to tuple. """
  45. s = encoded_tuple.split('/')
  46. return int(s[0]), int(s[1])
  47. def check_path(path):
  48. """Create path if not exist. """
  49. if not os.path.exists(path):
  50. os.makedirs(path)
  51. def get_config_path():
  52. """Return user config path.
  53. Note:
  54. Windows = %AppData% + app_name
  55. Linux = ~/.config + app_name
  56. """
  57. if os.name == 'nt':
  58. path = os.getenv('APPDATA')
  59. else:
  60. path = os.path.join(os.path.expanduser('~'), '.config')
  61. return os.path.join(path, __appname__.lower())
  62. def shutdown_sys(password=None):
  63. """Shuts down the system.
  64. Returns True if no errors occur else False.
  65. Args:
  66. password (string): SUDO password for linux.
  67. Note:
  68. On Linux you need to provide sudo password if you don't
  69. have elevated privileges.
  70. """
  71. _stderr = subprocess.PIPE
  72. _stdin = None
  73. info = None
  74. encoding = get_encoding()
  75. if os.name == 'nt':
  76. cmd = ['shutdown', '/s', '/t', '1']
  77. # Hide subprocess window
  78. info = subprocess.STARTUPINFO()
  79. info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
  80. else:
  81. if password:
  82. _stdin = subprocess.PIPE
  83. password = ('%s\n' % password).encode(encoding)
  84. cmd = ['sudo', '-S', '/sbin/shutdown', '-h', 'now']
  85. else:
  86. cmd = ['/sbin/shutdown', '-h', 'now']
  87. cmd = [item.encode(encoding, 'ignore') for item in cmd]
  88. shutdown_proc = subprocess.Popen(cmd,
  89. stderr=_stderr,
  90. stdin=_stdin,
  91. startupinfo=info)
  92. output = shutdown_proc.communicate(password)[1]
  93. return not output or output == "Password:"
  94. def to_string(data):
  95. """Convert data to string.
  96. Works for both Python2 & Python3. """
  97. return '%s' % data
  98. def get_time(seconds):
  99. """Convert given seconds to days, hours, minutes and seconds.
  100. Args:
  101. seconds (float): Time in seconds.
  102. Returns:
  103. Dictionary that contains the corresponding days, hours, minutes
  104. and seconds of the given seconds.
  105. """
  106. dtime = dict(seconds=0, minutes=0, hours=0, days=0)
  107. dtime['days'] = int(seconds / 86400)
  108. dtime['hours'] = int(seconds % 86400 / 3600)
  109. dtime['minutes'] = int(seconds % 86400 % 3600 / 60)
  110. dtime['seconds'] = int(seconds % 86400 % 3600 % 60)
  111. return dtime
  112. def get_encoding():
  113. """Return system encoding. """
  114. try:
  115. encoding = locale.getpreferredencoding()
  116. 'TEST'.encode(encoding)
  117. except:
  118. encoding = 'UTF-8'
  119. return encoding
  120. def get_locale_file():
  121. """Search for youtube-dlg locale file.
  122. Returns:
  123. The path to youtube-dlg locale file if exists else None.
  124. Note:
  125. Paths that get_locale_file() func searches.
  126. __main__ dir, library dir, /usr/share/youtube-dlg/locale
  127. """
  128. DIR_NAME = 'locale'
  129. SEARCH_DIRS = [
  130. os.path.join(absolute_path(sys.argv[0]), DIR_NAME),
  131. os.path.join(get_lib_path(), DIR_NAME),
  132. os.path.join('/usr', 'share', __appname__.lower(), DIR_NAME)
  133. ]
  134. for directory in SEARCH_DIRS:
  135. if os.path.isdir(directory):
  136. return directory
  137. return None
  138. def get_icon_file():
  139. """Search for youtube-dlg app icon.
  140. Returns:
  141. The path to youtube-dlg icon file if exists, else returns None.
  142. Note:
  143. Paths that get_icon_file() function searches.
  144. __main__ dir, library dir, /usr/share/pixmaps, $XDG_DATA_DIRS
  145. """
  146. SIZES = ('256x256', '128x128', '64x64', '48x48', '32x32', '16x16')
  147. ICON_NAME = 'youtube-dl-gui_%s.png'
  148. ICONS_LIST = [ICON_NAME % size for size in SIZES]
  149. search_dirs = [
  150. os.path.join(absolute_path(sys.argv[0]), 'icons'),
  151. os.path.join(get_lib_path(), 'icons'),
  152. ]
  153. # Append $XDG_DATA_DIRS on search_dirs
  154. path = os.getenv('XDG_DATA_DIRS')
  155. if path is not None:
  156. for xdg_path in path.split(':'):
  157. xdg_path = os.path.join(xdg_path, 'icons', 'hicolor')
  158. for size in SIZES:
  159. search_dirs.append(os.path.join(xdg_path, size, 'apps'))
  160. # Also append /usr/share/pixmaps on search_dirs
  161. search_dirs.append('/usr/share/pixmaps')
  162. for directory in search_dirs:
  163. for icon in ICONS_LIST:
  164. icon_file = os.path.join(directory, icon)
  165. if os.path.exists(icon_file):
  166. return icon_file
  167. return None
  168. class TwoWayOrderedDict(dict):
  169. """Custom data structure which implements a two way ordrered dictionary.
  170. TwoWayOrderedDict it's a custom dictionary in which you can get the
  171. key:value relationship but you can also get the value:key relationship.
  172. It also remembers the order in which the items were inserted and supports
  173. almost all the features of the build-in dict.
  174. Note:
  175. Ways to create a new dictionary.
  176. *) d = TwoWayOrderedDict(a=1, b=2) (Unordered)
  177. *) d = TwoWayOrderedDict({'a': 1, 'b': 2}) (Unordered)
  178. *) d = TwoWayOrderedDict([('a', 1), ('b', 2)]) (Ordered)
  179. *) d = TwoWayOrderedDict(zip(['a', 'b', 'c'], [1, 2, 3])) (Ordered)
  180. Examples:
  181. >>> d = TwoWayOrderedDict(a=1, b=2)
  182. >>> d['a']
  183. 1
  184. >>> d[1]
  185. 'a'
  186. >>> print d
  187. TwoWayOrderedDict([('a', 1), ('b', 2)])
  188. """
  189. _PREV = 0
  190. _KEY = 1
  191. _NEXT = 2
  192. def __init__(self, *args, **kwargs):
  193. self._items = item = []
  194. self._items += [item, None, item] # Double linked list [prev, key, next]
  195. self._items_map = {} # Map link list items into keys to speed up lookup
  196. self._load(args, kwargs)
  197. def __setitem__(self, key, value):
  198. if key in self:
  199. # If self[key] == key for example {'b': 'b'} and we
  200. # do d['b'] = 2 then we dont want to remove the 'b'
  201. # from our linked list because we will lose the order
  202. if self[key] in self._items_map and key != self[key]:
  203. self._remove_mapped_key(self[key])
  204. dict.__delitem__(self, self[key])
  205. if value in self:
  206. # If value == key we dont have to remove the
  207. # value from the items_map because the value is
  208. # the key and we want to keep the key in our
  209. # linked list in order to keep the order.
  210. if value in self._items_map and key != value:
  211. self._remove_mapped_key(value)
  212. if self[value] in self._items_map:
  213. self._remove_mapped_key(self[value])
  214. # Check if self[value] is in the dict
  215. # for cases like {'a': 'a'} where we
  216. # have only one copy instead of {'a': 1, 1: 'a'}
  217. if self[value] in self:
  218. dict.__delitem__(self, self[value])
  219. if key not in self._items_map:
  220. last = self._items[self._PREV] # self._items prev always points to the last item
  221. last[self._NEXT] = self._items[self._PREV] = self._items_map[key] = [last, key, self._items]
  222. dict.__setitem__(self, key, value)
  223. dict.__setitem__(self, value, key)
  224. def __delitem__(self, key):
  225. if self[key] in self._items_map:
  226. self._remove_mapped_key(self[key])
  227. if key in self._items_map:
  228. self._remove_mapped_key(key)
  229. dict.__delitem__(self, self[key])
  230. # Check if key is in the dict
  231. # for cases like {'a': 'a'} where we
  232. # have only one copy instead of {'a': 1, 1: 'a'}
  233. if key in self:
  234. dict.__delitem__(self, key)
  235. def __len__(self):
  236. return len(self._items_map)
  237. def __iter__(self):
  238. curr = self._items[self._NEXT]
  239. while curr is not self._items:
  240. yield curr[self._KEY]
  241. curr = curr[self._NEXT]
  242. def __reversed__(self):
  243. curr = self._items[self._PREV]
  244. while curr is not self._items:
  245. yield curr[self._KEY]
  246. curr = curr[self._PREV]
  247. def __repr__(self):
  248. return '%s(%r)' % (self.__class__.__name__, self.items())
  249. def __eq__(self, other):
  250. if isinstance(other, self.__class__):
  251. return self.items() == other.items()
  252. return False
  253. def __ne__(self, other):
  254. return not self == other
  255. def _remove_mapped_key(self, key):
  256. """Remove the given key both from the linked list
  257. and the map dictionary. """
  258. prev, __, next = self._items_map.pop(key)
  259. prev[self._NEXT] = next
  260. next[self._PREV] = prev
  261. def _load(self, args, kwargs):
  262. """Load items into our dictionary. """
  263. for item in args:
  264. if type(item) == dict:
  265. item = item.iteritems()
  266. for key, value in item:
  267. self[key] = value
  268. for key, value in kwargs.items():
  269. self[key] = value
  270. def items(self):
  271. return [(key, self[key]) for key in self]
  272. def values(self):
  273. return [self[key] for key in self]
  274. def keys(self):
  275. return list(self)
  276. def pop(self, key, default=_RANDOM_OBJECT):
  277. try:
  278. value = self[key]
  279. del self[key]
  280. except KeyError as error:
  281. if default == _RANDOM_OBJECT:
  282. raise error
  283. value = default
  284. return value
  285. def popitem(self, last=True):
  286. """Remove and return a (key, value) pair from the dictionary.
  287. If the dictionary is empty calling popitem() raises a KeyError.
  288. Args:
  289. last (bool): When False popitem() will remove the first item
  290. from the list.
  291. Note:
  292. popitem() is useful to destructively iterate over a dictionary.
  293. Raises:
  294. KeyError
  295. """
  296. if not self:
  297. raise KeyError('popitem(): dictionary is empty')
  298. if last:
  299. __, key, __ = self._items[self._PREV]
  300. else:
  301. __, key, __ = self._items[self._NEXT]
  302. value = self.pop(key)
  303. return key, value
  304. def update(self, *args, **kwargs):
  305. self._load(args, kwargs)
  306. def setdefault(self, key, default=None):
  307. try:
  308. return self[key]
  309. except KeyError:
  310. self[key] = default
  311. return default
  312. def copy(self):
  313. return self.__class__(self.items())
  314. def clear(self):
  315. self._items = item = []
  316. self._items += [item, None, item]
  317. self._items_map = {}
  318. dict.clear(self)