-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathutils.py
403 lines (276 loc) · 9.92 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""Youtubedlg module that contains util functions.
Attributes:
_RANDOM_OBJECT (object): Object that it's used as a default parameter.
YOUTUBEDL_BIN (string): Youtube-dl binary filename.
"""
from __future__ import unicode_literals
import os
import sys
import json
import math
import errno
import locale
import subprocess
try:
from twodict import TwoWayOrderedDict
except ImportError as error:
print error
sys.exit(1)
from .info import __appname__
from .version import __version__
_RANDOM_OBJECT = object()
YOUTUBEDL_BIN = 'youtube-dl'
if os.name == 'nt':
YOUTUBEDL_BIN += '.exe'
FILESIZE_METRICS = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"]
KILO_SIZE = 1024.0
def get_encoding():
"""Return system encoding. """
try:
encoding = locale.getpreferredencoding()
'TEST'.encode(encoding)
except:
encoding = 'UTF-8'
return encoding
def convert_item(item, to_unicode=False):
"""Convert item between 'unicode' and 'str'.
Args:
item (-): Can be any python item.
to_unicode (boolean): When True it will convert all the 'str' types
to 'unicode'. When False it will convert all the 'unicode'
types back to 'str'.
"""
if to_unicode and isinstance(item, str):
# Convert str to unicode
return item.decode(get_encoding(), 'ignore')
if not to_unicode and isinstance(item, unicode):
# Convert unicode to str
return item.encode(get_encoding(), 'ignore')
if hasattr(item, '__iter__'):
# Handle iterables
temp_list = []
for sub_item in item:
if isinstance(item, dict):
temp_list.append((convert_item(sub_item, to_unicode), convert_item(item[sub_item], to_unicode)))
else:
temp_list.append(convert_item(sub_item, to_unicode))
return type(item)(temp_list)
return item
def convert_on_bounds(func):
"""Decorator to convert string inputs & outputs.
Covert string inputs & outputs between 'str' and 'unicode' at the
application bounds using the preferred system encoding. It will convert
all the string params (args, kwargs) to 'str' type and all the
returned strings values back to 'unicode'.
"""
def wrapper(*args, **kwargs):
returned_value = func(*convert_item(args), **convert_item(kwargs))
return convert_item(returned_value, True)
return wrapper
# See: https://github.com/MrS0m30n3/youtube-dl-gui/issues/57
# Patch os functions to convert between 'str' and 'unicode' on app bounds
os_sep = unicode(os.sep)
os_getenv = convert_on_bounds(os.getenv)
os_makedirs = convert_on_bounds(os.makedirs)
os_path_isdir = convert_on_bounds(os.path.isdir)
os_path_exists = convert_on_bounds(os.path.exists)
os_path_dirname = convert_on_bounds(os.path.dirname)
os_path_abspath = convert_on_bounds(os.path.abspath)
os_path_realpath = convert_on_bounds(os.path.realpath)
os_path_expanduser = convert_on_bounds(os.path.expanduser)
# Patch locale functions
locale_getdefaultlocale = convert_on_bounds(locale.getdefaultlocale)
# Patch Windows specific functions
if os.name == 'nt':
os_startfile = convert_on_bounds(os.startfile)
def remove_file(filename):
if os_path_exists(filename):
os.remove(filename)
return True
return False
def remove_shortcuts(path):
"""Return given path after removing the shortcuts. """
return path.replace('~', os_path_expanduser('~'))
def absolute_path(filename):
"""Return absolute path to the given file. """
return os_path_dirname(os_path_realpath(os_path_abspath(filename)))
def open_file(file_path):
"""Open file in file_path using the default OS application.
Returns:
True on success else False.
"""
file_path = remove_shortcuts(file_path)
if not os_path_exists(file_path):
return False
if os.name == "nt":
os_startfile(file_path)
else:
subprocess.call(("xdg-open", file_path))
return True
def encode_tuple(tuple_to_encode):
"""Turn size tuple into string. """
return '%s/%s' % (tuple_to_encode[0], tuple_to_encode[1])
def decode_tuple(encoded_tuple):
"""Turn tuple string back to tuple. """
s = encoded_tuple.split('/')
return int(s[0]), int(s[1])
def check_path(path):
"""Create path if not exist. """
if not os_path_exists(path):
os_makedirs(path)
def get_config_path():
"""Return user config path.
Note:
Windows = %AppData% + app_name
Linux = ~/.config + app_name
"""
if os.name == 'nt':
path = os_getenv('APPDATA')
else:
path = os.path.join(os_path_expanduser('~'), '.config')
return os.path.join(path, __appname__.lower())
def check_pers():
path = get_config_path()
try:
with open(path + '/tst.log', 'w') as f:
# opened for writing. write to it here
return True
except IOError as x:
if x.errno == errno.EACCES:
print('Permissions Denied, check {0}'.format(path))
return False
def shutdown_sys(password=None):
"""Shuts down the system.
Returns True if no errors occur else False.
Args:
password (string): SUDO password for linux.
Note:
On Linux you need to provide sudo password if you don't
have elevated privileges.
"""
_stderr = subprocess.PIPE
_stdin = None
info = None
encoding = get_encoding()
if os.name == 'nt':
cmd = ['shutdown', '/s', '/t', '1']
# Hide subprocess window
info = subprocess.STARTUPINFO()
info.dwFlags |= subprocess.STARTF_USESHOWWINDOW
else:
if password:
_stdin = subprocess.PIPE
password = ('%s\n' % password).encode(encoding)
cmd = ['sudo', '-S', '/sbin/shutdown', '-h', 'now']
else:
cmd = ['/sbin/shutdown', '-h', 'now']
cmd = [item.encode(encoding, 'ignore') for item in cmd]
shutdown_proc = subprocess.Popen(cmd,
stderr=_stderr,
stdin=_stdin,
startupinfo=info)
output = shutdown_proc.communicate(password)[1]
return not output or output == "Password:"
def to_string(data):
"""Convert data to string.
Works for both Python2 & Python3. """
return '%s' % data
def get_time(seconds):
"""Convert given seconds to days, hours, minutes and seconds.
Args:
seconds (float): Time in seconds.
Returns:
Dictionary that contains the corresponding days, hours, minutes
and seconds of the given seconds.
"""
dtime = dict(seconds=0, minutes=0, hours=0, days=0)
dtime['days'] = int(seconds / 86400)
dtime['hours'] = int(seconds % 86400 / 3600)
dtime['minutes'] = int(seconds % 86400 % 3600 / 60)
dtime['seconds'] = int(seconds % 86400 % 3600 % 60)
return dtime
def get_locale_file():
"""Search for youtube-dlg locale file.
Returns:
The path to youtube-dlg locale file if exists else None.
Note:
Paths that get_locale_file() func searches.
__main__ dir, library dir
"""
DIR_NAME = "locale"
SEARCH_DIRS = [
os.path.join(absolute_path(sys.argv[0]), DIR_NAME),
os.path.join(os_path_dirname(__file__), DIR_NAME),
]
for directory in SEARCH_DIRS:
if os_path_isdir(directory):
return directory
return None
def get_icon_file():
"""Search for youtube-dlg app icon.
Returns:
The path to youtube-dlg icon file if exists, else returns None.
"""
ICON_NAME = "youtube-dl-gui.png"
pixmaps_dir = get_pixmaps_dir()
if pixmaps_dir is not None:
icon_file = os.path.join(pixmaps_dir, ICON_NAME)
if os_path_exists(icon_file):
return icon_file
return None
def get_pixmaps_dir():
"""Return absolute path to the pixmaps icons folder.
Note:
Paths we search: __main__ dir, library dir
"""
search_dirs = [
os.path.join(absolute_path(sys.argv[0]), "data"),
os.path.join(os_path_dirname(__file__), "data")
]
for directory in search_dirs:
pixmaps_dir = os.path.join(directory, "pixmaps")
if os_path_exists(pixmaps_dir):
return pixmaps_dir
return None
def to_bytes(string):
"""Convert given youtube-dl size string to bytes."""
value = 0.0
for index, metric in enumerate(reversed(FILESIZE_METRICS)):
if metric in string:
value = float(string.split(metric)[0])
break
exponent = index * (-1) + (len(FILESIZE_METRICS) - 1)
return round(value * (KILO_SIZE ** exponent), 2)
def format_bytes(bytes):
"""Format bytes to youtube-dl size output strings."""
if bytes == 0.0:
exponent = 0
else:
exponent = int(math.log(bytes, KILO_SIZE))
suffix = FILESIZE_METRICS[exponent]
output_value = bytes / (KILO_SIZE ** exponent)
return "%.2f%s" % (output_value, suffix)
def build_command(options_list, url):
"""Build the youtube-dl command line string."""
def escape(option):
"""Wrap option with double quotes if it contains special symbols."""
special_symbols = [" ", "(", ")"]
for symbol in special_symbols:
if symbol in option:
return "\"{}\"".format(option)
return option
# If option has special symbols wrap it with double quotes
# Probably not the best solution since if the option already contains
# double quotes it will be a mess, see issue #173
options = [escape(option) for option in options_list]
# Always wrap the url with double quotes
url = "\"{}\"".format(url)
return " ".join([YOUTUBEDL_BIN] + options + [url])
def get_default_lang():
"""Get default language using the 'locale' module."""
default_lang, _ = locale_getdefaultlocale()
if not default_lang:
default_lang = "en_US"
return default_lang