File: utils.py

package info (click to toggle)
miro 4.0.4-1
  • links: PTS
  • area: main
  • in suites: wheezy
  • size: 48,888 kB
  • sloc: python: 81,708; ansic: 2,076; xml: 1,930; cpp: 940; sh: 389; makefile: 63
file content (452 lines) | stat: -rw-r--r-- 13,103 bytes parent folder | download
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
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
# Miro - an RSS based video player application
# Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011
# Participatory Culture Foundation
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
#
# In addition, as a special exception, the copyright holders give
# permission to link the code of portions of this program with the OpenSSL
# library.
#
# You must obey the GNU General Public License in all respects for all of
# the code used other than OpenSSL. If you modify file(s) with this
# exception, you may extend this exception to your version of the file(s),
# but you are not obligated to do so. If you do not wish to do so, delete
# this exception statement from your version. If you delete this exception
# statement from all source files in the program, then also delete it here.

import errno
import signal
import os
import statvfs
from miro import app
from miro import prefs
import logging
import logging.handlers
import locale
import urllib
import sys
import time
from miro.util import returns_unicode, returns_binary, check_u, check_b
import miro
from miro.plat import options

PlatformFilenameType = str

# We need to define samefile for the portable code.  Lucky for us, this is
# very easy.
from os.path import samefile

# this is used in lib/gtcache.py
_locale_initialized = False

miro_exec_prefix = None


def dirfilt(root, dirs):
    """
    Platform hook to filter out any directories that should not be
    descended into, root and dirs corresponds as per os.walk().
    """
    return dirs

def get_available_bytes_for_movies():
    """Helper method used to get the free space on the disk where downloaded
    movies are stored.

    If it errors out, returns 0.

    :returns: free disk space on drive for MOVIES_DIRECTORY as an int
    Returns an integer
    """
    movie_dir = app.config.get(prefs.MOVIES_DIRECTORY)

    if not os.path.exists(movie_dir):
        # FIXME - this is a bogus value.  need to "do the right thing"
        # here.
        return 0

    statinfo = os.statvfs(movie_dir)
    return statinfo.f_frsize * statinfo.f_bavail


def locale_initialized():
    """Returns whether or not the locale has been initialized.

    :returns: True or False regarding whether initialize_locale has been
        called.
    """
    return _locale_initialized


def initialize_locale():
    """Initializes the locale.
    """
    # gettext understands *NIX locales, so we don't have to do anything
    global _locale_initialized
    _locale_initialized = True


FORMAT = "%(asctime)s %(levelname)-8s %(name)s: %(message)s"


def setup_logging(in_downloader=False):
    """Sets up logging using the Python logging module.

    :param in_downloader: True if this is being called from the
        downloader daemon, False otherwise.
    """
    if in_downloader:
        if 'MIRO_IN_UNIT_TESTS' in os.environ:
            level = logging.WARN
        elif os.environ.get('MIRO_DEBUGMODE', "") == "True":
            level = logging.DEBUG
        else:
            level = logging.INFO
        logging.basicConfig(level=level, format=FORMAT, stream=sys.stdout)
        pathname = os.environ.get("DEMOCRACY_DOWNLOADER_LOG")
        if not pathname:
            return
    else:
        if app.debugmode:
            level = logging.DEBUG
        else:
            level = logging.INFO
        logging.basicConfig(level=level,
                            format=FORMAT)
        pathname = app.config.get(prefs.LOG_PATHNAME)

    try:
        rotater = logging.handlers.RotatingFileHandler(
            pathname, mode="w", maxBytes=100000,
            backupCount=5)
    except IOError:
        # bug 13338.  sometimes there's a file there and it causes
        # RotatingFileHandler to flip out when opening it.  so we
        # delete it and then try again.
        os.remove(pathname)
        rotater = logging.handlers.RotatingFileHandler(
            pathname, mode="w", maxBytes=100000,
            backupCount=5)

    formatter = logging.Formatter(FORMAT)
    rotater.setFormatter(formatter)
    logging.getLogger('').addHandler(rotater)
    rotater.doRollover()


@returns_binary
def utf8_to_filename(filename):
    """Converts a utf-8 encoded string to a FilenameType.
    """
    if not isinstance(filename, str):
        raise ValueError("filename is not a str")
    return filename


@returns_unicode
def shorten_fn(filename):
    check_u(filename)
    first, last = os.path.splitext(filename)

    if first:
        return u"".join([first[:-1], last])

    return unicode(last[:-1])


def encode_fn(filename):
    try:
        return filename.encode(locale.getpreferredencoding())
    except UnicodeEncodeError:
        return filename.encode('ascii', 'replace')


@returns_binary
def unicode_to_filename(filename, path=None):
    """Takes in a unicode string representation of a filename (NOT a
    file path) and creates a valid byte representation of it
    attempting to preserve extensions.

    .. Note::

       This is not guaranteed to give the same results every time it
       is run, nor is it guaranteed to reverse the results of
       filename_to_unicode.
    """
    check_u(filename)
    if path:
        check_b(path)
    else:
        path = os.getcwd()

    # keep this a little shorter than the max length, so we can
    # add a number to the end
    max_len = os.statvfs(path)[statvfs.F_NAMEMAX] - 5

    for mem in ("/", "\000", "\\", ":", "*", "?", "\"", "'",
                "<", ">", "|", "&", "\r", "\n"):
        filename = filename.replace(mem, "_")

    new_filename = encode_fn(filename)

    while len(new_filename) > max_len:
        filename = shorten_fn(filename)
        new_filename = encode_fn(filename)

    return new_filename


@returns_unicode
def filename_to_unicode(filename, path=None):
    """Given a filename in raw bytes, return the unicode representation

    .. Note::

       This is not guaranteed to give the same results every time it
       is run, not is it guaranteed to reverse the results of
       unicode_to_filename.
    """
    if path:
        check_b(path)
    check_b(filename)
    try:
        return filename.decode(locale.getpreferredencoding())
    except UnicodeDecodeError:
        return filename.decode('ascii', 'replace')


@returns_unicode
def make_url_safe(s, safe='/'):
    """Takes in a byte string or a unicode string and does the right thing
    to make a URL
    """
    if isinstance(s, str):
        # quote the byte string
        return urllib.quote(s, safe=safe).decode('ascii')

    try:
        return urllib.quote(s.encode(locale.getpreferredencoding()),
                            safe=safe).decode('ascii')
    except (UnicodeEncodeError, UnicodeDecodeError):
        return s.decode('ascii', 'replace')


@returns_binary
def unmake_url_safe(s):
    """Undoes make_url_safe (assuming it was passed a FilenameType)
    """
    # unquote the byte string
    check_u(s)
    return urllib.unquote(s.encode('ascii'))


def _pid_is_running(pid):
    if pid is None:
        return False
    try:
        os.kill(pid, 0)
        return True
    except OSError, err:
        return err.errno == errno.EPERM


def kill_process(pid):
    """Kills the process with the given pid.
    """
    if pid is None:
        return
    if _pid_is_running(pid):
        try:
            os.kill(pid, signal.SIGTERM)
            for i in range(100):
                time.sleep(.01)
                if not _pid_is_running(pid):
                    return
            os.kill(pid, signal.SIGKILL)
        except StandardError:
            logging.exception("error killing download daemon")


def launch_download_daemon(oldpid, env):
    """Launches the download daemon.

    :param oldpid: the pid of the previous download daemon
    :param env: the environment to launch the daemon in
    """
    # Use UNIX style kill
    if oldpid is not None and _pid_is_running(oldpid):
        kill_process(oldpid)

    environ = os.environ.copy()
    environ['MIRO_FRONTEND'] = options.frontend
    environ['DEMOCRACY_DOWNLOADER_LOG'] = app.config.get(
        prefs.DOWNLOADER_LOG_PATHNAME)
    environ['MIRO_APP_VERSION'] = app.config.get(prefs.APP_VERSION)
    environ['MIRO_DEBUGMODE'] = str(app.debugmode)
    if hasattr(miro.app, 'in_unit_tests'):
        environ['MIRO_IN_UNIT_TESTS'] = '1'
    environ.update(env)
    miro_path = os.path.dirname(miro.__file__)
    dl_daemon_path = os.path.join(miro_path, 'dl_daemon')

    # run the Miro_Downloader script
    script = os.path.join(dl_daemon_path, 'MiroDownloader.py')
    os.spawnle(os.P_NOWAIT, sys.executable, sys.executable, script, environ)


def exit_miro(return_code):
    """Exits Miro.
    """
    sys.exit(return_code)


def movie_data_program_info(movie_path, thumbnail_path):
    """Returns the necessary information for Miro to run the media
    item info extractor program.

    The media item info extractor program takes a media item path and
    a path for where the thumbnail should go (if there is one) and
    returns information about the media item.

    Due to legacy reasons, this is called ``movie_data_program_info``,
    but it applies to audio items as well as video items.

    :returns: tuple of ``((python, script-path, movie-path, thumbnail-path),
        environment)``.  Environment is either a dict or None.
    """
    from miro import app
    return app.movie_data_program_info(movie_path, thumbnail_path)

def miro_helper_program_info():
    """Get the command line to launch miro_helper.py """

    miro_path = os.path.dirname(miro.__file__)
    miro_helper_path = os.path.join(miro_path, 'miro_helper.py')

    cmd_line = (sys.executable, miro_helper_path)
    env = None

    return (cmd_line, env)

def get_logical_cpu_count():
    """Returns the logical number of cpus on this machine.

    :returns: int
    """
    try:
        import multiprocessing
        return multiprocessing.cpu_count()
    except ImportError:
        ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
        if isinstance(ncpus, int) and ncpus > 0:
            return ncpus
    return 1

def register_exec_prefix():
    global miro_exec_prefix
    miro_exec_prefix = os.path.dirname(os.path.abspath(sys.argv[0]))

def get_segmenter_executable_path():
    # NB: Since this was installed using distutils this should give the right
    # path.
    return os.path.join(miro_exec_prefix, 'miro-segmenter')

def get_transcode_video_options():   
    # Linux ffmpeg may not have fancy pants encoders so leave to use default.
    has_video_args = []
    return has_video_args

def get_transcode_audio_options():
    # Linux ffmpeg may not have fancy pants encoders so leave to use default.
    has_audio_args = []
    return has_audio_args

def setup_ffmpeg_presets():
    # the linux distro should handle this
    pass


def get_ffmpeg_executable_path():
    """Returns the location of the ffmpeg binary.

    :returns: string
    """
    return app.config.get(options.FFMPEG_BINARY)


def customize_ffmpeg_parameters(params):
    """Takes a list of parameters and modifies it based on
    platform-specific issues.  Returns the newly modified list of
    parameters.

    :param params: list of parameters to modify

    :returns: list of modified parameters that will get passed to
        ffmpeg
    """
    return params


def get_ffmpeg2theora_executable_path():
    """Returns the location of the ffmpeg2theora binary.

    :returns: string
    """
    return app.config.get(options.FFMPEG2THEORA_BINARY)


def customize_ffmpeg2theora_parameters(params):
    """Takes a list of parameters and modifies it based on
    platform-specific issues.  Returns the newly modified list of
    parameters.

    :param params: list of parameters to modify

    :returns: list of modified parameters that will get passed to
        ffmpeg2theora
    """
    return params


def begin_thread_loop(context_object):
    # used for testing
    pass


def finish_thread_loop(context_object):
    # used for testing
    pass


def get_cookie_path():
    """
    Returns the path to a Netscape-style cookie file for Curl to use.

    Nothing is written to this file, but we use the cookies for downloading
    from Amazon.
    """
    return os.path.join(
        app.config.get(prefs.SUPPORT_DIRECTORY),
        'cookies.txt')


# Expand me: pick up Linux media players.
def get_plat_media_player_name_path():
    return (None, None)


def thread_body(func, *args, **kwargs):
    func(*args, **kwargs)