Compare commits

..

71 Commits

Author SHA1 Message Date
Antoine Bertin 3cf265f6c9 Change version from 1.0 to 0.4 2011-11-11 20:37:19 +01:00
Antoine Bertin 1b8dd8cc83 Update gitignore 2011-11-11 20:37:03 +01:00
Antoine Bertin f3292c2916 Release v1.0b6 2011-10-08 19:44:14 +02:00
Antoine Bertin 048d6adfa3 Exit the plugin if no correct language is given 2011-10-08 19:41:04 +02:00
Antoine Bertin f004271e8b Fix log message in PluginBase 2011-10-08 19:37:33 +02:00
Antoine Bertin 63ef2f1273 Release v1.0b5 2011-10-08 19:03:51 +02:00
Antoine Bertin f7f2b80028 Update unittest 2011-10-07 19:22:45 +02:00
Antoine Bertin ec21d564b2 Handle correctly non existent file paths 2011-10-07 19:13:21 +02:00
Antoine Bertin 6d2b772f21 Add a timeout on downloadFile method so it does not get stuck 2011-10-07 19:11:05 +02:00
Antoine Bertin d506dd2095 Remove useless checkLanguages method 2011-10-07 19:10:11 +02:00
Antoine Bertin c66028793d Release v1.0b4 2011-10-07 13:59:37 +02:00
Antoine Bertin 0e3467d3aa Add possible_languages method to filter undesired languages 2011-10-07 01:35:34 +02:00
Antoine Bertin d9dc6832e3 Be more pythonic
- Use property decorators
- Use new-style classes
2011-10-07 01:33:28 +02:00
Antoine Bertin a57d49abf1 Remove useless comments 2011-10-07 01:31:50 +02:00
Antoine Bertin 614388e28f Fix bad error logging when there is no error 2011-10-07 01:31:02 +02:00
Antoine Bertin 7e1a09fb64 Do not forget to remove the gziped file in OpenSubtitles 2011-10-07 01:30:02 +02:00
Antoine Bertin 6efbbd9cad Use logging to print stack instead of traceback 2011-10-07 00:00:07 +02:00
Antoine Bertin e0b5894e3f Use sets in listSubtitles for wanted languages 2011-10-06 23:59:03 +02:00
Antoine Bertin 5ed153425b Preserve order for languages and plugins 2011-10-06 23:56:09 +02:00
Antoine Bertin c961c9be93 Fix multiple issues
- Add a DownloadFailedError exception
- Fix not working "skip on failure" on a download task
- Add extensions to detect subtitles files
- Replace recursiveSearch with scan method
- Fix 'list' object has no attribute 'path'
2011-10-05 22:30:33 +02:00
Antoine Bertin 0bafc9d31f Remove Addic7ed from plugins (not working anymore) 2011-10-05 21:31:54 +02:00
Antoine Bertin a48cd6c824 Remove duplicates in setters for languages and plugins 2011-10-02 21:23:46 +02:00
Antoine Bertin cb899b81e4 Release v1.0b3 2011-10-01 17:02:35 +02:00
Antoine Bertin 918151013e Improve error handling in subliminal 2011-10-01 17:01:54 +02:00
Antoine Bertin 4cb9ebb5e3 Fix search with special characters in some plugins 2011-10-01 15:06:46 +02:00
Antoine Bertin e14a5f1776 Improve error handling in plugins 2011-10-01 15:04:21 +02:00
Antoine Bertin 2437dc602c More logging in plugins 2011-10-01 15:02:38 +02:00
Antoine Bertin 8962d84ed3 Release v1.0b2 2011-09-29 23:55:58 +02:00
Antoine Bertin 4172d2a8b1 Change the Subtitle class. Make download return a list of Subtitles 2011-09-29 23:55:33 +02:00
Antoine Bertin 78a2166efe Release v1.0b 2011-09-29 23:51:30 +02:00
Antoine Bertin 20a2519d92 Fix unittest 2011-09-28 21:54:45 +02:00
Antoine Bertin a7da526885 Compare lowercase only in OpenSubtitles 2011-09-28 21:53:18 +02:00
Antoine Bertin d2010c3510 Log a warning if non unicode is submitted 2011-09-28 21:40:38 +02:00
Antoine Bertin 14c0c6fe6b Fix possible encoding issues in logger using repr instead of str 2011-09-28 21:39:48 +02:00
Antoine Bertin 5d8be66167 Add docstrings in classes 2011-09-28 21:37:37 +02:00
Antoine Bertin 9ac838a257 Make cmpSubtitles and recursiveSearch public 2011-09-27 21:38:30 +02:00
Antoine Bertin 0cd1016cae Use two result queues instead of one 2011-09-27 21:35:14 +02:00
Antoine Bertin 49b8486f44 Use simple quotes 2011-09-27 21:26:27 +02:00
Antoine Bertin 0a363ba5d5 Add a method to add tasks with default priority 2011-09-27 21:24:55 +02:00
Antoine Bertin 5fbfaef31d Fix BadStateError in CLI 2011-09-26 21:41:44 +02:00
Antoine Bertin 9324fc4c07 Add auto argument to manage workers 2011-09-26 21:41:20 +02:00
Antoine Bertin 171147dfcd Fix bad argument in unittest 2011-09-26 21:39:04 +02:00
Antoine Bertin dee0b33cec Fix missing import 2011-09-26 21:37:52 +02:00
Antoine Bertin f9c7654b7c Update unittest 2011-09-24 16:51:37 +02:00
Antoine Bertin b1e0371bb5 Use a PriorityQueue for tasks 2011-09-24 16:50:55 +02:00
Antoine Bertin f94957c68d Accept basestring input, not just unicode 2011-09-24 16:48:16 +02:00
Antoine Bertin 03dd15f718 Merge pull request #15 from abenea/master
Bugfixes
2011-09-23 15:08:44 -07:00
Andrei Benea 938b30604b Fix --cache_dir. 2011-09-23 21:25:28 +03:00
Andrei Benea acff303897 Fix "Downloaded subtitles" report. 2011-09-23 20:46:37 +03:00
Andrei Benea 978f4fb376 Fix exception in _recursiveSearch if no languages are selected. 2011-09-23 20:39:50 +03:00
Andrei Benea 50e0a6704b Add missing newline. 2011-09-23 20:39:08 +03:00
Andrei Benea b4e9c0e655 Don't hang on exceptions from the main thread. 2011-09-23 20:11:24 +03:00
Antoine Bertin 97ab555e2d Update unittest 2011-09-01 21:35:24 +02:00
Antoine Bertin 9bc12ec619 Fix BierDopje download 2011-09-01 21:35:04 +02:00
Antoine Bertin e4c12dfe5c Remove unused imports and variables 2011-09-01 08:38:53 +02:00
Antoine Bertin ca0d8e4ab1 Update .gitignore 2011-09-01 08:37:54 +02:00
Antoine Bertin 570dc7ef9a Remove last encodingKludge import 2011-08-31 21:38:04 +02:00
Antoine Bertin 3e7cac972d Update unittests 2011-08-29 22:23:24 +02:00
Antoine Bertin 9c7ac431b5 Update docstrings. Double quotes to simple quotes. Minor fixes
Minor fixes includes:
- getClassName() converted to self.__class__.__name__
- Typos
- listTeams separators in plugins to include more separators
2011-08-29 21:36:49 +02:00
Antoine Bertin 8f50b317cc Remove encodingKludge 2011-08-29 21:28:35 +02:00
Antoine Bertin c5e2e8c4b0 Fix wrong subliminal version in TheSubDB 2011-08-29 21:22:58 +02:00
Antoine Bertin 7aec1f7653 Remove unused multi_languages_queries in plugins 2011-08-29 21:22:30 +02:00
Antoine Bertin 65a677e6d4 Remove unused plugins_config 2011-08-29 21:20:40 +02:00
Antoine Bertin 8e9781c081 Add Subtitle and Task classes. Change list and download in plugins 2011-08-29 21:20:00 +02:00
Antoine Bertin d76d81eda3 Fix release group comparison. Use lowercase 2011-08-29 20:50:34 +02:00
Antoine Bertin 7b439bbf6f Remove useless multi_filename_queries stuff 2011-08-28 09:50:01 +02:00
Antoine Bertin aea078d65f Add some features. Review module organization
- More constants (FORMATS, LANGUAGES, PLUGINS, API_PLUGINS)
- NullHandler is given to the logger
- Remove useless stuff
- Custom exceptions
- More strict setters
- Move PluginWorker class to subliminal
2011-08-27 23:44:03 +02:00
Antoine Bertin 5834c3edcf Improve argument naming in subliminal script 2011-08-27 23:29:15 +02:00
Antoine Bertin 1d40c026f4 Remove the configuration file 2011-08-27 22:55:11 +02:00
Antoine Bertin 00fb74c5b1 Replace deprecated optparse with argparse 2011-08-27 18:49:53 +02:00
Antoine Bertin 066dc77bd9 Revert to development version 2011-08-27 18:47:16 +02:00
22 changed files with 987 additions and 1437 deletions
+4
View File
@@ -2,3 +2,7 @@ build
dist
subliminal.egg-info
*.pyc
.settings
.project
.pydevproject
*~
+45 -65
View File
@@ -21,80 +21,60 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from optparse import OptionParser
import argparse
import subliminal
import logging
import mimetypes
import os
import os.path
import sys
def main():
'''Download subtitles'''
# parse command line options
parser = OptionParser("usage: %prog [options] file1 file2", version=subliminal.__version__)
parser.add_option("-l", "--language", action="append", dest="languages", help="wanted language (ISO 639-1 two chars) for the subtitles (e.g. fr, en). Multiple uses allowed such that `%prog -l fr -l en file1`")
parser.add_option("-m", "--multi", action="store_true", dest="multi", help="download one subtitle per specified language (instead of one of them) and name them accordingly (e.g. .fr.srt, .en.srt)")
parser.add_option("-p", "--plugin", action="append", dest="plugins", help="plugins to activate")
parser.add_option("-f", "--force", action="store_true", dest="force", help="force download of a subtitle even there is already one present")
parser.add_option("-C", "--no-config-file", action="store_false", dest="config", help="do not use configuration file (requires -l to be specified)")
parser.add_option("-c", "--config-file", action="store", dest="config", help="configuration file to use")
parser.add_option("-w", "--workers", action="store", dest="workers", help="specify the number of threads to use")
parser.add_option("--cache-dir", action="store", dest="cache_dir", help="cache directory to use")
parser.add_option("--no-cache-dir", action="store_false", dest="cache_dir", help="do not use cache directory (some plugins may not work)")
parser.add_option("--list-all-plugins", action="store_true", dest="list_all_plugins", help="list all plugins available")
parser.add_option("--list-api-plugins", action="store_true", dest="list_api_plugins", help="list api-based plugins")
parser.add_option("--list-active-plugins", action="store_true", dest="list_active_plugins", help="list currently active plugins")
parser.add_option("-v", "--verbose", action="count", dest="verbose", help="increase verbosity (maximum 2 times)")
parser.set_defaults(verbose=0, cache_dir=True, config=True, workers=4)
(options, args) = parser.parse_args()
if not args:
print parser.print_help()
exit(1)
if options.verbose == 2:
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)-24s %(levelname)-8s %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
elif options.verbose == 1:
logging.basicConfig(level=logging.WARN, format='%(levelname)s: %(name)s %(message)s')
else:
logging.basicConfig(level=logging.ERROR)
if not options.config and not options.languages:
parser.error("Option -C (--no-config-file) is used without -l (--language)")
subliminal_client = subliminal.Subliminal(config=options.config, cache_dir=options.cache_dir, workers=options.workers, multi=options.multi, force=options.force, max_depth=3, autostart=False)
if options.plugins:
subliminal_client.plugins = options.plugins
if options.list_all_plugins:
plugins = subliminal_client.listExistingPlugins()
print ', '.join(subliminal_client.listExistingPlugins())
exit(0)
if options.list_api_plugins:
plugins = subliminal_client.list_api_plugins()
print ', '.join(subliminal_client.listExistingPlugins())
exit(0)
if options.list_active_plugins:
plugins = subliminal_client.plugins
print ', '.join(subliminal_client.listExistingPlugins())
exit(0)
if options.languages:
subliminal_client.languages = options.languages
else:
logging.info(u"No language given, looking into configuration file")
languages = subliminal_client.languages
if not languages:
logging.error(u"No language found in configuration file")
sys.stderr.write("No language found in configuration file")
exit(1)
parser.exit
subliminal_client.startWorkers()
subtitles = subliminal_client.downloadSubtitles(args)
subliminal_client.stopWorkers()
if len(subtitles) == 0:
sys.stderr.write("No subtitles found")
exit(1)
print "*" * 50
print "Downloaded %s subtitles" % len(subtitles)
for s in subtitles:
print s['lang'] + " - " + s['subtitlepath']
print "*" * 50
parser = argparse.ArgumentParser(description='Subtitles, faster than your thoughts')
parser.add_argument('-l', '--language', action='append', dest='languages', help='wanted language (ISO 639-1)', metavar='LG')
parser.add_argument('-p', '--plugin', action='append', dest='plugins', help='plugin to use', metavar='NAME')
parser.add_argument('-m', '--multi', action='store_true', help='download multiple subtitle languages')
parser.add_argument('-f', '--force', action='store_true', help='replace existing subtitle file')
parser.add_argument('-w', '--workers', action='store', help='use N threads (default: %(default)s)', metavar='N', default=4)
group_verbosity = parser.add_mutually_exclusive_group()
group_verbosity.add_argument('-q', '--quiet', action='store_true', help='disable output')
group_verbosity.add_argument('-v', '--verbose', action='store_true', help='verbose output')
group_cache = parser.add_mutually_exclusive_group()
group_cache.add_argument('--cache-dir', action='store', dest='cache_dir', help='cache directory to use', metavar='DIR', default=os.path.expanduser('~/.config/subliminal'))
group_cache.add_argument('--no-cache-dir', action='store_false', dest='cache_dir', help='do not use cache directory (some plugins may not work)')
parser.add_argument('--version', action='version', version=subliminal.__version__)
parser.add_argument('paths', nargs='+', help='path to video files or folders', metavar='PATH')
args = parser.parse_args()
if __name__ == "__main__":
# Set log verbosity
if args.verbose:
logging.basicConfig(level=logging.DEBUG, format='%(levelname)-8s %(asctime)s %(name)-24s %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
elif not args.quiet:
logging.basicConfig(level=logging.WARN, format='%(levelname)s: %(name)s %(message)s')
# Initialize the instance
subli = subliminal.Subliminal(cache_dir=args.cache_dir, workers=args.workers, multi=args.multi, force=args.force, max_depth=3, files_mode=-1)
if args.plugins:
subli.plugins = args.plugins
if args.languages:
subli.languages = args.languages
try:
subtitles = subli.downloadSubtitles([unicode(x) for x in args.paths])
finally:
subli.stopWorkers()
if len(subtitles) == 0:
if not args.quiet:
sys.stderr.write('No subtitles found\n')
exit(1)
if not args.quiet:
print '*' * 50
print 'Downloaded %s subtitles' % len(subtitles)
for subtitle in subtitles:
print subtitle
print '*' * 50
if __name__ == '__main__':
main()
-71
View File
@@ -1,71 +0,0 @@
# -*- coding: utf-8 -*-
#
# Subliminal - Subtitles, faster than your thoughts
# Copyright (c) 2011 Antoine Bertin <diaoulael@gmail.com>
#
# This file is part of Subliminal.
#
# Subliminal is free software; you can redistribute it and/or modify it under
# the terms of the Lesser GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Subliminal 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
# Lesser GNU General Public License for more details.
#
# You should have received a copy of the Lesser GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import threading
import plugins
import logging
import traceback
class PluginWorker(threading.Thread):
"""Threaded plugin worker"""
def __init__(self, taskQueue, resultQueue):
threading.Thread.__init__(self)
self.taskQueue = taskQueue
self.resultQueue = resultQueue
self.logger = logging.getLogger('subliminal.worker')
def run(self):
while True:
task = self.taskQueue.get()
result = None
try:
if not task: # this is a poison pill
break
elif task['task'] == 'list': # the task is a listing
# get the corresponding plugin
plugin = getattr(plugins, task['plugin'])(task['config'])
# split tasks if the plugin can't handle multi queries
splitedTasks = plugin.splitTask(task)
myTask = splitedTasks.pop()
for st in splitedTasks:
self.taskQueue.put(st)
result = plugin.list(myTask['filenames'], myTask['languages'])
elif task['task'] == 'download': # the task is to download
result = None
while task['subtitle']:
subtitle = task['subtitle'].pop(0)
# get the corresponding plugin
plugin = getattr(plugins, subtitle['plugin'])(task['config'])
path = plugin.download(subtitle)
if path:
subtitle['subtitlepath'] = path
result = subtitle
break
else:
self.logger.error(u'Unknown task %s submited to worker %s' % (task['task'], self.name))
except:
self.logger.debug(traceback.print_exc())
self.logger.error(u"Worker couldn't do the job %s, continue anyway" % task['task'])
finally:
self.resultQueue.put(result)
self.taskQueue.task_done()
self.logger.debug(u'Thread %s terminated' % self.name)
+3 -1
View File
@@ -19,5 +19,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from subliminal import Subliminal
__all__ = ['FORMATS', 'LANGUAGES', 'PLUGINS', 'API_PLUGINS', 'Subliminal', 'Subtitle']
from classes import *
from subliminal import *
from version import __version__
+127
View File
@@ -0,0 +1,127 @@
# -*- coding: utf-8 -*-
#
# Subliminal - Subtitles, faster than your thoughts
# Copyright (c) 2011 Antoine Bertin <diaoulael@gmail.com>
#
# This file is part of Subliminal.
#
# Subliminal is free software; you can redistribute it and/or modify it under
# the terms of the Lesser GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Subliminal 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
# Lesser GNU General Public License for more details.
#
# You should have received a copy of the Lesser GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
class Error(Exception):
"""Base class for exceptions in subliminal"""
pass
class BadStateError(Error):
"""Exception raised when an invalid action is asked
Attributes:
current -- current state of Subliminal instance
expected -- expected state of Subliminal instance
"""
def __init__(self, current, expected):
self.current = current
self.expected = expected
def __str__(self):
return 'Expected state %d but current state is %d' % (self.expected, self.current)
class LanguageError(Error):
"""Exception raised when invalid language is submitted
Attributes:
language -- language that cause the error
"""
def __init__(self, language):
self.language = language
def __str__(self):
return self.language
class PluginError(Error):
""""Exception raised when invalid plugin is submitted
Attributes:
plugin -- plugin that cause the error
"""
def __init__(self, plugin):
self.plugin = plugin
def __str__(self):
return self.plugin
class WrongTaskError(Error):
""""Exception raised when invalid task is submitted"""
pass
class DownloadFailedError(Error):
""""Exception raised when a download task has failed in plugin"""
pass
class Subtitle(object):
"""Subtitle class
Attributes:
video_path -- path to the video file
path -- path to the subtitle file
plugin -- plugin used
language -- language of the subtitle
link -- download link
release -- release group identified by guessit
teams -- identified by subliminal
"""
def __init__(self, video_path=None, path=None, plugin=None, language=None, link=None, release=None, teams=None):
self.video_path = video_path
self.path = path
self.plugin = plugin
self.language = language
self.link = link
self.release = release
self.teams = teams
def __repr__(self):
return repr({'video_path': self.video_path, 'path': self.path, 'plugin': self.plugin,
'language': self.language, 'link': self.link, 'release': self.release, 'teams': self.teams})
class Task(object):
"""Base class for tasks to use in subliminal"""
pass
class ListTask(Task):
"""List task to list subtitles"""
def __init__(self, filepath, languages, plugin, config):
self.filepath = filepath
self.plugin = plugin
self.languages = languages
self.config = config
class DownloadTask(Task):
"""Download task to download subtitles"""
def __init__(self, subtitles):
self.subtitles = subtitles
class StopTask(Task):
"""Stop task to stop workers"""
pass
-66
View File
@@ -1,66 +0,0 @@
# -*- coding: utf-8 -*-
#
# Subliminal - Subtitles, faster than your thoughts
# Copyright (c) 2011 Nic Wolfe <nic@wolfeden.ca>
# Copyright (c) 2011 Antoine Bertin <diaoulael@gmail.com>
#
# This file is part of Subliminal.
#
# Subliminal is free software; you can redistribute it and/or modify it under
# the terms of the Lesser GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Subliminal 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
# Lesser GNU General Public License for more details.
#
# You should have received a copy of the Lesser GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import os
import subliminal
# This module tries to deal with the apparently random behavior of python when dealing with unicode <-> utf-8
# encodings. It tries to just use unicode, but if that fails then it tries forcing it to utf-8. Any functions
# which return something should always return unicode.
def fixStupidEncodings(x, silent=False):
if type(x) == str:
try:
return x.decode(subliminal.SYS_ENCODING)
except UnicodeDecodeError:
subliminal.logger.error(u"Unable to decode value: " + repr(x))
return None
elif type(x) == unicode:
return x
else:
subliminal.logger.log(u"Unknown value passed in, ignoring it: " + str(type(x)) + " (" + repr(x) + ":" + repr(type(x)) + ")", logging.DEBUG if silent else logging.ERROR)
return None
return None
def fixListEncodings(x):
if type(x) != list:
return x
else:
return filter(lambda x: x != None, map(fixStupidEncodings, x))
def ek(func, *args):
result = None
if os.name == 'nt':
result = func(*args)
else:
result = func(*[x.encode(subliminal.SYS_ENCODING) if type(x) in (str, unicode) else x for x in args])
if type(result) == list:
return fixListEncodings(result)
elif type(result) == str:
return fixStupidEncodings(result)
else:
return result
+51 -75
View File
@@ -21,107 +21,99 @@
#
from BeautifulSoup import BeautifulSoup
from subliminal.classes import Subtitle
import guessit
import PluginBase
import zipfile
import os
import urllib2
import urllib
import traceback
import httplib
import re
import socket
class Addic7ed(PluginBase.PluginBase):
site_url = 'http://www.addic7ed.com'
site_name = 'Addic7ed'
server_url = 'http://www.addic7ed.com'
multi_languages_queries = True
multi_filename_queries = False
api_based = False
_plugin_languages = {u"English": "en",
u"English (US)": "en",
u"English (UK)": "en",
u"Italian": "it",
u"Portuguese": "pt",
u"Portuguese (Brazilian)": "pt-br",
u"Romanian": "ro",
u"Español (Latinoamérica)": "es",
u"Español (España)": "es",
u"Spanish (Latin America)": "es",
u"Español": "es",
u"Spanish": "es",
u"Spanish (Spain)": "es",
u"French": "fr",
u"Greek": "el",
u"Arabic": "ar",
u"German": "de",
u"Croatian": "hr",
u"Indonesian": "id",
u"Hebrew": "he",
u"Russian": "ru",
u"Turkish": "tr",
u"Swedish": "se",
u"Czech": "cs",
u"Dutch": "nl",
u"Hungarian": "hu",
u"Norwegian": "no",
u"Polish": "pl",
u"Persian": "fa"}
_plugin_languages = {u'English': 'en',
u'English (US)': 'en',
u'English (UK)': 'en',
u'Italian': 'it',
u'Portuguese': 'pt',
u'Portuguese (Brazilian)': 'pt-br',
u'Romanian': 'ro',
u'Español (Latinoamérica)': 'es',
u'Español (España)': 'es',
u'Spanish (Latin America)': 'es',
u'Español': 'es',
u'Spanish': 'es',
u'Spanish (Spain)': 'es',
u'French': 'fr',
u'Greek': 'el',
u'Arabic': 'ar',
u'German': 'de',
u'Croatian': 'hr',
u'Indonesian': 'id',
u'Hebrew': 'he',
u'Russian': 'ru',
u'Turkish': 'tr',
u'Swedish': 'se',
u'Czech': 'cs',
u'Dutch': 'nl',
u'Hungarian': 'hu',
u'Norwegian': 'no',
u'Polish': 'pl',
u'Persian': 'fa'}
def __init__(self, config_dict=None):
super(Addic7ed, self).__init__(self._plugin_languages, config_dict, isRevert=True)
#http://www.addic7ed.com/serie/Smallville/9/11/Absolute_Justice
self.release_pattern = re.compile(" \nVersion (.+), ([0-9]+).([0-9])+ MBs")
self.release_pattern = re.compile(' \nVersion (.+), ([0-9]+).([0-9])+ MBs')
def list(self, filenames, languages):
''' Main method to call when you want to list subtitles '''
# as self.multi_filename_queries is false, we won't have multiple filenames in the list so pick the only one
# once multi-filename queries are implemented, set multi_filename_queries to true and manage a list of multiple filenames here
def list(self, filepath, languages):
if not self.checkLanguages(languages):
return []
filepath = filenames[0]
guess = guessit.guess_file_info(filepath, 'autodetect')
if guess['type'] != 'episode':
self.logger.debug(u'Not an episode')
return []
# add multiple things to the release group set
release_group = set()
if 'releaseGroup' in guess:
release_group.add(guess['releaseGroup'])
release_group.add(guess['releaseGroup'].lower())
else:
if 'title' in guess:
release_group.add(guess['title'])
release_group.add(guess['title'].lower())
if 'screenSize' in guess:
release_group.add(guess['screenSize'])
release_group.add(guess['screenSize'].lower())
if 'series' not in guess or len(release_group) == 0:
self.logger.debug(u'Not enough information to proceed')
return []
self.release_group = release_group # used to sort results
return self.query(guess['series'], guess['season'], guess['episodeNumber'], release_group, filepath, languages)
def query(self, name, season, episode, release_group, filepath, languages=None):
''' Make a query and returns info about found subtitles '''
searchname = name.lower().replace(" ", "_")
searchurl = "%s/serie/%s/%s/%s/%s" % (self.server_url, searchname, season, episode, searchname)
self.logger.debug(u"Searching in %s" % searchurl)
searchname = name.lower().replace(' ', '_')
if isinstance(searchname, unicode):
searchname = searchname.encode('utf-8')
searchurl = '%s/serie/%s/%s/%s/%s' % (self.server_url, urllib2.quote(searchname), season, episode, urllib2.quote(searchname))
self.logger.debug(u'Searching in %s' % searchurl)
try:
req = urllib2.Request(searchurl, headers={'User-Agent': self.user_agent})
page = urllib2.urlopen(req, timeout=self.timeout)
except urllib2.HTTPError as inst:
self.logger.info(u"Error: %s - %s" % (searchurl, inst))
self.logger.info(u'Error: %s - %s' % (searchurl, inst))
return []
except urllib2.URLError as inst:
self.logger.info(u"TimeOut: %s" % inst)
self.logger.info(u'TimeOut: %s' % inst)
return []
soup = BeautifulSoup(page.read())
sublinks = []
for html_sub in soup("td", {"class": "NewsTitle", "colspan": "3"}):
for html_sub in soup('td', {'class': 'NewsTitle', 'colspan': '3'}):
if not self.release_pattern.match(str(html_sub.contents[1])): # On not needed soup td result
continue
sub_teams = self.listTeams([self.release_pattern.match(str(html_sub.contents[1])).groups()[0]], [".", "_", " "])
sub_teams = self.listTeams([self.release_pattern.match(str(html_sub.contents[1])).groups()[0].lower()], ['.', '_', ' ', '/', '-'])
if not release_group.intersection(sub_teams): # On wrong team
continue
html_language = html_sub.findNext("td", {"class": "language"})
html_language = html_sub.findNext('td', {'class': 'language'})
sub_language = self.getRevertLanguage(html_language.contents[0].strip().replace('&nbsp;', ''))
if languages and not sub_language in languages: # On wrong language
continue
@@ -131,28 +123,12 @@ class Addic7ed(PluginBase.PluginBase):
continue
sub_link = self.server_url + html_status.findNextSibling('td', {'colspan': '3'}).find('a')['href']
self.logger.debug(u'Found a match with teams: %s' % sub_teams)
result = {}
result["release"] = "%s.S%.2dE%.2d.%s" % (name.replace(" ", "."), int(season), int(episode), '.'.join(sub_teams))
result["lang"] = sub_language
result["link"] = sub_link
result["page"] = searchurl
result["filename"] = filepath
result["plugin"] = self.getClassName()
result["teams"] = sub_teams # used to sort
result = Subtitle(filepath, self.getSubtitlePath(filepath, sub_language), self.__class__.__name__, sub_language, sub_link, teams=sub_teams)
sublinks.append(result)
sublinks.sort(self._cmpTeams)
sublinks.sort(self._cmpReleaseGroup)
return sublinks
def download(self, subtitle):
'''pass the URL of the sub and the file it matches, will unzip it
and return the path to the created file'''
suburl = subtitle["link"]
videofilename = subtitle["filename"]
srtbasefilename = videofilename.rsplit(".", 1)[0]
srtfilename = srtbasefilename + self.getExtension(subtitle)
self.downloadFile(suburl, srtfilename)
return srtfilename
self.downloadFile(subtitle.link, subtitle.path)
return subtitle
def _cmpTeams(self, x, y):
''' Sort based on teams matching '''
return -cmp(len(x['teams'].intersection(self.release_group)), len(y['teams'].intersection(self.release_group)))
+40 -56
View File
@@ -24,19 +24,18 @@ from xml.dom import minidom
import guessit
import PluginBase
import os
import pickle
import traceback
import urllib
try:
import cPickle as pickle
except ImportError:
import pickle
import urllib2
from subliminal import encodingKludge as ek
from subliminal.classes import Subtitle
class BierDopje(PluginBase.PluginBase):
site_url = 'http://bierdopje.com'
site_name = 'BierDopje'
server_url = 'http://api.bierdopje.com/A2B638AC5D804C2E/'
multi_languages_queries = True
multi_filename_queries = False
api_based = True
exceptions = {'the office': 10358,
'the office us': 10358,
@@ -50,34 +49,33 @@ class BierDopje(PluginBase.PluginBase):
'hawaii five-0 2010': 14211}
_plugin_languages = {'en': 'en', 'nl': 'nl'}
def __init__(self, config_dict):
def __init__(self, config_dict=None):
super(BierDopje, self).__init__(self._plugin_languages, config_dict)
#http://api.bierdopje.com/23459DC262C0A742/GetShowByName/30+Rock
#http://api.bierdopje.com/23459DC262C0A742/GetAllSubsFor/94/5/1/en (30 rock, season 5, episode 1)
if not config_dict or not config_dict['cache_dir']:
raise Exception('Cache directory is mandatory for this plugin')
self.showid_cache = ek.ek(os.path.join, config_dict['cache_dir'], "bierdopje_showid.cache")
with self.lock:
if not ek.ek(os.path.exists, self.showid_cache):
if not ek.ek(os.path.exists, ek.ek(os.path.dirname, self.showid_cache)):
raise Exception("Cache directory doesn't exists")
f = open(self.showid_cache, 'w')
pickle.dump({}, f)
if config_dict and config_dict['cache_dir']:
self.showid_cache = os.path.join(config_dict['cache_dir'], 'bierdopje_showid.cache')
with self.lock:
if not os.path.exists(self.showid_cache):
if not os.path.exists(os.path.dirname(self.showid_cache)):
raise Exception('Cache directory does not exist')
f = open(self.showid_cache, 'w')
pickle.dump({}, f)
f.close()
f = open(self.showid_cache, 'r')
self.showids = pickle.load(f)
self.logger.debug(u'Reading showids from cache: %s' % self.showids)
f.close()
f = open(self.showid_cache, 'r')
self.showids = pickle.load(f)
self.logger.debug(u"Reading showids from cache: %s" % self.showids)
f.close()
def list(self, filenames, languages):
"""Main method to call when you want to list subtitles"""
# as self.multi_filename_queries is false, we won't have multiple filenames in the list so pick the only one
# once multi-filename queries are implemented, set multi_filename_queries to true and manage a list of multiple filenames here
if not self.checkLanguages(languages):
def list(self, filepath, languages):
if not self.config_dict['cache_dir']:
raise Exception('Cache directory is required for this plugin')
possible_languages = self.possible_languages(languages)
if not possible_languages:
return []
filepath = filenames[0]
guess = guessit.guess_file_info(filepath, 'autodetect')
if guess['type'] != 'episode':
self.logger.debug(u'Not an episode')
return []
# add multiple things to the release group set
release_group = set()
@@ -89,24 +87,17 @@ class BierDopje(PluginBase.PluginBase):
if 'screenSize' in guess:
release_group.add(guess['screenSize'].lower())
if 'series' not in guess or len(release_group) == 0:
self.logger.debug(u'Not enough information to proceed')
return []
self.release_group = release_group # used to sort results
return self.query(guess['series'], guess['season'], guess['episodeNumber'], release_group, filepath, languages)
return self.query(guess['series'], guess['season'], guess['episodeNumber'], release_group, filepath, possible_languages)
def download(self, subtitle):
"""Main method to call when you want to download a subtitle"""
subpath = subtitle["filename"].rsplit(".", 1)[0] + self.getExtension(subtitle)
self.downloadFile(subtitle["link"], subpath)
return subpath
self.downloadFile(subtitle.link, subtitle.path)
return subtitle
def query(self, name, season, episode, release_group, filepath, languages=None):
"""Makes a query and returns info (link, lang) about found subtitles"""
if languages:
available_languages = list(set(languages).intersection((self._plugin_languages.values())))
else:
available_languages = self._plugin_languages.values()
def query(self, name, season, episode, release_group, filepath, languages):
sublinks = []
# get the show id
show_name = name.lower()
if show_name in self.exceptions: # get it from exceptions
@@ -114,8 +105,11 @@ class BierDopje(PluginBase.PluginBase):
elif show_name in self.showids: # get it from cache
show_id = self.showids[show_name]
else: # retrieve it
show_id_url = "%sGetShowByName/%s" % (self.server_url, urllib.quote(show_name))
self.logger.debug(u"Retrieving show id from web at %s" % show_id_url)
show_name_encoded = show_name
if isinstance(show_name_encoded, unicode):
show_name_encoded = show_name_encoded.encode('utf-8')
show_id_url = '%sGetShowByName/%s' % (self.server_url, urllib2.quote(show_name_encoded))
self.logger.debug(u'Retrieving show id from web at %s' % show_id_url)
page = urllib2.urlopen(show_id_url)
dom = minidom.parse(page)
if not dom or len(dom.getElementsByTagName('showid')) == 0: # no proper result
@@ -125,21 +119,21 @@ class BierDopje(PluginBase.PluginBase):
self.showids[show_name] = show_id
with self.lock:
f = open(self.showid_cache, 'w')
self.logger.debug(u"Writing showid %s to cache file" % show_id)
self.logger.debug(u'Writing showid %s to cache file' % show_id)
pickle.dump(self.showids, f)
f.close()
page.close()
# get the subs for the show id we have
for language in available_languages:
subs_url = "%sGetAllSubsFor/%s/%s/%s/%s" % (self.server_url, show_id, season, episode, language)
self.logger.debug(u"Getting subtitles at %s" % subs_url)
for language in languages:
subs_url = '%sGetAllSubsFor/%s/%s/%s/%s' % (self.server_url, show_id, season, episode, language)
self.logger.debug(u'Getting subtitles at %s' % subs_url)
page = urllib2.urlopen(subs_url)
dom = minidom.parse(page)
page.close()
for sub in dom.getElementsByTagName('result'):
sub_release = sub.getElementsByTagName('filename')[0].firstChild.data
if sub_release.endswith(".srt"):
if sub_release.endswith('.srt'):
sub_release = sub_release[:-4]
sub_release = sub_release + '.avi' # put a random extension for guessit not to fail guessing that file
# guess information from subtitle
@@ -153,18 +147,8 @@ class BierDopje(PluginBase.PluginBase):
if 'screenSize' in sub_guess:
sub_release_group.add(sub_guess['screenSize'].lower())
sub_link = sub.getElementsByTagName('downloadlink')[0].firstChild.data
result = {}
result["release"] = sub_release
result["link"] = sub_link
result["page"] = sub_link
result["lang"] = language
result["filename"] = filepath
result["plugin"] = self.getClassName()
result["releaseGroup"] = sub_release_group
result = Subtitle(filepath, self.getSubtitlePath(filepath, language), self.__class__.__name__, language, sub_link, sub_release, sub_release_group)
sublinks.append(result)
sublinks.sort(self._cmpReleaseGroup)
return sublinks
def _cmpReleaseGroup(self, x, y):
"""Sort based on teams matching"""
return -cmp(len(x['releaseGroup'].intersection(self.release_group)), len(y['releaseGroup'].intersection(self.release_group)))
+64 -98
View File
@@ -26,97 +26,64 @@ import os
import socket
import xmlrpclib
import guessit
from subliminal import encodingKludge as ek
import unicodedata
from subliminal.classes import Subtitle, DownloadFailedError
class OpenSubtitles(PluginBase.PluginBase):
site_url = 'http://www.opensubtitles.org'
site_name = 'OpenSubtitles'
server_url = 'http://api.opensubtitles.org/xml-rpc'
user_agent = 'Subliminal v0.3'
multi_languages_queries = True
multi_filename_queries = False
user_agent = 'Subliminal v0.4'
api_based = True
_plugin_languages = {"en": "eng",
"fr": "fre",
"hu": "hun",
"cs": "cze",
"pl": "pol",
"sk": "slo",
"pt": "por",
"pt-br": "pob",
"es": "spa",
"el": "ell",
"ar": "ara",
"sq": "alb",
"hy": "arm",
"ay": "ass",
"bs": "bos",
"bg": "bul",
"ca": "cat",
"zh": "chi",
"hr": "hrv",
"da": "dan",
"nl": "dut",
"eo": "epo",
"et": "est",
"fi": "fin",
"gl": "glg",
"ka": "geo",
"de": "ger",
"he": "heb",
"hi": "hin",
"is": "ice",
"id": "ind",
"it": "ita",
"ja": "jpn",
"kk": "kaz",
"ko": "kor",
"lv": "lav",
"lt": "lit",
"lb": "ltz",
"mk": "mac",
"ms": "may",
"no": "nor",
"oc": "oci",
"fa": "per",
"ro": "rum",
"ru": "rus",
"sr": "scc",
"sl": "slv",
"sv": "swe",
"th": "tha",
"tr": "tur",
"uk": "ukr",
"vi": "vie"}
_plugin_languages = {'aa': 'aar', 'ab': 'abk', 'af': 'afr', 'ak': 'aka', 'sq': 'alb', 'am': 'amh', 'ar': 'ara', 'an': 'arg', 'hy': 'arm',
'as': 'asm', 'av': 'ava', 'ae': 'ave', 'ay': 'aym', 'az': 'aze', 'ba': 'bak', 'bm': 'bam', 'eu': 'baq', 'be': 'bel', 'bn': 'ben',
'bh': 'bih', 'bi': 'bis', 'bs': 'bos', 'br': 'bre', 'bg': 'bul', 'my': 'bur', 'ca': 'cat', 'ch': 'cha', 'ce': 'che', 'zh': 'chi',
'cu': 'chu', 'cv': 'chv', 'kw': 'cor', 'co': 'cos', 'cr': 'cre', 'cs': 'cze', 'da': 'dan', 'dv': 'div', 'nl': 'dut', 'dz': 'dzo',
'en': 'eng', 'eo': 'epo', 'et': 'est', 'ee': 'ewe', 'fo': 'fao', 'fj': 'fij', 'fi': 'fin', 'fr': 'fre', 'fy': 'fry', 'ff': 'ful',
'ka': 'geo', 'de': 'ger', 'gd': 'gla', 'ga': 'gle', 'gl': 'glg', 'gv': 'glv', 'el': 'ell', 'gn': 'grn', 'gu': 'guj', 'ht': 'hat',
'ha': 'hau', 'he': 'heb', 'hz': 'her', 'hi': 'hin', 'ho': 'hmo', 'hr': 'hrv', 'hu': 'hun', 'ig': 'ibo', 'is': 'ice', 'io': 'ido',
'ii': 'iii', 'iu': 'iku', 'ie': 'ile', 'ia': 'ina', 'id': 'ind', 'ik': 'ipk', 'it': 'ita', 'jv': 'jav', 'ja': 'jpn', 'kl': 'kal',
'kn': 'kan', 'ks': 'kas', 'kr': 'kau', 'kk': 'kaz', 'km': 'khm', 'ki': 'kik', 'rw': 'kin', 'ky': 'kir', 'kv': 'kom', 'kg': 'kon',
'ko': 'kor', 'kj': 'kua', 'ku': 'kur', 'lo': 'lao', 'la': 'lat', 'lv': 'lav', 'li': 'lim', 'ln': 'lin', 'lt': 'lit', 'lb': 'ltz',
'lu': 'lub', 'lg': 'lug', 'mk': 'mac', 'mh': 'mah', 'ml': 'mal', 'mi': 'mao', 'mr': 'mar', 'ms': 'may', 'mg': 'mlg', 'mt': 'mlt',
'mo': 'mol', 'mn': 'mon', 'na': 'nau', 'nv': 'nav', 'nr': 'nbl', 'nd': 'nde', 'ng': 'ndo', 'ne': 'nep', 'nn': 'nno', 'nb': 'nob',
'no': 'nor', 'ny': 'nya', 'oc': 'oci', 'oj': 'oji', 'or': 'ori', 'om': 'orm', 'os': 'oss', 'pa': 'pan', 'fa': 'per', 'pi': 'pli',
'pl': 'pol', 'pt': 'por', 'ps': 'pus', 'qu': 'que', 'rm': 'roh', 'rn': 'run', 'ru': 'rus', 'sg': 'sag', 'sa': 'san', 'sr': 'scc',
'si': 'sin', 'sk': 'slo', 'sl': 'slv', 'se': 'sme', 'sm': 'smo', 'sn': 'sna', 'sd': 'snd', 'so': 'som', 'st': 'sot', 'es': 'spa',
'sc': 'srd', 'ss': 'ssw', 'su': 'sun', 'sw': 'swa', 'sv': 'swe', 'ty': 'tah', 'ta': 'tam', 'tt': 'tat', 'te': 'tel', 'tg': 'tgk',
'tl': 'tgl', 'th': 'tha', 'bo': 'tib', 'ti': 'tir', 'to': 'ton', 'tn': 'tsn', 'ts': 'tso', 'tk': 'tuk', 'tr': 'tur', 'tw': 'twi',
'ug': 'uig', 'uk': 'ukr', 'ur': 'urd', 'uz': 'uzb', 've': 'ven', 'vi': 'vie', 'vo': 'vol', 'cy': 'wel', 'wa': 'wln', 'wo': 'wol',
'xh': 'xho', 'yi': 'yid', 'yo': 'yor', 'za': 'zha', 'zu': 'zul', 'ro': 'rum', 'pb': 'pob', 'un': 'unk', 'ay': 'ass'}
def __init__(self, config_dict=None):
super(OpenSubtitles, self).__init__(self._plugin_languages, config_dict)
def list(self, filenames, languages):
"""Main method to call when you want to list subtitles """
# as self.multi_filename_queries is false, we won't have multiple filenames in the list so pick the only one
# once multi-filename queries are implemented, set multi_filename_queries to true and manage a list of multiple filenames here
filepath = filenames[0]
if ek.ek(os.path.isfile, filepath):
def list(self, filepath, languages):
possible_languages = self.possible_languages(languages)
if not possible_languages:
return []
if os.path.isfile(filepath):
filehash = self.hashFile(filepath)
size = ek.ek(os.path.getsize, filepath)
return self.query(moviehash=filehash, languages=languages, bytesize=size, filepath=filepath)
size = os.path.getsize(filepath)
return self.query(moviehash=filehash, languages=possible_languages, bytesize=size, filepath=filepath)
else:
return self.query(languages=languages, filepath=filepath)
return self.query(languages=possible_languages, filepath=filepath)
def download(self, subtitle):
"""Main method to call when you want to download a subtitle """
subtitleFilename = subtitle["filename"].rsplit(".", 1)[0] + self.getExtension(subtitle)
self.downloadFile(subtitle["link"], subtitleFilename + ".gz")
f = ek.ek(gzip.open, subtitleFilename + ".gz")
dump = ek.ek(open, subtitleFilename, "wb")
dump.write(f.read())
self.adjustPermissions(subtitleFilename)
dump.close()
f.close()
ek.ek(os.remove, subtitleFilename + ".gz")
return subtitleFilename
try:
self.downloadFile(subtitle.link, subtitle.path + '.gz')
with open(subtitle.path, 'wb') as dump:
gz = gzip.open(subtitle.path + '.gz')
dump.write(gz.read())
gz.close()
self.adjustPermissions(subtitle.path)
os.remove(subtitle.path + '.gz')
except Exception as e:
if os.path.exists(subtitle.path):
os.remove(subtitle.path)
raise DownloadFailedError(str(e))
return subtitle
def query(self, filepath, imdbID=None, moviehash=None, bytesize=None, languages=None):
"""Makes a query on OpenSubtitles and returns info about found subtitles.
@@ -130,26 +97,26 @@ class OpenSubtitles(PluginBase.PluginBase):
if bytesize:
search['moviebytesize'] = str(bytesize)
if languages:
search['sublanguageid'] = ",".join([self.getLanguage(l) for l in languages])
search['sublanguageid'] = ','.join([self.getLanguage(l) for l in languages])
if not imdbID and not moviehash and not bytesize:
self.logger.debug(u"No search term, we'll use the filename")
self.logger.debug(u'No search term, using the filename')
guess = guessit.guess_file_info(filepath, 'autodetect')
if guess['type'] == 'episode' and 'series' in guess:
search['query'] = guess['series']
search['query'] = guess['series'].lower()
elif guess['type'] == 'movie':
search['query'] = guess['title']
search['query'] = guess['title'].lower()
else: # we don't know what we have
return[]
# login
self.server = xmlrpclib.Server(self.server_url)
socket.setdefaulttimeout(self.timeout)
try:
log_result = self.server.LogIn("", "", "eng", self.user_agent)
if not log_result["status"] or log_result["status"] != '200 OK' or not log_result["token"]:
log_result = self.server.LogIn('', '', 'eng', self.user_agent)
if not log_result['status'] or log_result['status'] != '200 OK' or not log_result['token']:
raise Exception('OpenSubtitles login failed')
token = log_result["token"]
token = log_result['token']
except Exception:
self.logger.error(u"Cannot login")
self.logger.error(u'Cannot login')
token = None
socket.setdefaulttimeout(None)
return []
@@ -159,32 +126,30 @@ class OpenSubtitles(PluginBase.PluginBase):
try:
self.server.LogOut(token)
except:
self.logger.error(u"Cannot logout")
self.logger.error(u'Cannot logout')
socket.setdefaulttimeout(None)
return sublinks
def get_results(self, token, search, filepath):
self.logger.debug(u"Query uses token %s and search parameters %s" % (token, search))
self.logger.debug(u'Query uses token %s and search parameters %s' % (token, search))
try:
results = self.server.SearchSubtitles(token, [search])
except Exception, e:
self.logger.debug(u"Cannot query the server")
except Exception:
self.logger.debug(u'Cannot query the server')
return []
if not results['data']: # no subtitle found
return []
sublinks = []
self.filename = self.getFileName(filepath)
for r in sorted(results['data'], self._cmpSubFileName):
result = {}
result["release"] = r['SubFileName']
result["link"] = r['SubDownloadLink']
result["page"] = r['SubDownloadLink']
result["lang"] = self.getRevertLanguage(r['SubLanguageID'])
result["filename"] = filepath
result["plugin"] = self.getClassName()
if 'query' in search and not r["MovieReleaseName"].replace('.', ' ').startswith(search['query']): # query mode search, filter results
self.logger.debug(u"Skipping %s it does not start with %s" % (r["MovieReleaseName"].replace('.', ' '), search['query']))
continue
result = Subtitle(filepath, self.getSubtitlePath(filepath, self.getRevertLanguage(r['SubLanguageID'])), self.__class__.__name__, self.getRevertLanguage(r['SubLanguageID']), r['SubDownloadLink'], r['SubFileName'])
if 'query' in search: # query mode search, filter results
query_encoded = search['query']
if isinstance(query_encoded, unicode):
query_encoded = unicodedata.normalize('NFKD', query_encoded).encode('ascii', 'ignore')
if not r['MovieReleaseName'].replace('.', ' ').lower().startswith(query_encoded):
self.logger.debug(u'Skipping %s it does not start with %s' % (r['MovieReleaseName'].replace('.', ' ').lower(), query_encoded))
continue
sublinks.append(result)
return sublinks
@@ -204,3 +169,4 @@ class OpenSubtitles(PluginBase.PluginBase):
if not xmatch and ymatch:
return 1
return 0
+56 -81
View File
@@ -22,18 +22,15 @@
import abc
import logging
import os
import re
import sys
import urllib2
import struct
import threading
from subliminal import encodingKludge as ek
import socket
from subliminal.classes import DownloadFailedError
class PluginBase(object):
__metaclass__ = abc.ABCMeta
multi_languages_queries = False
multi_filename_queries = False
api_based = True
timeout = 3
user_agent = 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.3)'
@@ -51,128 +48,101 @@ class PluginBase(object):
else:
self.revertPluginLanguages = pluginLanguages
self.pluginLanguages = dict((v, k) for k, v in self.revertPluginLanguages.iteritems())
self.logger = logging.getLogger('subliminal.%s' % self.getClassName())
self.logger = logging.getLogger('subliminal.%s' % self.__class__.__name__)
@staticmethod
def getFileName(filepath):
filename = filepath
if ek.ek(os.path.isfile, filename):
filename = ek.ek(os.path.basename, filename)
if os.path.isfile(filename):
filename = os.path.basename(filename)
if filename.endswith(('.avi', '.wmv', '.mov', '.mp4', '.mpeg', '.mpg', '.mkv')):
filename = filename.rsplit('.', 1)[0]
return filename
def possible_languages(self, languages):
possible_languages = languages & set(self.pluginLanguages.keys())
if not possible_languages:
self.logger.debug(u'Languages %r are not in supported languages' % languages)
return possible_languages
def hashFile(self, filename):
"""Hash a file like OpenSubtitles"""
longlongformat = 'q' # long long
bytesize = struct.calcsize(longlongformat)
f = ek.ek(open, filename, "rb")
filesize = ek.ek(os.path.getsize, filename)
f = open(filename, 'rb')
filesize = os.path.getsize(filename)
hash = filesize
if filesize < 65536 * 2:
self.logger.error(u"File %s is too small (SizeError < 2**16)" % filename)
self.logger.error(u'File %s is too small (SizeError < 2**16)' % filename)
return []
for x in range(65536 / bytesize):
for _ in range(65536 / bytesize):
buffer = f.read(bytesize)
(l_value,) = struct.unpack(longlongformat, buffer)
hash += l_value
hash = hash & 0xFFFFFFFFFFFFFFFF # to remain as 64bit number
f.seek(max(0, filesize - 65536), 0)
for x in range(65536 / bytesize):
for _ in range(65536 / bytesize):
buffer = f.read(bytesize)
(l_value,) = struct.unpack(longlongformat, buffer)
hash += l_value
hash = hash & 0xFFFFFFFFFFFFFFFF
f.close()
returnedhash = "%016x" % hash
returnedhash = '%016x' % hash
return returnedhash
def downloadFile(self, url, filename, data=None):
"""Downloads the given url to the given filename"""
def downloadFile(self, url, filepath, data=None):
"""Download a subtitle file"""
self.logger.info(u'Downloading %s' % url)
socket.setdefaulttimeout(self.timeout)
try:
self.logger.info(u"Downloading %s" % url)
req = urllib2.Request(url, headers={'Referer': url, 'User-Agent': self.user_agent})
f = urllib2.urlopen(req, data=data)
dump = ek.ek(open, filename, "wb")
dump.write(f.read())
self.adjustPermissions(filename)
dump.close()
f.close()
self.logger.debug(u"Download finished for file %s. Size: %s" % (filename, ek.ek(os.path.getsize, filename)))
except urllib2.HTTPError, e:
self.logger.error(u"HTTP Error:", e.code, url)
except urllib2.URLError, e:
self.logger.error(u"URL Error:", e.reason, url)
with open(filepath, 'wb') as dump:
f = urllib2.urlopen(req, data=data)
dump.write(f.read())
self.adjustPermissions(filepath)
f.close()
except Exception as e:
self.logger.error(u'Download %s failed: %s' % (url, e))
if os.path.exists(filepath):
os.remove(filepath)
raise DownloadFailedError(str(e))
finally:
socket.setdefaulttimeout(self.timeout)
self.logger.debug(u'Download finished for file %s. Size: %s' % (filepath, os.path.getsize(filepath)))
def adjustPermissions(self, filepath):
if self.config_dict and 'files_mode' in self.config_dict and self.config_dict['files_mode'] != -1:
ek.ek(os.chmod, filepath, self.config_dict['files_mode'])
os.chmod(filepath, self.config_dict['files_mode'])
@abc.abstractmethod
def list(self, filenames, languages):
"""Main method to call when you want to list subtitles"""
def list(self, filepath, languages):
"""List subtitles"""
@abc.abstractmethod
def download(self, subtitle):
"""Main method to call when you want to download a subtitle"""
"""Download a subtitle"""
def getRevertLanguage(self, language):
"""Returns the short (two-character) representation from the long language name"""
"""ISO-639-1 language code from plugin language code"""
try:
return self.revertPluginLanguages[language]
except KeyError, e:
self.logger.warn(u"Ooops, you found a missing language in the configuration file of %s: %s. Send a bug report to have it added." % (self.getClassName(), language))
def checkLanguages(self, languages):
if languages and not set(languages).intersection((self._plugin_languages.values())):
self.logger.debug(u'None of requested languages %s are available' % languages)
return False
return True
except KeyError:
self.logger.warn(u'Ooops, you found a missing language in the configuration file of %s: %s. Send a bug report to have it added.' % (self.__class__.__name__, language))
def getLanguage(self, language):
"""Returns the long naming of the language from a two character code"""
"""Plugin language code from ISO-639-1 language code"""
try:
return self.pluginLanguages[language]
except KeyError, e:
self.logger.warn(u"Ooops, you found a missing language in the configuration file of %s: %s. Send a bug report to have it added." % (self.getClassName(), language))
def getExtension(self, subtitle):
except KeyError:
self.logger.warn(u'Ooops, you found a missing language in the configuration file of %s: %s. Send a bug report to have it added.' % (self.__class__.__name__, language))
def getSubtitlePath(self, video_path, language):
if not os.path.exists(video_path):
video_path = os.path.split(video_path)[1]
path = video_path.rsplit('.', 1)[0]
if self.config_dict and self.config_dict['multi']:
return ".%s.srt" % subtitle['lang']
return ".srt"
def getClassName(self):
return self.__class__.__name__
def splitTask(self, task):
"""Determines if the plugin can handle multi-thing queries and output splited tasks for list task only"""
if task['task'] != 'list':
return [task]
tasks = [task]
if not self.multi_filename_queries:
tasks = self._splitOnField(tasks, 'filenames')
if not self.multi_languages_queries:
tasks = self._splitOnField(tasks, 'languages')
return tasks
@staticmethod
def _splitOnField(elements, field):
"""
Split a list of dict in a bigger one if the element field in the dict has multiple elements too
i.e. [{'a': 1, 'b': [2,3]}, {'a': 7, 'b': [4]}] => [{'a': 1, 'b': [2]}, {'a': 1, 'b': [3]}, {'a': 7, 'b': [4]}]
with field = 'b'
"""
results = []
for e in elements:
for v in e[field]:
newElement = {}
for (key, value) in e.items():
if key != field:
newElement[key] = value
else:
newElement[key] = [v]
results.append(newElement)
return results
return path + '.%s.srt' % language
return path + '.srt'
def listTeams(self, sub_teams, separators):
"""List teams of a given string using separators"""
@@ -186,3 +156,8 @@ class PluginBase(object):
for t in sub_teams:
teams += t.split(sep)
return teams
def _cmpReleaseGroup(self, x, y):
"""Sort based on teams matching"""
return -cmp(len(x.teams.intersection(self.release_group)), len(y.teams.intersection(self.release_group)))
+2 -13
View File
@@ -23,22 +23,14 @@
from hashlib import md5, sha256
import PluginBase
import xmlrpclib
import struct
import socket
import zipfile
import os
import urllib2
import urllib
import traceback
from subliminal import encodingKludge as ek
class Podnapisi(PluginBase.PluginBase):
site_url = "http://www.podnapisi.net"
site_name = "Podnapisi"
server_url = 'http://ssp.podnapisi.net:8000'
multi_languages_queries = True
multi_filename_queries = False
api_based = True
_plugin_languages = {"sl": "1",
"en": "2",
@@ -96,10 +88,8 @@ class Podnapisi(PluginBase.PluginBase):
def list(self, filenames, languages):
"""Main method to call when you want to list subtitles"""
# as self.multi_filename_queries is false, we won't have multiple filenames in the list so pick the only one
# once multi-filename queries are implemented, set multi_filename_queries to true and manage a list of multiple filenames here
filepath = filenames[0]
if not ek.ek(os.path.isfile, filepath):
if not os.path.isfile(filepath):
return []
return self.query(self.hashFile(filepath), languages)
@@ -116,7 +106,7 @@ class Podnapisi(PluginBase.PluginBase):
self.logger.debug(u"Result: %s" % log_result)
token = log_result["session"]
nonce = log_result["nonce"]
except Exception, e:
except Exception:
self.logger.error(u"Cannot login" % log_result)
socket.setdefaulttimeout(None)
return []
@@ -141,6 +131,5 @@ class Podnapisi(PluginBase.PluginBase):
subs = []
for sub in results['results']:
subs.append(sub)
d = self.server.download(token, [173793])
self.server.terminate(token)
return subs
+10 -19
View File
@@ -25,18 +25,12 @@ import PluginBase
import zipfile
import os
import urllib2
import urllib
import traceback
import httplib
from subliminal import encodingKludge as ek
class SubScene(PluginBase.PluginBase):
site_url = 'http://subscene.com'
site_name = 'SubScene'
server_url = 'http://subscene.com/s.aspx?subtitle='
multi_languages_queries = True
multi_filename_queries = False
api_based = False
_plugin_languages = {"en": "English",
"se": "Swedish",
@@ -79,8 +73,6 @@ class SubScene(PluginBase.PluginBase):
def list(self, filenames, languages):
"""Main method to call when you want to list subtitles"""
# as self.multi_filename_queries is false, we won't have multiple filenames in the list so pick the only one
# once multi-filename queries are implemented, set multi_filename_queries to true and manage a list of multiple filenames here
filepath = filenames[0]
fname = self.getFileName(filepath)
subs = self.query(fname, filepath, languages)
@@ -110,7 +102,7 @@ class SubScene(PluginBase.PluginBase):
extension = el.orig_filename.rsplit(".", 1)[1]
if extension in ("srt", "sub", "txt"):
subtitlefilename = srtbasefilename + "." + extension
outfile = ek.ek(open, subtitlefilename, "wb")
outfile = open(subtitlefilename, "wb")
outfile.write(zf.read(el.orig_filename))
outfile.flush()
self.adjustPermissions(subtitlefilename)
@@ -119,7 +111,7 @@ class SubScene(PluginBase.PluginBase):
self.logger.info(u"File %s does not seem to be valid " % el.orig_filename)
# Deleting the zip file
zf.close()
ek.ek(os.remove, archivefilename)
os.remove(archivefilename)
return subtitlefilename
elif archivefilename.endswith('.rar'):
self.logger.warn(u'Rar is not really supported yet. Trying to call unrar')
@@ -130,16 +122,16 @@ class SubScene(PluginBase.PluginBase):
for el in output.splitlines():
extension = el.rsplit(".", 1)[1]
if extension in ("srt", "sub"):
args = ['unrar', 'e', archivefilename, el, ek.ek(os.path.dirname, archivefilename)]
args = ['unrar', 'e', archivefilename, el, os.path.dirname(archivefilename)]
subprocess.Popen(args)
tmpsubtitlefilename = ek.ek(os.path.join, ek.ek(os.path.dirname, archivefilename), el)
subtitlefilename = ek.ek(os.path.join, ek.ek(os.path.dirname, archivefilename), srtbasefilename + "." + extension)
if ek.ek(os.path.exists, tmpsubtitlefilename):
tmpsubtitlefilename = os.path.join(os.path.dirname(archivefilename), el)
subtitlefilename = os.path.join(os.path.dirname(archivefilename), srtbasefilename + "." + extension)
if os.path.exists(tmpsubtitlefilename):
# rename it to match the file
ek.ek(os.rename, tmpsubtitlefilename, subtitlefilename)
os.rename(tmpsubtitlefilename, subtitlefilename)
# exit
return subtitlefilename
except OSError, e:
except OSError as e:
self.logger.error(u"Execution failed: %s" % e)
return None
else:
@@ -149,12 +141,11 @@ class SubScene(PluginBase.PluginBase):
def downloadFile(self, url, filename):
"""Downloads the given url to the given filename"""
#FIXME: Not working
super(SubScene, self).downloadFile(url, filename, urllib.urlencode({'__EVENTTARGET': 's$lc$bcr$downloadLink', '__EVENTARGUMENT': '', '__VIEWSTATE': '/wEPDwUHNzUxOTkwNWRk4wau5efPqhlBJJlOkKKHN8FIS04='}))
def query(self, token, filepath, langs=None):
"""Make a query on SubScene and returns info about found subtitles"""
sublinks = []
searchurl = "%s%s" % (self.server_url, urllib.quote(token))
searchurl = "%s%s" % (self.server_url, urllib2.quote(token))
self.logger.debug(u"Query: %s" % searchurl)
page = urllib2.urlopen(searchurl)
soup = BeautifulSoup(page.read())
@@ -172,6 +163,6 @@ class SubScene(PluginBase.PluginBase):
result["link"] = None
result["page"] = self.site_url + sub_page
result["filename"] = filepath
result["plugin"] = self.getClassName()
result["plugin"] = self.__class__.__name__
sublinks.append(result)
return sublinks
+42 -82
View File
@@ -22,135 +22,95 @@
from BeautifulSoup import BeautifulSoup
import PluginBase
import zipfile
import urllib2
import urllib
import logging
import traceback
import httplib
import re
import guessit
from subliminal import encodingKludge as ek
from subliminal.classes import Subtitle
class SubsWiki(PluginBase.PluginBase):
site_url = 'http://www.subswiki.com'
site_name = 'SubsWiki'
server_url = 'http://www.subswiki.com'
multi_languages_queries = True
multi_filename_queries = False
api_based = False
_plugin_languages = {u"English (US)": "en",
u"English (UK)": "en",
u"English": "en",
u"French": "fr",
u"Brazilian": "pt-br",
u"Portuguese": "pt",
u"Español (Latinoamérica)": "es",
u"Español (España)": "es",
u"Español": "es",
u"Italian": "it",
u"Català": "ca"}
_plugin_languages = {u'English (US)': 'en',
u'English (UK)': 'en',
u'English': 'en',
u'French': 'fr',
u'Brazilian': 'pt-br',
u'Portuguese': 'pt',
u'Español (Latinoamérica)': 'es',
u'Español (España)': 'es',
u'Español': 'es',
u'Italian': 'it',
u'Català': 'ca'}
def __init__(self, config_dict=None):
super(SubsWiki, self).__init__(self._plugin_languages, config_dict, True)
self.release_pattern = re.compile("\nVersion (.+), ([0-9]+).([0-9])+ MBs")
self.release_pattern = re.compile('\nVersion (.+), ([0-9]+).([0-9])+ MBs')
def list(self, filenames, languages):
"""Main method to call when you want to list subtitles"""
# as self.multi_filename_queries is false, we won't have multiple filenames in the list so pick the only one
# once multi-filename queries are implemented, set multi_filename_queries to true and manage a list of multiple filenames here
filepath = filenames[0]
if not self.checkLanguages(languages):
def list(self, filepath, languages):
possible_languages = self.possible_languages(languages)
if not possible_languages:
return []
guess = guessit.guess_file_info(filepath, 'autodetect')
if guess['type'] != 'episode':
self.logger.debug(u'Not an episode')
return []
# add multiple things to the release group set
release_group = set()
if 'releaseGroup' in guess:
release_group.add(guess['releaseGroup'])
release_group.add(guess['releaseGroup'].lower())
else:
if 'title' in guess:
release_group.add(guess['title'])
release_group.add(guess['title'].lower())
if 'screenSize' in guess:
release_group.add(guess['screenSize'])
release_group.add(guess['screenSize'].lower())
if 'series' not in guess or len(release_group) == 0:
self.logger.debug(u'Not enough information to proceed')
return []
self.release_group = release_group # used to sort results
return self.query(guess['series'], guess['season'], guess['episodeNumber'], release_group, filepath, languages)
return self.query(guess['series'], guess['season'], guess['episodeNumber'], release_group, filepath, possible_languages)
def query(self, name, season, episode, release_group, filepath, languages=None):
"""Make a query and returns info about found subtitles"""
def query(self, name, season, episode, release_group, filepath, languages):
sublinks = []
searchname = name.lower().replace(" ", "_")
searchurl = "%s/serie/%s/%s/%s/" % (self.server_url, searchname, season, episode)
self.logger.debug(u"Searching in %s" % searchurl)
searchname = name.lower().replace(' ', '_')
if isinstance(searchname, unicode):
searchname = searchname.encode('utf-8')
searchurl = '%s/serie/%s/%s/%s/' % (self.server_url, urllib2.quote(searchname), season, episode)
self.logger.debug(u'Searching in %s' % searchurl)
try:
req = urllib2.Request(searchurl, headers={'User-Agent': self.user_agent})
page = urllib2.urlopen(req, timeout=self.timeout)
except urllib2.HTTPError as inst:
self.logger.info(u"Error: %s - %s" % (searchurl, inst))
self.logger.info(u'Error: %s - %s' % (searchurl, inst))
return []
except urllib2.URLError as inst:
self.logger.info(u"TimeOut: %s" % inst)
self.logger.info(u'TimeOut: %s' % inst)
return []
soup = BeautifulSoup(page.read())
for subs in soup("td", {"class": "NewsTitle"}):
sub_teams = self.listTeams([self.release_pattern.search("%s" % subs.contents[1]).group(1)], [".", "_", " ", "/", "-"])
for subs in soup('td', {'class': 'NewsTitle'}):
sub_teams = self.listTeams([self.release_pattern.search('%s' % subs.contents[1]).group(1).lower()], ['.', '_', ' ', '/', '-'])
if not release_group.intersection(sub_teams): # On wrong team
continue
self.logger.debug(u"Team from website: %s" % sub_teams)
self.logger.debug(u"Team from file: %s" % release_group)
for html_language in subs.parent.parent.findAll("td", {"class": "language"}):
self.logger.debug(u'Team from website: %s' % sub_teams)
self.logger.debug(u'Team from file: %s' % release_group)
for html_language in subs.parent.parent.findAll('td', {'class': 'language'}):
sub_language = self.getRevertLanguage(html_language.string.strip())
self.logger.debug(u"Subtitle reverted language: %s" % sub_language)
if languages and not sub_language in languages: # On wrong language
self.logger.debug(u'Subtitle reverted language: %s' % sub_language)
if not sub_language in languages: # On wrong language
continue
html_status = html_language.findNextSibling('td')
sub_status = html_status.find('strong').string.strip()
if not sub_status == 'Completed': # On not completed subtitles
continue
sub_link = html_status.findNext("td").find("a")["href"]
result = {}
result["release"] = "%s.S%.2dE%.2d.%s" % (name.replace(" ", "."), int(season), int(episode), '.'.join(sub_teams))
result["lang"] = sub_language
result["link"] = self.server_url + sub_link
result["page"] = searchurl
result["filename"] = filepath
result["plugin"] = self.getClassName()
result["teams"] = sub_teams # used to sort
sub_link = html_status.findNext('td').find('a')['href']
result = Subtitle(filepath, self.getSubtitlePath(filepath, sub_language), self.__class__.__name__, sub_language, self.server_url + sub_link, teams=sub_teams)
sublinks.append(result)
sublinks.sort(self._cmpTeams)
sublinks.sort(self._cmpReleaseGroup)
return sublinks
def download(self, subtitle):
"""Main method to call when you want to download a subtitle"""
subtitleFilename = subtitle["filename"].rsplit(".", 1)[0] + self.getExtension(subtitle)
self.downloadFile(subtitle["link"], subtitleFilename)
return subtitleFilename
self.downloadFile(subtitle.link, subtitle.path)
return subtitle
def listTeams(self, subteams, separators):
teams = []
for sep in separators:
subteams = self.splitTeam(subteams, sep)
return set(subteams)
def splitTeam(self, subteams, sep):
teams = []
for t in subteams:
teams += t.split(sep)
return teams
def downloadFile(self, url, filename):
"""Downloads the given url to the given filename"""
req = urllib2.Request(url, headers={'Referer': url, 'User-Agent': 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.3)'})
f = urllib2.urlopen(req)
dump = ek.ek(open, filename, "wb")
dump.write(f.read())
dump.close()
f.close()
def _cmpTeams(self, x, y):
"""Sort based on teams matching"""
return -cmp(len(x['teams'].intersection(self.release_group)), len(y['teams'].intersection(self.release_group)))
-122
View File
@@ -1,122 +0,0 @@
# -*- coding: utf-8 -*-
#
# Subliminal - Subtitles, faster than your thoughts
# Copyright (c) 2008-2011 Patrick Dessalle <patrick@dessalle.be>
# Copyright (c) 2011 Antoine Bertin <diaoulael@gmail.com>
#
# This file is part of Subliminal.
#
# Subliminal is free software; you can redistribute it and/or modify it under
# the terms of the Lesser GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Subliminal 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
# Lesser GNU General Public License for more details.
#
# You should have received a copy of the Lesser GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import ConfigParser
import PluginBase
import traceback
import urllib
import urllib2
import xml.dom.minidom
class SubtitleSource(PluginBase.PluginBase):
site_url = 'http://www.subtitlesource.org'
site_name = 'SubtitleSource'
server_url = 'http://www.subtitlesource.org/api/%s/3.0/xmlsearch'
multi_languages_queries = True
multi_filename_queries = False
api_based = True
_plugin_languages = {"en": "English",
"sv": "Swedish",
"da": "Danish",
"fi": "Finnish",
"no": "Norwegian",
"fr": "French",
"es": "Spanish",
"is": "Icelandic"}
def __init__(self, config_dict=None):
super(SubtitleSource, self).__init__(self._plugin_languages, config_dict)
if config_dict and "subtitlesource_key" in config_dict:
self.server_url = self.server_url % config_dict["subtitlesource_key"]
else:
self.logger.error(u'SubtitleSource API Key is mandatory for this plugin')
raise Exception('SubtitleSource API Key is mandatory for this plugin')
def list(self, filenames, languages):
"""Main method to call when you want to list subtitles"""
filepath = filenames[0]
fname = self.getFileName(filepath)
subs = self.query(fname, languages)
if not subs and fname.rfind(".[") > 0:
# Try to remove the [VTV] or [EZTV] at the end of the file
teamless_filename = fname[0:fname.rfind(".[")]
subs = self.query(teamless_filename, languages)
return subs
else:
return subs
def query(self, token, languages=None):
"""Makes a query on SubtitlesSource and returns info (link, lang) about found subtitles"""
self.logger.debug(u"Local file is: %s " % token)
sublinks = []
if not languages: # langs is empty of None
languages = ["all"]
else: # parse each lang to generate the equivalent lang
languages = [self._plugin_languages[l] for l in languages if l in self._plugin_languages.keys()]
# Get the CD part of this
metaData = self.guessFileData(token)
multipart = metaData.get('part', None)
part = metaData.get('part')
if not part: # part will return None if not found using the regex
part = 1
for lang in languages:
searchurl = "%s/%s/%s/0" % (self.server_url, urllib.quote(token), lang)
self.logger.debug(u"dl'ing %s" % searchurl)
page = urllib2.urlopen(searchurl, timeout=self.timeout)
xmltree = xml.dom.minidom.parse(page)
subs = xmltree.getElementsByTagName("sub")
for sub in subs:
sublang = self.getRevertLanguage(self.getValue(sub, "language"))
if languages and not sublang in languages:
continue # The language of this sub is not wanted => Skip
if multipart and not int(self.getValue(sub, 'cd')) > 1:
continue # The subtitle is not a multipart
dllink = "http://www.subtitlesource.org/download/text/%s/%s" % (self.getValue(sub, "id"), part)
self.logger.debug(u"Link added: %s (%s)" % (dllink, sublang))
result = {}
result["release"] = self.getValue(sub, "releasename")
result["link"] = dllink
result["page"] = dllink
result["lang"] = sublang
releaseMetaData = self.guessFileData(result['release'])
teams = set(metaData['teams'])
srtTeams = set(releaseMetaData['teams'])
self.logger.debug(u"Analyzing: %s " % result['release'])
self.logger.debug(u"Local file has: %s " % metaData['teams'])
self.logger.debug(u"Remote sub has: %s " % releaseMetaData['teams'])
if result['release'].startswith(token) or (releaseMetaData['name'] == metaData['name'] and releaseMetaData['type'] == metaData['type'] and (teams.issubset(srtTeams) or srtTeams.issubset(teams))):
sublinks.append(result)
return sublinks
def download(self, subtitle):
"""Main method to call when you want to download a subtitle"""
suburl = subtitle["link"]
videofilename = subtitle["filename"]
srtfilename = videofilename.rsplit(".", 1)[0] + self.getExtension(subtitle)
self.downloadFile(suburl, srtfilename)
return srtfilename
def getValue(self, sub, tagName):
for node in sub.childNodes:
if node.nodeType == node.ELEMENT_NODE and node.tagName == tagName:
return node.childNodes[0].nodeValue
+34 -75
View File
@@ -22,49 +22,33 @@
from BeautifulSoup import BeautifulSoup
import guessit
import zipfile
import urllib2
import urllib
import logging
import traceback
import httplib
import unicodedata
import re
import PluginBase
from subliminal import encodingKludge as ek
from subliminal.classes import Subtitle
class Subtitulos(PluginBase.PluginBase):
site_url = 'http://www.subtitulos.es'
site_name = 'Subtitulos'
server_url = 'http://www.subtitulos.es'
multi_languages_queries = True
multi_filename_queries = False
api_based = False
_plugin_languages = {u"English (US)": "en",
u"English (UK)": "en",
u"English": "en",
u"French": "fr",
u"Brazilian": "pt-br",
u"Portuguese": "pt",
u"Español (Latinoamérica)": "es",
u"Español (España)": "es",
u"Español": "es",
u"Italian": "it",
u"Català": "ca"}
_plugin_languages = {u'English (US)': 'en', u'English (UK)': 'en', u'English': 'en', u'French': 'fr', u'Brazilian': 'pt-br',
u'Portuguese': 'pt', u'Español (Latinoamérica)': 'es', u'Español (España)': 'es', u'Español': 'es', u'Italian': 'it',
u'Català': 'ca'}
def __init__(self, config_dict=None):
super(Subtitulos, self).__init__(self._plugin_languages, config_dict, True)
self.release_pattern = re.compile("Versi&oacute;n (.+) ([0-9]+).([0-9])+ megabytes")
self.release_pattern = re.compile('Versi&oacute;n (.+) ([0-9]+).([0-9])+ megabytes')
def list(self, filenames, languages):
"""Main method to call when you want to list subtitles"""
# as self.multi_filename_queries is false, we won't have multiple filenames in the list so pick the only one
# once multi-filename queries are implemented, set multi_filename_queries to true and manage a list of multiple filenames here
if not self.checkLanguages(languages):
def list(self, filepath, languages):
possible_languages = self.possible_languages(languages)
if not possible_languages:
return []
filepath = filenames[0]
guess = guessit.guess_file_info(filepath, 'autodetect')
if guess['type'] != 'episode':
self.logger.debug(u'Not an episode')
return []
# add multiple things to the release group set
release_group = set()
@@ -76,75 +60,50 @@ class Subtitulos(PluginBase.PluginBase):
if 'screenSize' in guess:
release_group.add(guess['screenSize'].lower())
if 'series' not in guess or len(release_group) == 0:
self.logger.debug(u'Not enough information to proceed')
return []
self.release_group = release_group # used to sort results
return self.query(guess['series'], guess['season'], guess['episodeNumber'], release_group, filepath, languages)
return self.query(guess['series'], guess['season'], guess['episodeNumber'], release_group, filepath, possible_languages)
def query(self, name, season, episode, release_group, filepath, languages=None):
"""Make a query and returns info about found subtitles"""
def query(self, name, season, episode, release_group, filepath, languages):
sublinks = []
searchname = name.lower().replace(" ", "-")
searchurl = "%s/%s/%sx%.2d" % (self.server_url, searchname, season, episode)
self.logger.debug(u"Searching in %s" % searchurl)
searchname = name.lower().replace(' ', '-')
if isinstance(searchname, unicode):
searchname = unicodedata.normalize('NFKD', searchname).encode('ascii','ignore')
searchurl = '%s/%s/%sx%.2d' % (self.server_url, urllib2.quote(searchname), season, episode)
self.logger.debug(u'Searching in %s' % searchurl)
try:
req = urllib2.Request(searchurl, headers={'User-Agent': self.user_agent})
page = urllib2.urlopen(req, timeout=self.timeout)
except urllib2.HTTPError as inst:
self.logger.info(u"Error: %s - %s" % (searchurl, inst))
self.logger.info(u'Error: %s - %s' % (searchurl, inst))
return []
except urllib2.URLError as inst:
self.logger.info(u"TimeOut: %s" % inst)
self.logger.info(u'TimeOut: %s' % inst)
return []
soup = BeautifulSoup(page.read())
for subs in soup("div", {"id": "version"}):
version = subs.find("p", {"class": "title-sub"})
sub_teams = self.listTeams([self.release_pattern.search("%s" % version.contents[1]).group(1).lower()], [".", "_", " ", "/"])
for subs in soup('div', {'id': 'version'}):
version = subs.find('p', {'class': 'title-sub'})
sub_teams = self.listTeams([self.release_pattern.search('%s' % version.contents[1]).group(1).lower()], ['.', '_', ' ', '/', '-'])
self.logger.debug(u'Team from website: %s' % sub_teams)
self.logger.debug(u'Team from file: %s' % release_group)
if not release_group.intersection(sub_teams): # On wrong team
continue
self.logger.debug(u"Team from website: %s" % sub_teams)
self.logger.debug(u"Team from file: %s" % release_group)
for html_language in subs.findAllNext("ul", {"class": "sslist"}):
sub_language = self.getRevertLanguage(html_language.findNext("li", {"class": "li-idioma"}).find("strong").contents[0].string.strip())
if languages and not sub_language in languages: # On wrong language
for html_language in subs.findAllNext('ul', {'class': 'sslist'}):
sub_language = self.getRevertLanguage(html_language.findNext('li', {'class': 'li-idioma'}).find('strong').contents[0].string.strip())
if not sub_language in languages: # On wrong language
continue
html_status = html_language.findNext("li", {"class": "li-estado green"})
html_status = html_language.findNext('li', {'class': 'li-estado green'})
sub_status = html_status.contents[0].string.strip()
if not sub_status == 'Completado': # On not completed subtitles
continue
sub_link = html_status.findNext("span", {"class": "descargar green"}).find("a")["href"]
result = {}
result["release"] = "%s.S%.2dE%.2d.%s" % (name.replace(" ", "."), int(season), int(episode), '.'.join(sub_teams))
result["lang"] = sub_language
result["link"] = sub_link
result["page"] = searchurl
result["filename"] = filepath
result["plugin"] = self.getClassName()
result["teams"] = sub_teams # used to sort
sub_link = html_status.findNext('span', {'class': 'descargar green'}).find('a')['href']
result = Subtitle(filepath, self.getSubtitlePath(filepath, sub_language), self.__class__.__name__, sub_language, sub_link, teams=sub_teams)
sublinks.append(result)
sublinks.sort(self._cmpTeams)
sublinks.sort(self._cmpReleaseGroup)
return sublinks
def download(self, subtitle):
"""
Pass the URL of the sub and the file it matches, will unzip it
and return the path to the created file
"""
suburl = subtitle["link"]
videofilename = subtitle["filename"]
srtbasefilename = videofilename.rsplit(".", 1)[0]
srtfilename = srtbasefilename + ".srt"
self.downloadFile(suburl, srtfilename)
return srtfilename
self.downloadFile(subtitle.link, subtitle.path)
return subtitle
def downloadFile(self, url, filename):
"""Downloads the given url to the given filename"""
req = urllib2.Request(url, headers={'Referer': url, 'User-Agent': 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.3)'})
f = urllib2.urlopen(req)
dump = ek.ek(open, filename, "wb")
dump.write(f.read())
dump.close()
f.close()
def _cmpTeams(self, x, y):
"""Sort based on teams matching"""
return -cmp(len(x['teams'].intersection(self.release_group)), len(y['teams'].intersection(self.release_group)))
+23 -61
View File
@@ -24,51 +24,33 @@ import PluginBase
import hashlib
import os
import urllib2
from subliminal import encodingKludge as ek
from subliminal.classes import Subtitle
class TheSubDB(PluginBase.PluginBase):
site_url = 'http://thesubdb.com'
site_name = 'SubDB'
server_url = 'http://api.thesubdb.com' # for testing purpose, use http://sandbox.thesubdb.com instead
multi_languages_queries = True
multi_filename_queries = False
api_based = True
user_agent = 'SubDB/1.0 (Subliminal/0.1; https://github.com/Diaoul/subliminal)' # defined by the API
_plugin_languages = {'cs': 'cs', # the whole list is available with the API: http://sandbox.thesubdb.com/?action=languages
'da': 'da',
'de': 'de',
'en': 'en',
'fi': 'fi',
'fr': 'fr',
'hu': 'hu',
'id': 'id',
'it': 'it',
'nl': 'nl',
'no': 'no',
'pl': 'pl',
'pt': 'pt',
'ro': 'ro',
'ru': 'ru',
'sl': 'sl',
'sr': 'sr',
'sv': 'sv',
'tr': 'tr'}
user_agent = 'SubDB/1.0 (Subliminal/0.4; https://github.com/Diaoul/subliminal)' # defined by the API
_plugin_languages = {'af': 'af', 'cs': 'cs', 'da': 'da', 'de': 'de', 'en': 'en', 'es': 'es', 'fi': 'fi', 'fr': 'fr', 'hu': 'hu', 'id': 'id',
'it': 'it', 'la': 'la', 'nl': 'nl', 'no': 'no', 'oc': 'oc', 'pl': 'pl', 'pt': 'pt', 'ro': 'ro', 'ru': 'ru', 'sl': 'sl', 'sr': 'sr',
'sv': 'sv', 'tr': 'tr'} # list available with the API at http://sandbox.thesubdb.com/?action=languages
def __init__(self, config_dict=None):
super(TheSubDB, self).__init__(self._plugin_languages, config_dict)
def list(self, filenames, languages):
"""Main method to call when you want to list subtitles"""
# as self.multi_filename_queries is false, we won't have multiple filenames in the list so pick the only one
# once multi-filename queries are implemented, set multi_filename_queries to true and manage a list of multiple filenames here
filepath = filenames[0]
if not ek.ek(os.path.isfile, filepath):
def list(self, filepath, languages):
possible_languages = self.possible_languages(languages)
if not possible_languages:
return []
return self.query(filepath, self.hashFile(filepath), languages)
if not os.path.isfile(filepath):
return []
return self.query(filepath, self.hashFile(filepath), possible_languages)
def query(self, filepath, moviehash, languages=None):
searchurl = "%s/?action=%s&hash=%s" % (self.server_url, "search", moviehash)
def query(self, filepath, moviehash, languages):
searchurl = '%s/?action=%s&hash=%s' % (self.server_url, 'search', moviehash)
self.logger.debug(u'Query URL: %s' % searchurl)
try:
req = urllib2.Request(searchurl, headers={'User-Agent': self.user_agent})
@@ -76,50 +58,30 @@ class TheSubDB(PluginBase.PluginBase):
except urllib2.HTTPError as inst:
if inst.code == 404: # no result found
return []
self.logger.error(u"Error: %s - %s" % (searchurl, inst))
self.logger.error(u'Error: %s - %s' % (searchurl, inst))
return []
except urllib2.URLError as inst:
self.logger.error(u"TimeOut: %s" % inst)
self.logger.error(u'TimeOut: %s' % inst)
return []
available_languages = page.readlines()[0].split(',')
self.logger.debug(u'Available languages: %s' % available_languages)
subs = []
for l in available_languages:
if not languages or l in languages:
result = {}
result['release'] = filepath
result['lang'] = l
result['link'] = "%s/?action=download&hash=%s&language=%s" % (self.server_url, moviehash, l)
result['page'] = result['link']
result['filename'] = filepath
result['plugin'] = self.getClassName()
if l in languages:
result = Subtitle(filepath, self.getSubtitlePath(filepath, l), self.__class__.__name__, l, '%s/?action=download&hash=%s&language=%s' % (self.server_url, moviehash, l))
subs.append(result)
return subs
def hashFile(self, name):
"""This hash function receives the filename and returns the hash code"""
def hashFile(self, filepath):
"""TheSubDB specific hash function"""
readsize = 64 * 1024
with ek.ek(open, name, 'rb') as f:
size = ek.ek(os.path.getsize, name)
with open(filepath, 'rb') as f:
data = f.read(readsize)
f.seek(-readsize, os.SEEK_END)
data += f.read(readsize)
return hashlib.md5(data).hexdigest()
def download(self, subtitle):
"""Main method to call when you want to download a subtitle"""
suburl = subtitle["link"]
videofilename = subtitle["filename"]
srtfilename = videofilename.rsplit(".", 1)[0] + self.getExtension(subtitle)
self.downloadFile(suburl, srtfilename)
return srtfilename
self.downloadFile(subtitle.link, subtitle.path)
return subtitle
def downloadFile(self, url, srtfilename):
"""Downloads the given url to the given filename"""
req = urllib2.Request(url)
req.add_header('User-Agent', self.user_agent)
f = urllib2.urlopen(req)
dump = open(srtfilename, "wb")
dump.write(f.read())
dump.close()
f.close()
+2 -2
View File
@@ -20,12 +20,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from Addic7ed import Addic7ed
#from Addic7ed import Addic7ed
from BierDopje import BierDopje
from OpenSubtitles import OpenSubtitles
#from Podnapisi import Podnapisi
#from SubScene import SubScene
from SubsWiki import SubsWiki
#from SubtitleSource import SubtitleSource
from Subtitulos import Subtitulos
from TheSubDB import TheSubDB
Executable → Regular
+241 -343
View File
@@ -19,409 +19,307 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import threading
from itertools import groupby
import ConfigParser
import PluginWorker
from classes import DownloadTask, ListTask, StopTask, LanguageError, PluginError, BadStateError, WrongTaskError, DownloadFailedError
import Queue
import locale
import logging
import mimetypes
import os
import plugins
import sys
import traceback
import locale
import encodingKludge as ek
SUPPORTED_FORMATS = 'video/x-msvideo', 'video/quicktime', 'video/x-matroska', 'video/mp4'
logger = logging.getLogger('subliminal')
SYS_ENCODING = None
# be nice
try:
locale.setlocale(locale.LC_ALL, "")
SYS_ENCODING = locale.getpreferredencoding()
except (locale.Error, IOError):
pass
# for OSes that are poorly configured I'll just force UTF-8
if not SYS_ENCODING or SYS_ENCODING in ('ANSI_X3.4-1968', 'US-ASCII', 'ASCII'):
SYS_ENCODING = 'UTF-8'
from logging import NullHandler
except ImportError:
class NullHandler(logging.Handler):
def emit(self, record):
pass
logger = logging.getLogger('subliminal')
logger.addHandler(NullHandler())
# const
FORMATS = ['video/x-msvideo', 'video/quicktime', 'video/x-matroska', 'video/mp4']
EXTENSIONS = set(['srt', 'sub'])
LANGUAGES = set(['aa', 'ab', 'ae', 'af', 'ak', 'am', 'an', 'ar', 'as', 'av', 'ay', 'az', 'ba', 'be', 'bg', 'bh', 'bi',
'bm', 'bn', 'bo', 'br', 'bs', 'ca', 'ce', 'ch', 'co', 'cr', 'cs', 'cu', 'cv', 'cy', 'da', 'de', 'dv',
'dz', 'ee', 'el', 'en', 'eo', 'es', 'et', 'eu', 'fa', 'ff', 'fi', 'fj', 'fo', 'fr', 'fy', 'ga', 'gd',
'gl', 'gn', 'gu', 'gv', 'ha', 'he', 'hi', 'ho', 'hr', 'ht', 'hu', 'hy', 'hz', 'ia', 'id', 'ie', 'ig',
'ii', 'ik', 'io', 'is', 'it', 'iu', 'ja', 'jv', 'ka', 'kg', 'ki', 'kj', 'kk', 'kl', 'km', 'kn', 'ko',
'kr', 'ks', 'ku', 'kv', 'kw', 'ky', 'la', 'lb', 'lg', 'li', 'ln', 'lo', 'lt', 'lu', 'lv', 'mg', 'mh',
'mi', 'mk', 'ml', 'mn', 'mo', 'mr', 'ms', 'mt', 'my', 'na', 'nb', 'nd', 'ne', 'ng', 'nl', 'nn', 'no',
'nr', 'nv', 'ny', 'oc', 'oj', 'om', 'or', 'os', 'pa', 'pi', 'pl', 'ps', 'pt', 'qu', 'rm', 'rn', 'ro',
'ru', 'rw', 'sa', 'sc', 'sd', 'se', 'sg', 'si', 'sk', 'sl', 'sm', 'sn', 'so', 'sq', 'sr', 'ss', 'st',
'su', 'sv', 'sw', 'ta', 'te', 'tg', 'th', 'ti', 'tk', 'tl', 'tn', 'to', 'tr', 'ts', 'tt', 'tw', 'ty',
'ug', 'uk', 'ur', 'uz', 've', 'vi', 'vo', 'wa', 'wo', 'xh', 'yi', 'yo', 'za', 'zh', 'zu']) # ISO 639-1
PLUGINS = ['BierDopje', 'OpenSubtitles', 'SubsWiki', 'Subtitulos', 'TheSubDB']
API_PLUGINS = filter(lambda p: getattr(plugins, p).api_based, PLUGINS)
IDLE = 0
RUNNING = 1
PAUSED = 2
class Subliminal(object):
"""Main Subliminal class"""
def __init__(self, config=True, cache_dir=True, workers=4, multi=False, force=False, max_depth=3, autostart=False, plugins_config=None, files_mode=-1):
# set default values
def __init__(self, cache_dir=None, workers=4, multi=False, force=False, max_depth=3, files_mode=-1):
self.multi = multi
self.force = force
self.max_depth = max_depth
self.config = None
self.config_file = None
self.cache_dir = None
self.taskQueue = Queue.Queue()
self.resultQueue = Queue.Queue()
self._languages = None
self._plugins = self.listAPIPlugins()
self.taskQueue = Queue.PriorityQueue()
self.listResultQueue = Queue.Queue()
self.downloadResultQueue = Queue.Queue()
self._languages = []
self._plugins = API_PLUGINS
self.workers = workers
self.plugins_config = plugins_config
self.files_mode = files_mode
if autostart:
self.startWorkers()
# handle configuration file preferences
self.state = IDLE
try:
if config == True: # default configuration file
import xdg.BaseDirectory as bd
self.config = ConfigParser.SafeConfigParser({"languages": "", "plugins": ""})
self.config_file = ek.ek(os.path.join, bd.xdg_config_home, "subliminal", "config.ini")
if not ek.ek(os.path.exists, self.config_file): # configuration file doesn't exist, create it
self._createConfigFile()
else: # configuration file exists, load it
self._loadConfigFile()
elif config: # custom configuration file
self.config = ConfigParser.SafeConfigParser({"languages": "", "plugins": ""})
self.config_file = config
if not ek.ek(os.path.isfile, self.config_file): # custom configuration file doesn't exist, create it
self._createConfigFile()
else:
self._loadConfigFile()
except:
self.config = None
self.config_file = None
logger.error(u"Failed to use the configuration file, continue without it")
raise
# handle cache directory preferences
try:
if cache_dir == True: # default cache directory
import xdg.BaseDirectory as bd
self.cache_dir = ek.ek(os.path.join, bd.xdg_config_home, "subliminal", "cache")
if not ek.ek(os.path.exists, self.cache_dir): # cache directory doesn't exist, create it
ek.ek(os.mkdir, self.cache_dir)
logger.debug(u'Creating cache directory: %s' % self.cache_dir)
elif cache_dir: # custom configuration file
if cache_dir:
self.cache_dir = cache_dir
if not ek.ek(os.path.isdir, self.cache_dir): # custom v file doesn't exist, create it
ek.ek(os.mkdir, self.cache_dir)
logger.debug(u'Creating cache directory: %s' % self.cache_dir)
if not os.path.isdir(self.cache_dir):
os.makedirs(self.cache_dir)
logger.debug(u'Creating cache directory: %r' % self.cache_dir)
except:
self.cache_dir = None
logger.error(u"Failed to use the cache directory, continue without it")
logger.error(u'Failed to use the cache directory, continue without it')
def _loadConfigFile(self):
"""Load a configuration file specified in self.config_file"""
self.config.read(self.config_file)
self._loadLanguagesFromConfig()
self._loadPluginsFromConfig()
def _createConfigFile(self):
"""Create a configuration file specified in self.config_file"""
folder = ek.ek(os.path.dirname, self.config_file)
if not ek.ek(os.path.exists, folder):
logger.info(u"Creating folder: %s" % folder)
ek.ek(os.mkdir, folder)
# try to load a language from system
self._loadLanguageFromSystem()
self.config.set("DEFAULT", "languages", ",".join(self._languages))
self.config.set("DEFAULT", "plugins", ",".join(self._plugins))
self.config.add_section("SubtitleSource")
self.config.set("SubtitleSource", "key", "")
self._writeConfigFile()
logger.info(u"Creating configuration file: %s" % self.config_file)
logger.debug(u"Languages in created configuration file: %s" % self._languages)
logger.debug(u"Plugins in created configuration file: %s" % self._plugins)
@staticmethod
def listExistingPlugins():
"""List all possible plugins"""
return map(lambda x: x.__name__, plugins.PluginBase.PluginBase.__subclasses__())
@staticmethod
def listAPIPlugins():
"""List plugins that use API"""
return filter(Subliminal.isAPIBasedPlugin, Subliminal.listExistingPlugins())
def _writeConfigFile(self):
"""Write the configuration file"""
configfile = open(self.config_file, "w")
self.config.write(configfile)
configfile.close()
def get_languages(self):
"""Get current languages"""
@property
def languages(self):
"""Getter for languages"""
return self._languages
def set_languages(self, value):
"""Set languages and save to configuration file if specified by the constructor"""
logger.debug(u"Setting languages to %s" % value)
self._languages = value
if self.config:
self._saveLanguagesToConfig()
@languages.setter
def languages(self, languages):
"""Setter for languages"""
logger.debug(u'Setting languages to %r' % languages)
self._languages = []
for l in languages:
if l not in LANGUAGES:
raise LanguageError(l)
if not l in self._languages:
self._languages.append(l)
@staticmethod
def isValidLanguage(language):
"""Check if a language is valid"""
if len(language) != 2:
logger.error(u"Language %s is not valid" % language)
return False
return True
def _saveLanguagesToConfig(self):
"""Save languages to configuration file"""
logger.debug(u"Saving languages %s to configuration file" % self._languages)
self.config.set("DEFAULT", "languages", ",".join(self._languages))
self._writeConfigFile()
def _loadLanguagesFromConfig(self):
"""Load languages from configuration file"""
configLanguages = self.config.get("DEFAULT", "languages")
logger.debug(u"Loading languages %s from configuration file" % configLanguages)
if not configLanguages:
self._languages = None
return
self._languages = filter(self.isValidLanguage, map(str.strip, configLanguages.split(",")))
def _loadLanguageFromSystem(self):
"""Load language from system"""
logger.debug(u"Loading language from system")
try:
self._languages = [locale.getdefaultlocale()[0][:2]]
logger.debug(u"Language %s loaded from system" % self._languages)
except:
logger.warning(u"Could not read language from system")
def get_plugins(self):
"""Get current plugins"""
@property
def plugins(self):
"""Getter for plugins"""
return self._plugins
def set_plugins(self, value):
"""Set plugins and save to configuration file if specified by the constructor"""
logger.debug(u'Setting plugins to %r' % value)
self._plugins = filter(self.isValidPlugin, value)
if self.config:
self._savePluginsToConfig()
@plugins.setter
def plugins(self, plugins):
"""Setter for plugins"""
logger.debug(u'Setting plugins to %r' % plugins)
self._plugins = []
for p in plugins:
if p not in PLUGINS:
raise PluginError(p)
if not p in self._plugins:
self._plugins.append(p)
@staticmethod
def isValidPlugin(pluginName):
"""Check if a plugin is valid (exists)"""
if pluginName not in Subliminal.listExistingPlugins():
logger.error(u"Plugin %s does not exist" % pluginName)
return False
return True
@staticmethod
def isAPIBasedPlugin(pluginName):
"""Check if a plugin is API-based"""
if not getattr(plugins, pluginName).api_based:
return False
return True
def _savePluginsToConfig(self):
"""Save plugins to configuration file"""
logger.debug(u"Saving plugins %s to configuration file" % self._plugins)
self.config.set("DEFAULT", "plugins", ",".join(self._plugins))
self._writeConfigFile()
def _loadPluginsFromConfig(self):
"""Load plugins from configuration file"""
configPlugins = self.config.get("DEFAULT", "plugins")
logger.debug(u"Loading plugins %s from configuration file" % configPlugins)
self._plugins = filter(self.isValidPlugin, map(str.strip, configPlugins.split(",")))
# getters/setters for the property _languages and _plugins
languages = property(get_languages, set_languages)
plugins = property(get_plugins, set_plugins)
def deactivatePlugin(self, plugin):
"""Deactivate a plugin"""
self._plugins.remove(plugin)
if self.config:
self._savePluginsToConfig()
def activatePlugin(self, plugin):
"""Activate a plugin"""
if self.isValidPlugin(plugin):
self._plugins.append(plugin)
if self.config:
self._savePluginsToConfig()
def listSubtitles(self, entries):
def listSubtitles(self, entries, auto=True):
"""
Searches subtitles within the active plugins and returns all found matching subtitles.
entries can be:
- filepaths
- folderpaths (N.B. internal recursive search function will be used)
- filenames
"""
search_results = []
Search subtitles within the plugins and return all found subtitles in a list of Subtitle object.
Attributes:
entries -- filepath or folderpath of video file or a list of that
auto -- automaticaly manage workers"""
if auto:
if self.state != IDLE:
raise BadStateError(self.state, IDLE)
self.startWorkers()
if isinstance(entries, basestring):
entries = [ek.fixStupidEncodings(entries)]
elif not isinstance(entries, list):
raise TypeError('Entries should be a list or a string')
entries = [entries]
scan_result = []
for e in entries:
search_results.extend(self._recursiveSearch(e))
taskCount = 0
for (l, f) in search_results:
taskCount += self.searchSubtitlesThreaded(f, l)
if not isinstance(e, unicode):
logger.warning(u'Entry %r is not unicode' % e)
if not os.path.exists(e):
scan_result.append((e, set(), False))
continue
scan_result.extend(scan(e))
task_count = 0
for filepath, languages, has_single in scan_result:
wanted_languages = set(self._languages)
if not wanted_languages:
wanted_languages = LANGUAGES
if not self.force and self.multi:
wanted_languages = set(wanted_languages) - languages
if not wanted_languages:
logger.debug(u'No need to list multi subtitles %r for %r because %r subtitles detected' % (self._languages, filepath, languages))
continue
if not self.force and not self.multi and has_single:
logger.debug(u'No need to list single subtitles %r for %r because one detected' % (self._languages, filepath))
continue
logger.debug(u'Listing subtitles %r for %r with %r' % (wanted_languages, filepath, self._plugins))
for plugin in self._plugins:
self.taskQueue.put((5, ListTask(filepath, wanted_languages, plugin, self.getConfigDict())))
task_count += 1
subtitles = []
for i in range(taskCount):
subtitles.extend(self.resultQueue.get(timeout=10))
for _ in range(task_count):
subtitles.extend(self.listResultQueue.get())
if auto:
self.stopWorkers()
return subtitles
@staticmethod
def arrangeSubtitles(subtitles):
"""Arrange subtitles in a handy dict by filename, language and plugin"""
arrangedSubtitles = {}
for (filename, subsByFilename) in groupby(sorted(subtitles, key=lambda x: x["filename"]), lambda x: x["filename"]):
arrangedSubtitles[filename] = {}
for (language, subsByFilenameByLanguage) in groupby(sorted(subsByFilename, key=lambda x: x["lang"]), lambda x: x["lang"]):
arrangedSubtitles[filename][language] = {}
for (plugin, subsByFilenameByLanguageByPlugin) in groupby(sorted(subsByFilenameByLanguage, key=lambda x: x["plugin"]), lambda x: x["plugin"]):
arrangedSubtitles[filename][language][plugin] = sorted(list(subsByFilenameByLanguageByPlugin))
return arrangedSubtitles
def sortSubtitlesRaw(self, subtitles):
"""Sort subtitles using user defined languages and plugins"""
return sorted(subtitles, cmp=self._cmpSubtitles)
def _cmpSubtitles(self, x, y):
def downloadSubtitles(self, entries, auto=True):
"""
Compares 2 subtitles elements x and y. Returns -1 if x < y, 0 if =, 1 if >
Use filename, languages and plugin comparison
"""
filenames = sorted([x['filename'], y['filename']])
if x['filename'] != y['filename'] and filenames.index(x['filename']) < filenames(y['filename']):
Download subtitles using the plugins preferences and languages. Also use internal algorithm to find
the best match inside a plugin.
Attributes:
entries -- filepath or folderpath of video file or a list of that
auto -- automaticaly manage workers"""
if auto:
if self.state != IDLE:
raise BadStateError(self.state, IDLE)
self.startWorkers()
subtitles = self.listSubtitles(entries, False)
task_count = 0
for _, subsByVideoPath in groupby(sorted(subtitles, key=lambda x: x.video_path), lambda x: x.video_path):
if not self.multi:
self.taskQueue.put((5, DownloadTask(sorted(list(subsByVideoPath), cmp=self.cmpSubtitles))))
task_count += 1
continue
for __, subsByVideoPathByLanguage in groupby(sorted(subsByVideoPath, key=lambda x: x.language), lambda x: x.language):
self.taskQueue.put((5, DownloadTask(sorted(list(subsByVideoPathByLanguage), cmp=self.cmpSubtitles))))
task_count += 1
downloaded = []
for _ in range(task_count):
downloaded.extend(self.downloadResultQueue.get())
if auto:
self.stopWorkers()
return downloaded
def cmpSubtitles(self, x, y):
"""Compares 2 subtitles elements x and y using video_path, languages and plugin"""
video_paths = sorted([x.video_path, y.video_path])
if x.video_path != y.video_path and video_paths.index(x.video_path) < video_paths(y.video_path):
return - 1
if x['filename'] != y['filename'] and filenames.index(x['filename']) > filenames(y['filename']):
if x.video_path != y.video_path and video_paths.index(x.video_path) > video_paths(y.video_path):
return 1
if self._languages and self._languages.index(x['lang']) < self._languages.index(y['lang']):
if self._languages and self._languages.index(x.language) < self._languages.index(y.language):
return - 1
if self._languages and self._languages.index(x['lang']) > self._languages.index(y['lang']):
if self._languages and self._languages.index(x.language) > self._languages.index(y.language):
return 1
if self._plugins.index(x['plugin']) < self._plugins.index(y['plugin']):
if self._plugins.index(x.plugin) < self._plugins.index(y.plugin):
return - 1
if self._plugins.index(x['plugin']) > self._plugins.index(y['plugin']):
if self._plugins.index(x.plugin) > self._plugins.index(y.plugin):
return 1
return 0
def searchSubtitlesThreaded(self, filenames, languages):
"""
Makes workers search for subtitles in different languages for multiple filenames and puts the result in the result queue.
Aslo split the work in multiple tasks
When the function returns, all the results may not be available yet!
"""
logger.info(u"Searching subtitles for %s with languages %s" % (filenames, languages))
tasks = []
for pluginName in self._plugins:
try:
plugin = getattr(plugins, pluginName)(self.getConfigDict())
except:
logger.debug(traceback.print_exc())
continue
# split tasks if the plugin can't handle multi-thing queries
tasks.extend(plugin.splitTask({'task': 'list', 'plugin': pluginName, 'languages': languages, 'filenames': filenames, 'config': self.getConfigDict()}))
for t in tasks:
self.taskQueue.put(t)
return len(tasks)
def downloadSubtitlesThreaded(self, subtitles):
"""
Makes workers download subtitles and puts the result in the result queue.
When the function returns, all the results may not be available yet!
"""
# 1 task per file if not multi, 1 task per file and per language if multi
taskCount = 0
for (filename, subsByFilename) in groupby(sorted(subtitles, key=lambda x: x["filename"]), lambda x: x["filename"]):
if not self.multi:
self.taskQueue.put({'task': 'download', 'subtitle': sorted(list(subsByFilename), cmp=self._cmpSubtitles), 'config': self.getConfigDict()})
taskCount += 1
continue
for (language, subsByFilenameByLanguage) in groupby(sorted(subsByFilename, key=lambda x: x["lang"]), lambda x: x["lang"]):
self.taskQueue.put({'task': 'download', 'subtitle': sorted(list(subsByFilenameByLanguage), cmp=self._cmpSubtitles), 'config': self.getConfigDict()})
taskCount += 1
return taskCount
def downloadSubtitles(self, entries):
"""Download subtitles recursivly in entries"""
subtitles = self.listSubtitles(entries)
taskCount = self.downloadSubtitlesThreaded(subtitles)
paths = []
for i in range(taskCount):
paths.append(self.resultQueue.get(timeout=10))
return paths
def _recursiveSearch(self, entry, depth=0):
"""
Searches files in the entry
This will output a list of tuples (filename, languages)
"""
if depth > self.max_depth and self.max_depth != 0: # we do not want to search the whole file system except if max_depth = 0
return []
if ek.ek(os.path.isfile, entry): # a file? scan it
if depth != 0: # only check for valid format if recursing, trust the user
mimetypes.add_type("video/x-matroska", ".mkv")
mimetype = mimetypes.guess_type(entry)[0]
if mimetype not in SUPPORTED_FORMATS:
return []
basepath = ek.fixStupidEncodings(ek.ek(os.path.splitext, entry)[0])
# check for .xx.srt if needed
if self.multi and self.languages:
if self.force:
return [(self.languages, [ek.ek(os.path.normpath, entry)])]
needed_languages = self.languages[:]
for l in self.languages:
if ek.ek(os.path.exists, basepath + '.%s.srt' % l):
logger.info(u"Skipping language %s for file %s as it already exists. Use the --force option to force the download" % (l, entry))
needed_languages.remove(l)
if needed_languages:
return [(needed_languages, [ek.ek(os.path.normpath, entry)])]
return []
# single subtitle download: .srt
if self.force or not ek.ek(os.path.exists, basepath + '.srt'):
return [(self.languages, [ek.ek(os.path.normpath, entry)])]
if ek.ek(os.path.isdir, entry): # a dir? recurse
#TODO if hidden folder, don't keep going (how to handle windows/mac/linux ?)
files = []
for e in ek.ek(os.listdir, entry):
files.extend(self._recursiveSearch(ek.ek(os.path.join, entry, e), depth + 1))
files.sort()
grouped_files = []
for languages, group in groupby(files, lambda t: t[0]):
filenames = []
for t in group:
filenames.extend(t[1])
grouped_files.append((languages, filenames))
return grouped_files
return [] # anything else, nothing.
def startWorkers(self):
"""Create a pool of workers and start them"""
self.pool = []
for i in range(self.workers):
worker = PluginWorker.PluginWorker(self.taskQueue, self.resultQueue)
for _ in range(self.workers):
worker = PluginWorker(self.taskQueue, self.listResultQueue, self.downloadResultQueue)
worker.start()
self.pool.append(worker)
logger.debug(u"Worker %s added to the pool" % worker.name)
def sendStopSignal(self):
"""Send a stop signal the pool of workers (poison pill)"""
logger.debug(u"Sending %d poison pills into the task queue" % self.workers)
for i in range(self.workers):
self.taskQueue.put(None)
logger.debug(u'Worker %s added to the pool' % worker.name)
self.state = RUNNING
def stopWorkers(self):
"""Stop workers using a stop signal and wait for them to terminate properly"""
self.sendStopSignal()
"""Stop workers using a lowest priority stop signal and wait for them to terminate properly"""
for _ in range(self.workers):
self.taskQueue.put((10, StopTask()))
for worker in self.pool:
worker.join()
self.state = IDLE
def pauseWorkers(self):
"""Pause workers using a highest priority stop signal and wait for them to terminate properly"""
for _ in range(self.workers):
self.taskQueue.put((0, StopTask()))
for worker in self.pool:
worker.join()
self.state = PAUSED
if self.taskQueue.empty():
self.state = STOPPED
def getConfigDict(self):
"""Produce a dict with configuration items. Used by plugins to read configuration"""
config = {}
config['multi'] = self.multi
config['cache_dir'] = self.cache_dir
if self.config:
config['subtitlesource_key'] = self.config.get('SubtitleSource', 'key')
if self.plugins_config and 'subtitlesource_key' in self.plugins_config:
config['subtitlesource_key'] = self.plugins_config['subtitlesource_key']
config['force'] = self.force
config['files_mode'] = self.files_mode
return config
def addTask(self, task):
if not isinstance(task, Task) or isinstance(task, StopTask):
raise WrongTaskError()
self.taskQueue.put((5, task))
class PluginWorker(threading.Thread):
"""Threaded plugin worker"""
def __init__(self, taskQueue, listResultQueue, downloadResultQueue):
threading.Thread.__init__(self)
self.taskQueue = taskQueue
self.listResultQueue = listResultQueue
self.downloadResultQueue = downloadResultQueue
self.logger = logging.getLogger('subliminal.worker')
def run(self):
while True:
task = self.taskQueue.get()[1]
if isinstance(task, StopTask):
self.logger.debug(u'Poison pill received, terminating thread %s' % self.name)
self.taskQueue.task_done()
break
result = []
try:
if isinstance(task, ListTask):
plugin = getattr(plugins, task.plugin)(task.config)
result = plugin.list(task.filepath, task.languages)
elif isinstance(task, DownloadTask):
for subtitle in task.subtitles:
plugin = getattr(plugins, subtitle.plugin)()
try:
result = [plugin.download(subtitle)]
break
except DownloadFailedError as e:
self.logger.warning(u'Could not download subtitle %r, trying next' % subtitle)
continue
if not result:
self.logger.error(u'No subtitles could be downloaded for file %r' % subtitle.video_path)
except:
self.logger.error(u'Exception raised in worker %s' % self.name, exc_info=True)
finally:
if isinstance(task, ListTask):
self.listResultQueue.put(result)
elif isinstance(task, DownloadTask):
self.downloadResultQueue.put(result)
self.taskQueue.task_done()
self.logger.debug(u'Thread %s terminated' % self.name)
def scan(entry, depth=0, max_depth=3):
"""Scan a path and return a list of tuples (filepath, set(languages), has single)"""
if depth > max_depth and max_depth != 0: # we do not want to search the whole file system except if max_depth = 0
return []
if depth == 0:
entry = os.path.abspath(entry)
if os.path.isfile(entry): # a file? scan it
if depth != 0: # trust the user: only check for valid format if recursing
mimetypes.add_type('video/x-matroska', '.mkv')
if mimetypes.guess_type(entry)[0] not in FORMATS:
return []
# check for .lg.ext and .ext
available_languages = set()
has_single = False
basepath = os.path.splitext(entry)[0]
for l in LANGUAGES:
for e in EXTENSIONS:
if os.path.exists(basepath + '.%s.%s' % (l, e)):
available_languages.add(l)
if os.path.exists(basepath + '.%s' % e):
has_single = True
return [(os.path.normpath(entry), available_languages, has_single)]
if os.path.isdir(entry): # a dir? recurse
result = []
for e in os.listdir(entry):
result.extend(scan(os.path.join(entry, e), depth + 1))
return result
return [] # anything else
-206
View File
@@ -1,206 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Subliminal - Subtitles, faster than your thoughts
# Copyright (c) 2008-2011 Patrick Dessalle <patrick@dessalle.be>
# Copyright (c) 2011 Antoine Bertin <diaoulael@gmail.com>
#
# This file is part of Subliminal.
#
# Subliminal is free software; you can redistribute it and/or modify it under
# the terms of the Lesser GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Subliminal 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
# Lesser GNU General Public License for more details.
#
# You should have received a copy of the Lesser GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import unittest
import logging
import os
logging.basicConfig(level=logging.DEBUG, format='%(name)-24s %(levelname)-8s %(message)s')
if not os.path.exists('/tmp/subliminal/cache'):
os.mkdir('/tmp/subliminal/cache')
config = {'multi': True, 'cache_dir': '/tmp/subliminal/cache', 'subtitlesource_key': '', 'force': False}
class Addic7edListTestCase1(unittest.TestCase):
def runTest(self):
from subliminal.plugins import Addic7ed
plugin = Addic7ed(config)
results = plugin.list(["The.Big.Bang.Theory.S03E13.HDTV.XviD-2HD.mkv"], ["en", "fr"])
print results
assert len(results) > 0
class Addic7edListTestCase2(unittest.TestCase):
def runTest(self):
from subliminal.plugins import Addic7ed
plugin = Addic7ed(config)
results = plugin.list(["Dexter.S05E02.720p.HDTV.x264-IMMERSE.mkv"], ["en", "fr"])
print results
assert len(results) > 0
class Addic7edDownloadTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import Addic7ed
plugin = Addic7ed(config)
results = plugin.download(plugin.list(["/tmp/The.Big.Bang.Theory.S03E13.HDTV.XviD-2HD.mkv"], ["en", "fr"])[0])
print results
assert len(results) > 0
class BierDopjeListTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import BierDopje
plugin = BierDopje(config)
results = plugin.list(["The.Big.Bang.Theory.S03E13.HDTV.XviD-2HD.mkv"], ["en", "fr"])
print results
assert len(results) > 0
class BierDopjeListExceptionTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import BierDopje
plugin = BierDopje(config)
results = plugin.list(["The.Office.US.S07E08.Viewing.Party.HDTV.XviD-FQM.[VTV].avi"], ["en", "fr"])
print results
assert len(results) > 0
class BierDopjeListTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import BierDopje
plugin = BierDopje(config)
results = plugin.list(["The.Big.Bang.Theory.S03E13.HDTV.XviD-2HD.mkv"], ["en", "fr"])
print results
assert len(results) > 0
class OpenSubtitlesQueryTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import OpenSubtitles
plugin = OpenSubtitles()
results = plugin.query('Night.Watch.2004.CD1.DVDRiP.XViD-FiCO.avi', moviehash="09a2c497663259cb", bytesize="733589504") # http://trac.opensubtitles.org/projects/opensubtitles/wiki/XMLRPC
print results
assert len(results) > 0
class OpenSubtitlesQueryNoHashTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import OpenSubtitles
plugin = OpenSubtitles()
results = plugin.query('Night.Watch.2004.CD1.DVDRiP.XViD-FiCO.avi', languages=['en', 'fr']) # http://trac.opensubtitles.org/projects/opensubtitles/wiki/XMLRPC
print results
assert len(results) > 0
class OpenSubtitlesListTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import OpenSubtitles
plugin = OpenSubtitles()
results = plugin.download(plugin.query('/tmp/Night.Watch.2004.CD1.DVDRiP.XViD-FiCO.avi', moviehash="09a2c497663259cb", bytesize="733589504")[0]) # http://trac.opensubtitles.org/projects/opensubtitles/wiki/XMLRPC
assert len(results) > 0
class SubtitulosListTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import Subtitulos
plugin = Subtitulos()
results = plugin.list(["The.Big.Bang.Theory.S03E13.HDTV.XviD-2HD.mkv"], ['en', 'fr'])
print results
assert len(results) > 0
class SubtitulosDownloadTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import Subtitulos
plugin = Subtitulos()
results = plugin.download(plugin.list(["/tmp/The.Big.Bang.Theory.S03E13.HDTV.XviD-2HD.mkv"], ['en', 'fr'])[0])
print results
assert len(results) > 0
class TheSubDBQueryTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import TheSubDB
plugin = TheSubDB()
results = plugin.query("test.mkv", "edc1981d6459c6111fe36205b4aff6c2")
print results
assert len(results) > 0
class TheSubDBDownloadTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import TheSubDB
plugin = TheSubDB()
results = plugin.download(plugin.query("/tmp/test.mkv", "edc1981d6459c6111fe36205b4aff6c2")[0])
print results
assert len(results) > 0
class SubsWikiListTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import SubsWiki
plugin = SubsWiki()
results = plugin.list(["The.Big.Bang.Theory.S03E13.HDTV.XviD-2HD.mkv"], ['en', 'es'])
print results
assert len(results) > 0
class SubsWikiDownloadTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import SubsWiki
plugin = SubsWiki()
results = plugin.download(plugin.list(["/tmp/The.Big.Bang.Theory.S03E13.HDTV.XviD-2HD.mkv"], ['en', 'es'])[0])
print results
assert len(results) > 0
'''
class PodnapisiQueryTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import Podnapisi
plugin = Podnapisi()
results = plugin.query('09a2c497663259cb', ["en", "fr"])
print results
assert len(results) > 5
class SubSceneListTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import SubScene
plugin = SubScene()
results = plugin.list(["Dexter.S04E01.HDTV.XviD-NoTV.avi"], ['en', 'fr'])
print results
assert len(results) > 0, "No result could be found for Dexter.S04E01.HDTV.XviD-NoTV.avi and no languages"
class SubSceneDownloadTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import SubScene
plugin = SubScene()
results = plugin.download(plugin.list(["Dexter.S04E01.HDTV.XviD-NoTV.avi"], ['en', 'fr'])[0])
print results
assert len(results) > 0, "No result could be found for Dexter.S04E01.HDTV.XviD-NoTV.avi and no languages"
class SubtitleSourceListTestCase(unittest.TestCase):
def runTest(self):
from subliminal.plugins import SubtitleSource
plugin = SubtitleSource()
results = plugin.list(["PrisM-Inception.2010"], ['en', 'fr'])
print results
assert len(results) > 0, "No result could be found for PrisM-Inception.2010"
'''
if __name__ == "__main__":
unittest.main()
+1 -1
View File
@@ -19,4 +19,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
__version__ = '0.3'
__version__ = '0.4'
+152
View File
@@ -0,0 +1,152 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Subliminal - Subtitles, faster than your thoughts
# Copyright (c) 2011 Antoine Bertin <diaoulael@gmail.com>
#
# This file is part of Subliminal.
#
# Subliminal is free software; you can redistribute it and/or modify it under
# the terms of the Lesser GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Subliminal 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
# Lesser GNU General Public License for more details.
#
# You should have received a copy of the Lesser GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import unittest
import logging
import os
logging.basicConfig(level=logging.DEBUG, format='%(name)-24s %(levelname)-8s %(message)s')
test_folder = u'/your/path/here/videos/'
test_file = u'/your/path/here/videos/the.big.bang.theory.s04e01.hdtv.xvid-fqm.avi'
cache_dir = u'/tmp/sublicache'
if not os.path.exists(cache_dir):
os.mkdir(cache_dir)
class Addic7edTestCase(unittest.TestCase):
def setUp(self):
from subliminal.plugins import Addic7ed
self.config = {'multi': True, 'cache_dir': cache_dir, 'files_mode': -1}
self.plugin = Addic7ed(self.config)
self.languages = set(['en', 'fr'])
def test_list(self):
list = self.plugin.list(test_file, self.languages)
assert list
def test_download(self):
subtitle = self.plugin.list(test_file, self.languages)[0]
if os.path.exists(subtitle.path):
os.remove(subtitle.path)
download = self.plugin.download(subtitle)
assert download
class BierDopjeTestCase(unittest.TestCase):
def setUp(self):
from subliminal.plugins import BierDopje
self.config = {'multi': True, 'cache_dir': cache_dir, 'files_mode': -1}
self.plugin = BierDopje(self.config)
self.languages = set(['en', 'fr'])
def test_list(self):
list = self.plugin.list(test_file, self.languages)
assert list
def test_download(self):
subtitle = self.plugin.list(test_file, self.languages)[0]
if os.path.exists(subtitle.path):
os.remove(subtitle.path)
download = self.plugin.download(subtitle)
assert download
class OpenSubtitlesTestCase(unittest.TestCase):
def setUp(self):
from subliminal.plugins import OpenSubtitles
self.config = {'multi': True, 'cache_dir': cache_dir, 'files_mode': -1}
self.plugin = OpenSubtitles(self.config)
self.languages = set(['en', 'fr'])
def test_list(self):
list = self.plugin.list(test_file, self.languages)
assert list
def test_download(self):
subtitle = self.plugin.list(test_file, self.languages)[0]
if os.path.exists(subtitle.path):
os.remove(subtitle.path)
download = self.plugin.download(subtitle)
assert download
class SubsWikiTestCase(unittest.TestCase):
def setUp(self):
from subliminal.plugins import SubsWiki
self.config = {'multi': True, 'cache_dir': cache_dir, 'files_mode': -1}
self.plugin = SubsWiki(self.config)
self.languages = set(['en', 'fr', 'es', 'pt'])
def test_list(self):
list = self.plugin.list(test_file, self.languages)
assert list
def test_download(self):
subtitle = self.plugin.list(test_file, self.languages)[0]
if os.path.exists(subtitle.path):
os.remove(subtitle.path)
download = self.plugin.download(subtitle)
assert download
class SubtitulosTestCase(unittest.TestCase):
def setUp(self):
from subliminal.plugins import Subtitulos
self.config = {'multi': True, 'cache_dir': cache_dir, 'files_mode': -1}
self.plugin = Subtitulos(self.config)
self.languages = set(['en', 'fr', 'es', 'pt'])
def test_list(self):
list = self.plugin.list(test_file, self.languages)
assert list
def test_download(self):
subtitle = self.plugin.list(test_file, self.languages)[0]
if os.path.exists(subtitle.path):
os.remove(subtitle.path)
download = self.plugin.download(subtitle)
assert download
class TheSubDBTestCase(unittest.TestCase):
def setUp(self):
from subliminal.plugins import TheSubDB
self.config = {'multi': True, 'cache_dir': cache_dir, 'files_mode': -1}
self.plugin = TheSubDB(self.config)
self.languages = set(['en', 'fr', 'es', 'pt'])
def test_list(self):
list = self.plugin.list(test_file, self.languages)
assert list
def test_download(self):
subtitle = self.plugin.list(test_file, self.languages)[0]
if os.path.exists(subtitle.path):
os.remove(subtitle.path)
download = self.plugin.download(subtitle)
assert download
if __name__ == "__main__":
unittest.main()
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Subliminal - Subtitles, faster than your thoughts
# Copyright (c) 2011 Antoine Bertin <diaoulael@gmail.com>
#
# This file is part of Subliminal.
#
# Subliminal is free software; you can redistribute it and/or modify it under
# the terms of the Lesser GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Subliminal 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
# Lesser GNU General Public License for more details.
#
# You should have received a copy of the Lesser GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import unittest
import logging
import os
import subliminal
logging.basicConfig(level=logging.DEBUG, format='%(name)-24s %(levelname)-8s %(message)s')
test_folder = u'/your/path/here/videos/'
test_file = u'/your/path/here/videos/the.big.bang.theory.s04e01.hdtv.xvid-fqm.avi'
cache_dir = u'/tmp/sublicache'
if not os.path.exists(cache_dir):
os.mkdir(cache_dir)
class FileTestCase(unittest.TestCase):
def setUp(self):
self.subli = subliminal.Subliminal(cache_dir=cache_dir, workers=4, multi=False, force=True, max_depth=3, files_mode=-1)
self.subli.languages = ['en', 'fr', 'es', 'pt']
self.subli.plugins = subliminal.PLUGINS
def test_list(self):
results = self.subli.listSubtitles(test_file)
self.assertTrue(len(results) > 0)
def test_download(self):
results = self.subli.downloadSubtitles(test_file)
self.assertTrue(len(results) > 0)
class ErrorTestCase(unittest.TestCase):
def setUp(self):
self.subli = subliminal.Subliminal(cache_dir=cache_dir, workers=4, multi=False, force=True, max_depth=3, files_mode=-1)
def test_language(self):
with self.assertRaises(subliminal.classes.LanguageError):
self.subli.languages = ['en', 'fr', 'zz', 'pt']
def test_plugin(self):
with self.assertRaises(subliminal.classes.PluginError):
self.subli.plugins = ['WrongPlugin']
class PriorityQueueTestCase(unittest.TestCase):
def setUp(self):
self.subli = subliminal.Subliminal(cache_dir=cache_dir, workers=4, multi=False, force=True, max_depth=3, files_mode=-1)
self.subli.languages = ['en', 'fr', 'es', 'pt']
self.subli.plugins = subliminal.PLUGINS
def test_bad_state_error(self):
with self.assertRaises(subliminal.classes.BadStateError):
self.subli.startWorkers()
results = self.subli.listSubtitles(test_folder)
self.subli.stopWorkers()
def test_manual_list(self):
self.subli.taskQueue.put((5, subliminal.classes.ListTask(test_file, set(self.subli.languages), 'OpenSubtitles', self.subli.getConfigDict())))
self.subli.startWorkers()
# parallel stuff...
self.subli.stopWorkers()
result = self.subli.listResultQueue.get()
self.assertTrue(len(result) > 0)
if __name__ == "__main__":
unittest.main()