Compare commits

..

7 Commits

Author SHA1 Message Date
Diaoul e0788be5af Release 1.0.1 2015-07-23 00:44:56 +02:00
Diaoul 52709a3e25 Remove useless dash in cli message 2015-07-23 00:35:05 +02:00
Diaoul 25cf0ac996 Fix scaled score in cli 2015-07-23 00:33:58 +02:00
Diaoul 4c3d7d5b9d Improve CLI
- Convert paths to str
- Add logs
- Catch errors
- Colored output report
2015-07-23 00:11:29 +02:00
Diaoul b4cbfc0de2 Add CLI api doc 2015-07-23 00:05:47 +02:00
Diaoul 600393c12a Fix scan_videos docstring 2015-07-22 19:43:34 +02:00
Diaoul faeeeca39a Switch to dev version 2015-07-22 19:43:07 +02:00
7 changed files with 77 additions and 17 deletions
+10
View File
@@ -1,6 +1,16 @@
Changelog
---------
1.0.1
^^^^^
**release date:** 2015-07-23
* Fix unicode issues in CLI (python 2 only)
* Fix score scaling in CLI (python 2 only)
* Improve error handling in CLI
* Color collect report in CLI
1.0
^^^
**release date:** 2015-07-22
+1 -1
View File
@@ -35,7 +35,7 @@ Download English subtitles::
$ subliminal download -l en The.Big.Bang.Theory.S05E18.HDTV.x264-LOL.mp4
Collecting videos [####################################] 100%
1 video collected / 0 video ignored
1 video collected / 0 video ignored / 0 error
Downloading subtitles [####################################] 100%
Downloaded 1 subtitle
+3
View File
@@ -0,0 +1,3 @@
CLI
===
.. automodule:: subliminal.cli
+1
View File
@@ -33,6 +33,7 @@ If you are looking for information on a specific function, class or method, this
api/providers
api/cache
api/score
api/cli
api/exceptions
+1 -1
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
__title__ = 'subliminal'
__version__ = '1.0'
__version__ = '1.0.1'
__author__ = 'Antoine Bertin'
__license__ = 'MIT'
__copyright__ = 'Copyright 2015, Antoine Bertin'
+60 -13
View File
@@ -3,12 +3,13 @@
Subliminal uses `click <http://click.pocoo.org>`_ to provide a powerful :abbr:`CLI (command-line interface)`.
"""
from __future__ import unicode_literals
from __future__ import unicode_literals, division
from collections import defaultdict
from datetime import timedelta
import logging
import os
import re
import sys
from babelfish import Error as BabelfishError, Language
import click
@@ -19,6 +20,8 @@ from subliminal import (Episode, Movie, ProviderPool, Video, __version__, check_
save_subtitles, scan_video, scan_videos)
from subliminal.subtitle import compute_score
logger = logging.getLogger(__name__)
class MutexLock(AbstractFileLock):
""":class:`MutexLock` is a thread-based rw lock based on :class:`dogpile.core.ReadWriteMutex`."""
@@ -40,6 +43,22 @@ class MutexLock(AbstractFileLock):
return self.mutex.release_write_lock()
class StringPath(click.Path):
"""A :class:`~click.Path` as :class:`str`."""
def convert(self, value, param, ctx):
if isinstance(value, bytes):
try:
enc = getattr(sys.stdin, 'encoding', None)
if enc is not None:
value = value.decode(enc)
except UnicodeDecodeError:
try:
value = value.decode(click.utils.get_filesystem_encoding())
except UnicodeDecodeError:
self.fail('%s is not a correctly encoded path' % value.decode('utf-8', 'replace'))
return super(StringPath, self).convert(value, param, ctx)
class LanguageParamType(click.ParamType):
""":class:`~click.ParamType` for languages that returns a :class:`~babelfish.language.Language`"""
name = 'language'
@@ -149,7 +168,7 @@ def cache(ctx, clear_subliminal):
@click.option('-m', '--min-score', type=click.IntRange(0, 100), default=0, help='Minimum score for a subtitle '
'to be downloaded (0 to 100).')
@click.option('-v', '--verbose', count=True, help='Increase verbosity.')
@click.argument('path', type=click.Path(), required=True, nargs=-1)
@click.argument('path', type=StringPath(), required=True, nargs=-1)
@click.pass_obj
def download(obj, provider, language, age, directory, encoding, single, force, hearing_impaired, min_score, verbose,
path):
@@ -167,17 +186,31 @@ def download(obj, provider, language, age, directory, encoding, single, force, h
# scan videos
videos = []
ignored_videos = []
with click.progressbar(path, label='Collecting videos',
item_show_func=lambda p: str(p) if p is not None else '') as bar:
errored_paths = []
with click.progressbar(path, label='Collecting videos', item_show_func=lambda p: p or '') as bar:
for p in bar:
logger.debug('Collecting path %s', p)
# non-existing
if not os.path.exists(p):
videos.append(Video.fromname(p))
try:
video = Video.fromname(p)
except:
logger.exception('Unexpected error while collecting non-existing path %s', p)
errored_paths.append(p)
continue
videos.append(video)
continue
# directories
if os.path.isdir(p):
for video in scan_videos(p, subtitles=not force, embedded_subtitles=not force):
try:
scanned_videos = scan_videos(p, subtitles=not force, embedded_subtitles=not force)
except:
logger.exception('Unexpected error while collecting directory path %s', p)
errored_paths.append(p)
continue
for video in scanned_videos:
if check_video(video, languages=language, age=age, undefined=single):
videos.append(video)
else:
@@ -185,16 +218,26 @@ def download(obj, provider, language, age, directory, encoding, single, force, h
continue
# other inputs
video = scan_video(p, subtitles=not force, embedded_subtitles=not force)
try:
video = scan_video(p, subtitles=not force, embedded_subtitles=not force)
except:
logger.exception('Unexpected error while collecting path %s', p)
errored_paths.append(p)
continue
if check_video(video, languages=language, age=age, undefined=single):
videos.append(video)
else:
ignored_videos.append(video)
# output errored paths
if verbose > 0:
for p in errored_paths:
click.secho('%s errored' % p, fg='red')
# output ignored videos
if verbose > 1:
for video in ignored_videos:
click.secho('%s ignored - subtitles: %s / age: %d day%s ' % (
click.secho('%s ignored - subtitles: %s / age: %d day%s' % (
os.path.split(video.name)[1],
', '.join(str(s) for s in video.subtitle_languages) or 'none',
video.age.days,
@@ -202,10 +245,14 @@ def download(obj, provider, language, age, directory, encoding, single, force, h
), fg='yellow')
# report collected videos
click.echo('%s video%s collected / %s video%s ignored' % (click.style(str(len(videos)), bold=True),
's' if len(videos) > 1 else '',
click.style(str(len(ignored_videos)), bold=True),
's' if len(ignored_videos) > 1 else ''))
click.echo('%s video%s collected / %s video%s ignored / %s error%s' % (
click.style(str(len(videos)), bold=True, fg='green' if videos else None),
's' if len(videos) > 1 else '',
click.style(str(len(ignored_videos)), bold=True, fg='yellow' if ignored_videos else None),
's' if len(ignored_videos) > 1 else '',
click.style(str(len(errored_paths)), bold=True, fg='red' if errored_paths else None),
's' if len(errored_paths) > 1 else '',
))
# exit if no video collected
if not videos:
@@ -263,7 +310,7 @@ def download(obj, provider, language, age, directory, encoding, single, force, h
scaled_score *= 100 / v.scores['hash']
# echo some nice colored output
click.echo(' - [{score}] - {language} subtitle from {provider_name} (match on {matches})'.format(
click.echo(' - [{score}] {language} subtitle from {provider_name} (match on {matches})'.format(
score=click.style('{:5.1f}'.format(scaled_score), fg=score_color, bold=score >= v.scores['hash']),
language=s.language.name if s.language.country is None else '%s (%s)' % (s.language.name,
s.language.country.name),
+1 -2
View File
@@ -395,8 +395,7 @@ def scan_video(path, subtitles=True, embedded_subtitles=True):
def scan_videos(path, subtitles=True, embedded_subtitles=True):
"""Scan `path` for videos and their subtitles.
:params path: existing directory path to scan.
:type path: str
:param str path: existing directory path to scan.
:param bool subtitles: scan for subtitles with the same name.
:param bool embedded_subtitles: scan for embedded subtitles.
:return: the scanned videos.