Example #1
0
    def test_converter_opensubtitles(self):
        self.assertEqual(Language('fra').opensubtitles, Language('fra').alpha3b)
        self.assertEqual(Language('por', 'BR').opensubtitles, 'pob')
        self.assertEqual(Language.fromopensubtitles('fre'), Language('fra'))
        self.assertEqual(Language.fromopensubtitles('pob'), Language('por', 'BR'))
        self.assertEqual(Language.fromopensubtitles('pb'), Language('por', 'BR'))
        # Montenegrin is not recognized as an ISO language (yet?) but for now it is
        # unofficially accepted as Serbian from Montenegro
        self.assertEqual(Language.fromopensubtitles('mne'), Language('srp', 'ME'))
        self.assertEqual(Language.fromcode('pob', 'opensubtitles'), Language('por', 'BR'))
        with self.assertRaises(LanguageReverseError):
            Language.fromopensubtitles('zzz')
        with self.assertRaises(LanguageConvertError):
            Language('aaa').opensubtitles
        self.assertEqual(len(get_language_converter('opensubtitles').codes), 606)

        # test with all the languages from the opensubtitles api
        # downloaded from: http://www.opensubtitles.org/addons/export_languages.php
        f = resource_stream('babelfish', 'data/opensubtitles_languages.txt')
        f.readline()
        for l in f:
            idlang, alpha2, _, upload_enabled, web_enabled = l.decode('utf-8').strip().split('\t')
            if not int(upload_enabled) and not int(web_enabled):
                # do not test languages that are too esoteric / not widely available
                continue
            self.assertEqual(Language.fromopensubtitles(idlang).opensubtitles, idlang)
            if alpha2:
                self.assertEqual(Language.fromopensubtitles(idlang), Language.fromopensubtitles(alpha2))
        f.close()
Example #2
0
    def test_check_for_better_checks_languages_with_no_current_subs(self):
        superliminal.env.settings.languages.append(Language.fromietf('pt-BR'))
        okensub = {
            'id': 'okensub',
            'language': Language.fromietf('en'),
            'series': "Series Title",
            'season': 2,
            'episode': 3,
            'title': "The Episode",
            'release_group': "OtherRG",
            'content': SUBTITLE_CONTENT
        }
        self.set_subtitles([okensub])
        self.add_video(name="Series.Title.S02E03.720p.WEB-DL.H264-TvRG.mkv")
        yield self.wait_until_processed()

        betterensub = self.transform_sub(okensub, 'betterensub',
            release_group="TvRG", content=SUBTITLE_CONTENT_2)
        okbrsub = self.transform_sub(okensub, 'okbrsub',
            language=Language.fromietf('pt-BR'), content=SUBTITLE_CONTENT_3)
        self.set_subtitles([okensub, betterensub, okbrsub])

        SuperliminalCore.check_for_better()
        yield self.wait_until_processed()
        self.assert_subtitle_contents_matches(expected_content=SUBTITLE_CONTENT_2, suffix='.en.srt')
        self.assert_subtitle_contents_matches(expected_content=SUBTITLE_CONTENT_3, suffix='.pt-BR.srt')
Example #3
0
    def test_register_converter(self):
        class TestConverter(LanguageReverseConverter):
            def __init__(self):
                self.to_test = {'fra': 'test1', 'eng': 'test2'}
                self.from_test = {'test1': 'fra', 'test2': 'eng'}

            def convert(self, alpha3, country=None, script=None):
                if alpha3 not in self.to_test:
                    raise LanguageConvertError(alpha3, country, script)
                return self.to_test[alpha3]

            def reverse(self, test):
                if test not in self.from_test:
                    raise LanguageReverseError(test)
                return (self.from_test[test], None)
        language = Language('fra')
        self.assertFalse(hasattr(language, 'test'))
        register_language_converter('test', TestConverter)
        self.assertTrue(hasattr(language, 'test'))
        self.assertIn('test', LANGUAGE_CONVERTERS)
        self.assertEqual(Language('fra').test, 'test1')
        self.assertEqual(Language.fromtest('test2').alpha3, 'eng')
        unregister_language_converter('test')
        self.assertNotIn('test', LANGUAGE_CONVERTERS)
        with self.assertRaises(KeyError):
            Language.fromtest('test1')
        with self.assertRaises(AttributeError):
            Language('fra').test
        clear_language_converters()
        load_language_converters()
Example #4
0
    def test_check_for_better_checks_all_recent_videos(self):
        oktvsub = {
            'id': 'oktvsub',
            'language': Language.fromietf('en'),
            'series': "Series Title",
            'season': 2,
            'episode': 3,
            'title': "The Episode",
            'release_group': "OtherRG",
            'content': SUBTITLE_CONTENT
        }
        yield self.add_video(name="Series.Title.S02E03.720p.WEB-DL.H264-TvRG.mkv", subtitle=oktvsub)
        movie_filename = self.create_video_file()
        okmoviesub = {
            'id': 'okmoviesub',
            'language': Language.fromietf('en'),
            'title': "Movie Title",
            'year': 2016,
            'release_group': "OtherRG",
            'content': SUBTITLE_CONTENT
        }
        yield self.add_video(path=movie_filename,
            name="Movie.Title.2016.720p.WEB-DL.H264-MovieRG.mkv", subtitle=okmoviesub)

        bettertvsub = self.transform_sub(oktvsub, 'bettertvsub',
            release_group="TvRG", content=SUBTITLE_CONTENT_2)
        bettermoviesub = self.transform_sub(okmoviesub, 'bettermoviesub',
            release_group="MovieRG", content=SUBTITLE_CONTENT_3)
        self.set_subtitles([oktvsub, bettertvsub, okmoviesub, bettermoviesub])

        SuperliminalCore.check_for_better()
        yield self.wait_until_processed()
        self.assert_subtitle_contents_matches(expected_content=SUBTITLE_CONTENT_2)
        self.assert_subtitle_contents_matches(video_filename=movie_filename,
            expected_content=SUBTITLE_CONTENT_3)
Example #5
0
 def test_downloads_for_all_configured_languages(self):
     superliminal.env.settings.languages.append(Language.fromietf('pt-BR'))
     self.set_subtitles([{
         'id': 'english_sub',
         'language': Language.fromietf('en'),
         'series': "Series Title",
         'season': 2,
         'episode': 3,
         'title': "The Episode",
         'release_group': "TvRG",
         'resolution': '720p',
         'content': SUBTITLE_CONTENT
     },
     {
         'id': 'brazilian_sub',
         'language': Language.fromietf('pt-BR'),
         'series': "Series Title",
         'season': 2,
         'episode': 3,
         'title': "The Episode",
         'release_group': "TvRG",
         'resolution': '720p',
         'content': SUBTITLE_CONTENT_2
     }])
     request = self.get_add_request(name="Series.Title.S02E03.720p.WEB-DL.H264-TvRG.mkv")
     response = yield self.http_client.fetch(request)
     self.assertEqual(200, response.code)
     yield self.wait_until_processed()
     self.assert_subtitle_contents_matches(expected_content=SUBTITLE_CONTENT, suffix='.en.srt')
     self.assert_subtitle_contents_matches(expected_content=SUBTITLE_CONTENT_2, suffix='.pt-BR.srt')
Example #6
0
 def test_downloads_new_sub_if_new_video_added_for_existing_path(self):
     self.set_subtitles([{
         'id': 'new_sub',
         'language': Language.fromietf('en'),
         'series': "Series Title",
         'season': 2,
         'episode': 3,
         'title': "The Episode",
         'release_group': "TvRG",
         'resolution': '720p',
         'content': SUBTITLE_CONTENT
     },
     {
         'id': 'old_sub',
         'language': Language.fromietf('en'),
         'series': "Series Title",
         'season': 2,
         'episode': 3,
         'title': "The Episode",
         'release_group': "OtherRG",
         'resolution': '720p',
         'content': SUBTITLE_CONTENT_2
     }])
     request = self.get_add_request(name="Series.Title.S02E03.720p.WEB-DL.H264-OtherRG.mkv")
     response = yield self.http_client.fetch(request)
     self.assertEqual(200, response.code)
     yield self.wait_until_processed()
     request = self.get_add_request(name="Series.Title.S02E03.720p.WEB-DL.H264-TvRG.mkv")
     response = yield self.http_client.fetch(request)
     self.assertEqual(200, response.code)
     yield self.wait_until_processed()
     self.assert_subtitle_contents_matches()
Example #7
0
 def test_converter_alpha3t(self):
     self.assertEqual(Language('fra').alpha3t, 'fra')
     self.assertEqual(Language.fromalpha3t('fra'), Language('fra'))
     self.assertEqual(Language.fromcode('fra', 'alpha3t'), Language('fra'))
     self.assertRaises(LanguageReverseError, lambda: Language.fromalpha3t('zzz'))
     self.assertRaises(LanguageConvertError, lambda: Language('aaa').alpha3t)
     self.assertEqual(len(language_converters['alpha3t'].codes), 418)
Example #8
0
 def test_converter_alpha2(self):
     self.assertEqual(Language('eng').alpha2, 'en')
     self.assertEqual(Language.fromalpha2('en'), Language('eng'))
     self.assertEqual(Language.fromcode('en', 'alpha2'), Language('eng'))
     self.assertRaises(LanguageReverseError, lambda: Language.fromalpha2('zz'))
     self.assertRaises(LanguageConvertError, lambda: Language('aaa').alpha2)
     self.assertEqual(len(language_converters['alpha2'].codes), 184)
Example #9
0
def subtitlesLanguages(video_path):
    """Return a list detected subtitles for the given video file"""
    resultList = []
    embedded_subtitle_languages = set()

    # Serch for embedded subtitles
    if not sickbeard.EMBEDDED_SUBTITLES_ALL:
        if video_path.endswith('mkv'):
            try:
                with open(video_path.encode(sickbeard.SYS_ENCODING), 'rb') as f:
                    mkv = MKV(f)
                if mkv.subtitle_tracks:
                    for st in mkv.subtitle_tracks:
                        if st.language:
                            try:
                                embedded_subtitle_languages.add(Language.fromalpha3b(st.language))
                            except BabelfishError:
                                logger.log('Embedded subtitle track is not a valid language', logger.DEBUG)
                                embedded_subtitle_languages.add(Language('und'))
                        elif st.name:
                            try:
                                embedded_subtitle_languages.add(Language.fromname(st.name))
                            except BabelfishError:
                                logger.log('Embedded subtitle track is not a valid language', logger.DEBUG)
                                embedded_subtitle_languages.add(Language('und'))
                        else:
                            embedded_subtitle_languages.add(Language('und'))
                else:
                    logger.log('MKV has no subtitle track', logger.DEBUG)
            except MalformedMKVError:
                logger.log('MKV seems to be malformed, ignoring embedded subtitles', logger.WARNING)

    # Search subtitles in the absolute path
    if sickbeard.SUBTITLES_DIR and ek(os.path.exists, sickbeard.SUBTITLES_DIR):
        video_path = ek(os.path.join, sickbeard.SUBTITLES_DIR, ek(os.path.basename, video_path))
    # Search subtitles in the relative path
    elif sickbeard.SUBTITLES_DIR:
        video_path = ek(os.path.join, ek(os.path.dirname, video_path), sickbeard.SUBTITLES_DIR, ek(os.path.basename, video_path))

    external_subtitle_languages = subliminal.video.scan_subtitle_languages(video_path)
    subtitle_languages = external_subtitle_languages.union(embedded_subtitle_languages)

    if (len(subtitle_languages) is 1 and len(wantedLanguages()) is 1) and Language('und') in subtitle_languages:
        subtitle_languages.remove(Language('und'))
        subtitle_languages.add(fromietf(wantedLanguages()[0]))

    for language in subtitle_languages:
        if hasattr(language, 'opensubtitles') and language.opensubtitles:
            resultList.append(language.opensubtitles)
        elif hasattr(language, 'alpha3') and language.alpha3:
            resultList.append(language.alpha3)
        elif hasattr(language, 'alpha2') and language.alpha2:
            resultList.append(language.alpha2)

    defaultLang = wantedLanguages()

    if ('pob' in defaultLang or 'pb' in defaultLang) and ('pt' not in defaultLang and 'por' not in defaultLang):
            resultList = [x if not x in ['por', 'pt'] else u'pob' for x in resultList]

    return sorted(resultList)
Example #10
0
    def test_register_converter(self):
        class TestConverter(LanguageReverseConverter):
            def __init__(self):
                self.to_test = {'fra': 'test1', 'eng': 'test2'}
                self.from_test = {'test1': 'fra', 'test2': 'eng'}

            def convert(self, alpha3, country=None, script=None):
                if alpha3 not in self.to_test:
                    raise LanguageConvertError(alpha3, country, script)
                return self.to_test[alpha3]

            def reverse(self, test):
                if test not in self.from_test:
                    raise LanguageReverseError(test)
                return (self.from_test[test], None)
        language = Language('fra')
        self.assertFalse(hasattr(language, 'test'))
        language_converters['test'] = TestConverter()
        self.assertTrue(hasattr(language, 'test'))
        self.assertIn('test', language_converters)
        self.assertEqual(Language('fra').test, 'test1')
        self.assertEqual(Language.fromtest('test2').alpha3, 'eng')
        del language_converters['test']
        self.assertNotIn('test', language_converters)
        self.assertRaises(KeyError, lambda: Language.fromtest('test1'))
        self.assertRaises(AttributeError, lambda: Language('fra').test)
Example #11
0
def check_missing_subtitle_languages(dirname, filename):
    log.debug("Checking for missing subtitle")
    missing_subtitles = []
    # Check embedded subtitles
    embedded_subtitles = []
    if autosubliminal.SCANEMBEDDEDSUBS:
        embedded_subtitles = _get_embedded_subtitles(dirname, filename)
    # Check default language
    if autosubliminal.DEFAULTLANGUAGE:
        default_language = Language.fromietf(autosubliminal.DEFAULTLANGUAGE)
        # Check with or without alpha2 code suffix depending on configuration
        if autosubliminal.DEFAULTLANGUAGESUFFIX:
            srtfile = os.path.splitext(filename)[0] + u"." + autosubliminal.DEFAULTLANGUAGE + u".srt"
        else:
            srtfile = os.path.splitext(filename)[0] + u".srt"
        if not os.path.exists(os.path.join(dirname, srtfile)) and default_language not in embedded_subtitles:
            missing_subtitles.append(autosubliminal.DEFAULTLANGUAGE)
    # Check additional languages
    if autosubliminal.ADDITIONALLANGUAGES:
        # Always check with alpha2 code suffix for additional languages
        for language in autosubliminal.ADDITIONALLANGUAGES:
            additional_language = Language.fromietf(language)
            srtfile = os.path.splitext(filename)[0] + u"." + language + u".srt"
            if not os.path.exists(os.path.join(dirname, srtfile)) and additional_language not in embedded_subtitles:
                missing_subtitles.append(language)
    return missing_subtitles
Example #12
0
    def test_added_subs_are_returned_by_lang(self):
        path = "/data/Series/Season 1/01 Title.mkv"
        video = Video.fromname("Series.S01E02.Title.720p.WEB-DL.DD5.1.H264-ReleaseGroup.mkv")
        self.datastore.add_video(path, video)

        provider1 = "davessubs"
        sub_id1 = "ABC123"
        lang1 = Language.fromietf("en")
        score1 = 123
        provider2 = "stevesubs"
        sub_id2 = "steve123"
        lang2 = lang1
        score2 = 120
        provider3 = "pablosubs"
        sub_id3 = "umdoistres"
        lang3 = Language.fromietf("pt-BR")
        score3 = 150
        self.datastore.add_download(path, provider1, sub_id1, lang1, score1)
        self.datastore.add_download(path, provider2, sub_id2, lang2, score2)
        self.datastore.add_download(path, provider3, sub_id3, lang3, score3)

        downloads = self.datastore.get_downloads_for_video(path)

        self.assertEqual(
            downloads,
            {
                lang1: [
                    {"provider": provider1, "sub_id": sub_id1, "lang": lang1, "score": score1},
                    {"provider": provider2, "sub_id": sub_id2, "lang": lang2, "score": score2},
                ],
                lang3: [{"provider": provider3, "sub_id": sub_id3, "lang": lang3, "score": score3}],
            },
        )
Example #13
0
def getEmbeddedLanguages(video_path):
    embedded_subtitle_languages = set()
    try:
        with ek(io.open, video_path, 'rb') as f:
            mkv = MKV(f)
            if mkv.subtitle_tracks:
                for st in mkv.subtitle_tracks:
                    if st.language:
                        try:
                            embedded_subtitle_languages.add(Language.fromalpha3b(st.language))
                        except BabelfishError:
                            logging.debug('Embedded subtitle track is not a valid language')
                            embedded_subtitle_languages.add(Language('und'))
                    elif st.name:
                        try:
                            embedded_subtitle_languages.add(Language.fromname(st.name))
                        except BabelfishError:
                            logging.debug('Embedded subtitle track is not a valid language')
                            embedded_subtitle_languages.add(Language('und'))
                    else:
                        embedded_subtitle_languages.add(Language('und'))
            else:
                logging.debug('MKV has no subtitle track')
    except MalformedMKVError:
        logging.info('MKV seems to be malformed ( %s ), ignoring embedded subtitles' % video_path)

    return embedded_subtitle_languages
def getAlpha3TCode(code):  # We need to make sure that language codes are alpha3T
    """
        :param    code: Alpha2, Alpha3, or Alpha3b code.
        :type     code: C{str}

        :return: Alpha3t language code (ISO 639-2/T) as C{str}
    """
    lang = 'und'
    code = code.strip().lower()

    if len(code) == 3:
        try:
            lang = Language(code).alpha3t
        except:
            try:
                lang = Language.fromalpha3b(code).alpha3t
            except:
                try:
                    lang = Language.fromalpha3t(code).alpha3t
                except:
                    pass

    elif len(code) == 2:
        lang = Language.fromalpha2(code).alpha3t

    return lang
Example #15
0
 def test_converter_name(self):
     self.assertEqual(Language('eng').name, 'English')
     self.assertEqual(Language.fromname('English'), Language('eng'))
     self.assertEqual(Language.fromcode('English', 'name'), Language('eng'))
     with self.assertRaises(LanguageReverseError):
         Language.fromname('Zzzzzzzzz')
     self.assertEqual(len(get_language_converter('name').codes), 7874)
Example #16
0
def getEmbeddedLanguages(video_path):
    embedded_subtitle_languages = set()
    try:
        with open(video_path, 'rb') as f:
            mkv = MKV(f)
            if mkv.subtitle_tracks:
                for st in mkv.subtitle_tracks:
                    if st.language:
                        try:
                            embedded_subtitle_languages.add(Language.fromalpha3b(st.language))
                        except BabelfishError:
                            logger.log('Embedded subtitle track is not a valid language', logger.DEBUG)
                            embedded_subtitle_languages.add(Language('und'))
                    elif st.name:
                        try:
                            embedded_subtitle_languages.add(Language.fromname(st.name))
                        except BabelfishError:
                            logger.log('Embedded subtitle track is not a valid language', logger.DEBUG)
                            embedded_subtitle_languages.add(Language('und'))
                    else:
                        embedded_subtitle_languages.add(Language('und'))
            else:
                logger.log('MKV has no subtitle track', logger.DEBUG)
    except MalformedMKVError:
        logger.log('MKV seems to be malformed, ignoring embedded subtitles', logger.WARNING)

    return embedded_subtitle_languages
Example #17
0
def _get_embedded_subtitles(dirname, filename):
    """
    Based on subliminal.video.scan_video(...) but only keep the check for embedded subtitles
    """
    log.debug("Checking for embedded subtitle")
    embedded_subtitle_languages = set()
    try:
        path = os.path.join(dirname, filename)
        if filename.endswith('.mkv'):
            with open(path, 'rb') as f:
                mkv = MKV(f)

            # subtitle tracks
            if mkv.subtitle_tracks:
                for st in mkv.subtitle_tracks:
                    if st.language:
                        try:
                            embedded_subtitle_languages.add(Language.fromalpha3b(st.language))
                        except BabelfishError:
                            log.error('Embedded subtitle track language %r is not a valid language', st.language)
                            embedded_subtitle_languages.add(Language('und'))
                    elif st.name:
                        try:
                            embedded_subtitle_languages.add(Language.fromname(st.name))
                        except BabelfishError:
                            log.debug('Embedded subtitle track name %r is not a valid language', st.name)
                            embedded_subtitle_languages.add(Language('und'))
                    else:
                        embedded_subtitle_languages.add(Language('und'))
                log.debug('Found embedded subtitles %r with enzyme', embedded_subtitle_languages)
            else:
                log.debug('MKV has no subtitle track')
    except:
        log.exception('Parsing video metadata with enzyme failed')
    return embedded_subtitle_languages
Example #18
0
 def test_converter_alpha3t(self):
     self.assertEqual(Language('fra').alpha3t, 'fra')
     self.assertEqual(Language.fromalpha3t('fra'), Language('fra'))
     self.assertEqual(Language.fromcode('fra', 'alpha3t'), Language('fra'))
     with self.assertRaises(LanguageReverseError):
         Language.fromalpha3t('zzz')
     with self.assertRaises(LanguageConvertError):
         Language('aaa').alpha3t
     self.assertEqual(len(get_language_converter('alpha3t').codes), 418)
Example #19
0
    def query(self, searchword=None):
        params = {"searchword": searchword}
        logger.info("Searching subtitles %r", params)

        r = self.session.get("http://sub.makedie.me/sub/?", params=params, timeout=10)
        r.raise_for_status()

        # loop over
        subtitles = []
        try:
            # BeautifulSoup gives you Unicode, damn it
            soup = BeautifulSoup(r.content, "html5lib")
            results = soup.find_all("div", attrs={"class": "subitem"})
            for it in results:
                releases = it.find("a", attrs={"class": "introtitle"})["title"].strip().split("/")
                sid = re.search("/xml/sub/\d+/(\d+).xml", it.find("a", attrs={"class": "introtitle"})["href"]).group(1)

                download_button = it.find("a", attrs={"id": "downsubbtn"})["onclick"].strip()
                if re.search("location.href\s*=\s*", download_button):
                    link = "http://sub.makedie.me/" + re.search(
                        "location.href\s*=\s*['\"](.*?)['\"]", download_button
                    ).group(1)
                else:
                    logger.debug("No download link found")
                    continue

                lang = ""
                for li in it.find_all("li"):
                    if re.search("格式:", li.text.strip()):
                        subtype = re.search("格式:\s*([^\(]+)(?:\(\?\))*", li.text.strip()).group(1)

                    if re.search("语言:", li.text.strip()):
                        lang = re.search("语言:\s*([^\(]+)(?:\(\?\))*", li.text.strip()).group(1)

                    if re.search("下载次数:", li.text.strip()):
                        downloads = re.search("下载次数:\s*([^\(]+)(?:\(\?\))*", li.text.strip()).group(1)
                        downloads = re.search("(\d+).*", downloads).group(1)

                    if re.search("日期:", li.text.strip()):
                        upload_date = re.search("日期:\s*([^\(]+)(?:\(\?\))*", li.text.strip()).group(1)

                if "简" in lang or "繁" in lang or "双语" in lang:
                    subtitle = MakeDieSubtitle(
                        Language.fromalpha2("zh"), releases, sid, link, subtype, downloads, upload_date
                    )
                else:
                    subtitle = MakeDieSubtitle(
                        Language.fromalpha2("en"), releases, sid, link, subtype, downloads, upload_date
                    )

                logger.debug("Found subtitle %r", subtitle)
                subtitles.append(subtitle)

            return subtitles
        except:
            logger.debug("No subtitle found")
            return []
Example #20
0
 def test_converter_alpha2(self):
     self.assertEqual(Language('eng').alpha2, 'en')
     self.assertEqual(Language.fromalpha2('en'), Language('eng'))
     self.assertEqual(Language.fromcode('en', 'alpha2'), Language('eng'))
     with self.assertRaises(LanguageReverseError):
         Language.fromalpha2('zz')
     with self.assertRaises(LanguageConvertError):
         Language('aaa').alpha2
     self.assertEqual(len(get_language_converter('alpha2').codes), 184)
Example #21
0
def scan_subtitle_languages(path):
    language_extensions = tuple('.' + c for c in language_converters['opensubtitles'].codes)
    dirpath, filename = os.path.split(path)
    subtitles = set()
    for p in os.listdir(dirpath):
        if not isinstance(p, bytes) and p.startswith(os.path.splitext(filename)[0]) and p.endswith(subliminal.video.SUBTITLE_EXTENSIONS):
            if os.path.splitext(p)[0].endswith(language_extensions) and len(os.path.splitext(p)[0].rsplit('.', 1)[1]) is 2:
                subtitles.add(Language.fromopensubtitles(os.path.splitext(p)[0][-2:]))
            elif os.path.splitext(p)[0].endswith(language_extensions) and len(os.path.splitext(p)[0].rsplit('.', 1)[1]) is 3:
                subtitles.add(Language.fromopensubtitles(os.path.splitext(p)[0][-3:]))
            else:
                subtitles.add(Language('und'))

    return subtitles
Example #22
0
    def fix_subtitles_codes(self):

        sqlResults = self.connection.select(
            "SELECT subtitles, episode_id FROM tv_episodes WHERE subtitles != '' AND subtitles_lastsearch < ?;",
                [datetime.datetime(2015, 7, 15, 17, 20, 44, 326380).strftime(dateTimeFormat)])

        validLanguages = [Language.fromopensubtitles(language).opensubtitles for language in language_converters['opensubtitles'].codes if len(language) == 3]

        if not sqlResults:
            return

        for sqlResult in sqlResults:
            langs = []

            logger.log("Checking subtitle codes for episode_id: %s, codes: %s" %
                (sqlResult['episode_id'], sqlResult['subtitles']), logger.DEBUG)

            for subcode in sqlResult['subtitles'].split(','):
                if not len(subcode) is 3 or not subcode in validLanguages:
                    logger.log("Fixing subtitle codes for episode_id: %s, invalid code: %s" %
                        (sqlResult['episode_id'], subcode), logger.DEBUG)
                    continue

                langs.append(subcode)

            self.connection.action("UPDATE tv_episodes SET subtitles = ?, subtitles_lastsearch = ? WHERE episode_id = ?;",
                [','.join(langs), datetime.datetime.now().strftime(dateTimeFormat), sqlResult['episode_id']])
Example #23
0
def find_possible_languages(string):
    """Find possible languages in the string

    :return: list of tuple (property, Language, lang_word, word)
    """
    words = find_words(string)

    valid_words = []
    for word in words:
        lang_word = word.lower()
        key = 'language'
        for prefix in subtitle_prefixes:
            if lang_word.startswith(prefix):
                lang_word = lang_word[len(prefix):]
                key = 'subtitleLanguage'
        for suffix in subtitle_suffixes:
            if lang_word.endswith(suffix):
                lang_word = lang_word[:len(suffix)]
                key = 'subtitleLanguage'
        for prefix in lang_prefixes:
            if lang_word.startswith(prefix):
                lang_word = lang_word[len(prefix):]
        if not lang_word in LNG_COMMON_WORDS:
            try:
                lang = Language.fromguessit(lang_word)
                # Keep language with alpha2 equivalent. Others are probably
                # uncommon languages.
                if lang == 'mul' or hasattr(lang, 'alpha2'):
                    valid_words.append((key, lang, lang_word, word))
            except babelfish.Error:
                pass
    return valid_words
Example #24
0
    def query(self, series, season, episode, year=None):
        # search the show id
        show_id = self.search_show_id(series, year)
        if show_id is None:
            logger.error("No show id found for %r (%r)", series, {"year": year})
            return []

        # get the episode ids
        episode_ids = self.get_episode_ids(show_id, season)
        if episode not in episode_ids:
            logger.error("Episode %d not found", episode)
            return []

        # get the episode page
        logger.info("Getting the page for episode %d", episode_ids[episode])
        r = self.session.get(self.server_url + "episode-%d.html" % episode_ids[episode], timeout=10)
        soup = ParserBeautifulSoup(r.content, ["lxml", "html.parser"])

        # loop over subtitles rows
        subtitles = []
        for row in soup.select(".subtitlen"):
            # read the item
            language = Language.fromtvsubtitles(row.h5.img["src"][13:-4])
            subtitle_id = int(row.parent["href"][10:-5])
            page_link = self.server_url + "subtitle-%d.html" % subtitle_id
            rip = row.find("p", title="rip").text.strip() or None
            release = row.find("p", title="release").text.strip() or None

            subtitle = TVsubtitlesSubtitle(
                language, page_link, subtitle_id, series, season, episode, year, rip, release
            )
            logger.debug("Found subtitle %s", subtitle)
            subtitles.append(subtitle)

        return subtitles
Example #25
0
def _detect_subtitle_language(srt_path):
    log.debug('Detecting subtitle language')

    # Load srt file (try first iso-8859-1 with fallback to utf-8)
    try:
        subtitle = pysrt.open(path=srt_path, encoding='iso-8859-1')
    except Exception:
        try:
            subtitle = pysrt.open(path=srt_path, encoding='utf-8')
        except Exception:
            # If we can't read it, we can't detect, so return
            return None

    # Read first 5 subtitle lines to determine the language
    if len(subtitle) >= 5:
        text = ''
        for sub in subtitle[0:5]:
            text += sub.text

        # Detect the language with highest probability and return it if it's more than the required minimum probability
        detected_languages = langdetect.detect_langs(text)
        log.debug('Detected subtitle language(s): %s', detected_languages)
        if len(detected_languages) > 0:
            # Get first detected language (list is sorted according to probability, highest first)
            detected_language = detected_languages[0]
            language_probability = detected_language.prob
            if language_probability >= autosubliminal.DETECTEDLANGUAGEPROBABILITY:
                log.debug('Probability of detected subtitle language accepted: %s', detected_language)
                return Language.fromietf(detected_language.lang)
            else:
                log.debug('Probability of detected subtitle language too low: %s', detected_language)

    return None
Example #26
0
    def query(self, series, season, episode, year=None):
        # search the show id
        show_id = self.search_show_id(series, year)
        if show_id is None:
            logger.error('No show id found for %r (%r)', series, {'year': year})
            return []

        # get the episode ids
        episode_ids = self.get_episode_ids(show_id, season)
        if episode not in episode_ids:
            logger.error('Episode %d not found', episode)
            return []

        # get the episode page
        logger.info('Getting the page for episode %d', episode_ids[episode])
        r = self.session.get(self.server_url + 'episode-%d.html' % episode_ids[episode], timeout=10)
        soup = ParserBeautifulSoup(r.content, ['lxml', 'html.parser'])

        # loop over subtitles rows
        subtitles = []
        for row in soup.select('.subtitlen'):
            # read the item
            language = Language.fromtvsubtitles(row.h5.img['src'][13:-4])
            subtitle_id = int(row.parent['href'][10:-5])
            page_link = self.server_url + 'subtitle-%d.html' % subtitle_id
            rip = row.find('p', title='rip').text.strip() or None
            release = row.find('p', title='release').text.strip() or None

            subtitle = TVsubtitlesSubtitle(language, page_link, subtitle_id, series, season, episode, year, rip,
                                           release)
            logger.debug('Found subtitle %s', subtitle)
            subtitles.append(subtitle)

        return subtitles
    def from_english(self, language):
        if language in EXCEPTIONS:
            return Language.fromietf(EXCEPTIONS[language])

        language_match = re.search('(\w[\w\s]*\w)', language)
        if not language_match:
            return

        language_english = language_match.group(1)
        language_alpha3 = xbmc.convertLanguage(language_english, format=xbmc.ISO_639_2)
        if not language_alpha3:
            return

        result = Language.fromcode(language_alpha3, 'alpha3b')
        result.country = self.get_country(language)
        return result
Example #28
0
def search_external_subtitles(path):
    """Search for external subtitles from a video `path` and their associated language.

    :param str path: path to the video.
    :return: found subtitles with their languages.
    :rtype: dict

    """
    dirpath, filename = os.path.split(path)
    dirpath = dirpath or '.'
    fileroot, fileext = os.path.splitext(filename)
    subtitles = {}
    for p in os.listdir(dirpath):
        # keep only valid subtitle filenames
        if not p.startswith(fileroot) or not p.endswith(SUBTITLE_EXTENSIONS):
            continue

        # extract the potential language code
        language_code = p[len(fileroot):-len(os.path.splitext(p)[1])].replace(fileext, '').replace('_', '-')[1:]

        # default language is undefined
        language = Language('und')

        # attempt to parse
        if language_code:
            try:
                language = Language.fromietf(language_code)
            except ValueError:
                logger.error('Cannot parse language code %r', language_code)

        subtitles[p] = language

    logger.debug('Found subtitles %r', subtitles)

    return subtitles
Example #29
0
    def reverse(self, name):
        with_country = (GuessitConverter._with_country_regexp.match(name) or
                        GuessitConverter._with_country_regexp2.match(name))

        name  = u(name.lower())
        if with_country:
            lang = Language.fromguessit(with_country.group(1).strip())
            lang.country = babelfish.Country.fromguessit(with_country.group(2).strip())
            return (lang.alpha3, lang.country.alpha2 if lang.country else None, lang.script or None)

        # exceptions come first, as they need to override a potential match
        # with any of the other guessers
        try:
            return self.guessit_exceptions[name]
        except KeyError:
            pass

        for conv in [babelfish.Language,
                     babelfish.Language.fromalpha3b,
                     babelfish.Language.fromalpha2,
                     babelfish.Language.fromname,
                     babelfish.Language.fromopensubtitles]:
            try:
                c = conv(name)
                return c.alpha3, c.country, c.script
            except (ValueError, babelfish.LanguageReverseError):
                pass

        raise babelfish.LanguageReverseError(name)
Example #30
0
    def query(self, languages, hash=None, size=None, imdb_id=None, query=None, season=None, episode=None, tag=None):
        # fill the search criteria
        criteria = []
        if hash and size:
            criteria.append({'moviehash': hash, 'moviebytesize': str(size)})
        if imdb_id:
            if season and episode:
                criteria.append({'imdbid': imdb_id[2:], 'season': season, 'episode': episode})
            else:
                criteria.append({'imdbid': imdb_id[2:]})
        if tag:
            criteria.append({'tag': tag})
        if query and season and episode:
            criteria.append({'query': query.replace('\'', ''), 'season': season, 'episode': episode})
        elif query:
            criteria.append({'query': query.replace('\'', '')})
        if not criteria:
            raise ValueError('Not enough information')

        # add the language
        for criterion in criteria:
            criterion['sublanguageid'] = ','.join(sorted(l.opensubtitles for l in languages))

        # query the server
        logger.info('Searching subtitles %r', criteria)
        response = checked(self.server.SearchSubtitles(self.token, criteria))
        subtitles = []

        # exit if no data
        if not response['data']:
            logger.debug('No subtitles found')
            return subtitles

        # loop over subtitle items
        for subtitle_item in response['data']:
            # read the item
            language = Language.fromopensubtitles(subtitle_item['SubLanguageID'])
            hearing_impaired = bool(int(subtitle_item['SubHearingImpaired']))
            page_link = subtitle_item['SubtitlesLink']
            subtitle_id = int(subtitle_item['IDSubtitleFile'])
            matched_by = subtitle_item['MatchedBy']
            movie_kind = subtitle_item['MovieKind']
            hash = subtitle_item['MovieHash']
            movie_name = subtitle_item['MovieName']
            movie_release_name = subtitle_item['MovieReleaseName']
            movie_year = int(subtitle_item['MovieYear']) if subtitle_item['MovieYear'] else None
            movie_imdb_id = 'tt' + subtitle_item['IDMovieImdb']
            series_season = int(subtitle_item['SeriesSeason']) if subtitle_item['SeriesSeason'] else None
            series_episode = int(subtitle_item['SeriesEpisode']) if subtitle_item['SeriesEpisode'] else None
            filename = subtitle_item['SubFileName']
            encoding = subtitle_item.get('SubEncoding') or None

            subtitle = self.subtitle_class(language, hearing_impaired, page_link, subtitle_id, matched_by, movie_kind,
                                           hash, movie_name, movie_release_name, movie_year, movie_imdb_id,
                                           series_season, series_episode, filename, encoding)
            logger.debug('Found subtitle %r by %s', subtitle, matched_by)
            subtitles.append(subtitle)

        return subtitles
Example #31
0
 def test_ne_with_country(self):
     self.assertNotEqual(Language('eng', 'US'),
                         Language('eng', Country('GB')))
Example #32
0
def get_language(lang_short):
    return Language.fromietf(lang_short)
Example #33
0
 def test_eq_with_country(self):
     self.assertEqual(Language('eng', 'US'), Language('eng', Country('US')))
Example #34
0
 def test_converter_type(self):
     self.assertEqual(language_converters['type'].codes,
                      set(['A', 'C', 'E', 'H', 'L', 'S']))
     self.assertEqual(Language('eng').type, 'living')
     self.assertEqual(Language('und').type, 'special')
Example #35
0
    def __init__(self, directory, filename, logger=None):

        # Setup logging
        if logger:
            log = logger
        else:
            log = logging.getLogger(__name__)

        # Setup encoding to avoid UTF-8 errors
        if sys.version[0] == '2':
            SYS_ENCODING = None
            try:
                locale.setlocale(locale.LC_ALL, "")
                SYS_ENCODING = locale.getpreferredencoding()
            except (locale.Error, IOError):
                pass

            # For OSes that are poorly configured just force UTF-8
            if not SYS_ENCODING or SYS_ENCODING in ('ANSI_X3.4-1968',
                                                    'US-ASCII', 'ASCII'):
                SYS_ENCODING = 'UTF-8'

            if not hasattr(sys, "setdefaultencoding"):
                reload(sys)

            try:
                # pylint: disable=E1101
                # On non-unicode builds this will raise an AttributeError, if encoding type is not valid it throws a LookupError
                sys.setdefaultencoding(SYS_ENCODING)
            except:
                log.exception(
                    "Sorry, your environment is not setup correctly for utf-8 support. Please fix your setup and try again"
                )
                sys.exit(
                    "Sorry, your environment is not setup correctly for utf-8 support. Please fix your setup and try again"
                )

        log.info(sys.executable)

        # Default settings for SickBeard
        sb_defaults = {
            'host': 'localhost',
            'port': '8081',
            'ssl': "False",
            'api_key': '',
            'web_root': '',
            'username': '',
            'password': ''
        }
        # Default MP4 conversion settings
        mp4_defaults = {
            'ffmpeg': 'ffmpeg.exe',
            'ffprobe': 'ffprobe.exe',
            'threads': 'auto',
            'output_directory': '',
            'copy_to': '',
            'move_to': '',
            'output_extension': 'mp4',
            'output_format': 'mp4',
            'delete_original': 'True',
            'relocate_moov': 'True',
            'ios-audio': 'True',
            'ios-first-track-only': 'False',
            'ios-audio-filter': '',
            'max-audio-channels': '',
            'audio-language': '',
            'audio-default-language': '',
            'audio-codec': 'ac3',
            'audio-filter': '',
            'audio-channel-bitrate': '256',
            'video-codec': 'h264, x264',
            'video-bitrate': '',
            'video-max-width': '',
            'h264-max-level': '',
            'aac_adtstoasc': 'False',
            'use-qsv-decoder-with-encoder': 'True',
            'subtitle-codec': 'mov_text',
            'subtitle-language': '',
            'subtitle-default-language': '',
            'subtitle-encoding': '',
            'convert-mp4': 'False',
            'fullpathguess': 'True',
            'tagfile': 'True',
            'tag-language': 'en',
            'download-artwork': 'poster',
            'download-subs': 'False',
            'embed-subs': 'True',
            'sub-providers': 'addic7ed, podnapisi, thesubdb, opensubtitles',
            'permissions': '777',
            'post-process': 'False',
            'pix-fmt': ''
        }
        # Default settings for CouchPotato
        cp_defaults = {
            'host': 'localhost',
            'port': '5050',
            'username': '',
            'password': '',
            'apikey': '',
            'delay': '65',
            'method': 'renamer',
            'delete_failed': 'False',
            'ssl': 'False',
            'web_root': ''
        }
        # Default settings for Sonarr
        sonarr_defaults = {
            'host': 'localhost',
            'port': '8989',
            'apikey': '',
            'ssl': 'False',
            'web_root': ''
        }
        # Default uTorrent settings
        utorrent_defaults = {
            'couchpotato-label': 'couchpotato',
            'sickbeard-label': 'sickbeard',
            'sickrage-label': 'sickrage',
            'sonarr-label': 'sonarr',
            'bypass-label': 'bypass',
            'convert': 'True',
            'webui': 'False',
            'action_before': 'stop',
            'action_after': 'removedata',
            'host': 'http://localhost:8080/',
            'username': '',
            'password': ''
        }
        # Default SAB settings
        sab_defaults = {
            'convert': 'True',
            'Sickbeard-category': 'sickbeard',
            'Sickrage-category': 'sickrage',
            'Couchpotato-category': 'couchpotato',
            'Sonarr-category': 'sonarr',
            'Bypass-category': 'bypass'
        }
        # Default Sickrage Settings
        sr_defaults = {
            'host': 'localhost',
            'port': '8081',
            'ssl': "False",
            'api_key': '',
            'web_root': '',
            'username': '',
            'password': ''
        }

        # Default deluge settings
        deluge_defaults = {
            'couchpotato-label': 'couchpotato',
            'sickbeard-label': 'sickbeard',
            'sickrage-label': 'sickrage',
            'sonarr-label': 'sonarr',
            'bypass-label': 'bypass',
            'convert': 'True',
            'host': 'localhost',
            'port': '58846',
            'username': '',
            'password': ''
        }

        # Default Plex Settings
        plex_defaults = {
            'host': 'localhost',
            'port': '32400',
            'refresh': 'true',
            'token': ''
        }

        defaults = {
            'SickBeard': sb_defaults,
            'CouchPotato': cp_defaults,
            'Sonarr': sonarr_defaults,
            'MP4': mp4_defaults,
            'uTorrent': utorrent_defaults,
            'SABNZBD': sab_defaults,
            'Sickrage': sr_defaults,
            'Deluge': deluge_defaults,
            'Plex': plex_defaults
        }
        write = False  # Will be changed to true if a value is missing from the config file and needs to be written

        config = configparser.SafeConfigParser()
        configFile = os.path.join(directory, filename)
        if os.path.isfile(configFile):
            config.read(configFile)
        else:
            log.error("Config file not found, creating %s." % configFile)
            # config.filename = filename
            write = True

        # Make sure all sections and all keys for each section are present
        for s in defaults:
            if not config.has_section(s):
                config.add_section(s)
                write = True
            for k in defaults[s]:
                if not config.has_option(s, k):
                    config.set(s, k, defaults[s][k])
                    write = True

        # If any keys are missing from the config file, write them
        if write:
            self.writeConfig(config, configFile)

        # Read relevant MP4 section information
        section = "MP4"
        self.ffmpeg = os.path.normpath(self.raw(config.get(
            section, "ffmpeg")))  # Location of FFMPEG.exe
        self.ffprobe = os.path.normpath(
            self.raw(config.get(section,
                                "ffprobe")))  # Location of FFPROBE.exe
        self.threads = config.get(section,
                                  "threads")  # Number of FFMPEG threads
        try:
            if int(self.threads) < 1:
                self.threads = "auto"
        except:
            self.threads = "auto"

        self.output_dir = config.get(section, "output_directory")
        if self.output_dir == '':
            self.output_dir = None
        else:
            self.output_dir = os.path.normpath(self.raw(
                self.output_dir))  # Output directory
        self.copyto = config.get(
            section,
            "copy_to")  # Directories to make copies of the final product
        if self.copyto == '':
            self.copyto = None
        else:
            self.copyto = self.copyto.split('|')
            for i in range(len(self.copyto)):
                self.copyto[i] = os.path.normpath(self.copyto[i])
                if not os.path.isdir(self.copyto[i]):
                    try:
                        os.makedirs(self.copyto[i])
                    except:
                        log.exception("Error making directory %s." %
                                      (self.copyto[i]))
        self.moveto = config.get(
            section, "move_to")  # Directory to move final product to
        if self.moveto == '':
            self.moveto = None
        else:
            self.moveto = os.path.normpath(self.moveto)
            if not os.path.isdir(self.moveto):
                try:
                    os.makedirs(self.moveto)
                except:
                    log.exception("Error making directory %s." % (self.moveto))
                    self.moveto = None

        self.output_extension = config.get(
            section, "output_extension")  # Output extension
        self.output_format = config.get(section,
                                        "output_format")  # Output format
        if self.output_format not in valid_formats:
            self.output_format = 'mov'
        self.delete = config.getboolean(
            section, "delete_original")  # Delete original file
        self.relocate_moov = config.getboolean(
            section, "relocate_moov")  # Relocate MOOV atom to start of file
        if self.relocate_moov:
            try:
                import qtfaststart
            except:
                log.error(
                    "Please install QTFastStart via PIP, relocate_moov will be disabled without this module."
                )
                self.relocate_moov = False
        self.acodec = config.get(section, "audio-codec").lower(
        )  # Gets the desired audio codec, if no valid codec selected, default to AC3
        if self.acodec == '':
            self.acodec == ['ac3']
        else:
            self.acodec = self.acodec.lower().replace(' ', '').split(',')

        self.abitrate = config.get(section, "audio-channel-bitrate")
        try:
            self.abitrate = int(self.abitrate)
        except:
            self.abitrate = 256
            log.warning(
                "Audio bitrate was invalid, defaulting to 256 per channel.")
        if self.abitrate > 256:
            log.warning(
                "Audio bitrate >256 may create errors with common codecs.")

        self.afilter = config.get(
            section, "audio-filter").lower().strip()  # Audio filter
        if self.afilter == '':
            self.afilter = None

        self.iOS = config.get(
            section, "ios-audio"
        )  # Creates a second audio channel if the standard output methods are different from this for iOS compatability
        if self.iOS == "" or self.iOS.lower() in ['false', 'no', 'f', '0']:
            self.iOS = False
        else:
            if self.iOS.lower() in ['true', 'yes', 't', '1']:
                self.iOS = ['aac']
            else:
                self.iOS = self.iOS.lower().replace(' ', '').split(',')

        self.iOSFirst = config.getboolean(
            section, "ios-first-track-only"
        )  # Enables the iOS audio option only for the first track

        self.iOSfilter = config.get(
            section, "ios-audio-filter").lower().strip()  # iOS audio filter
        if self.iOSfilter == '':
            self.iOSfilter = None

        self.downloadsubs = config.getboolean(
            section, "download-subs"
        )  # Enables downloading of subtitles from the internet sources using subliminal
        if self.downloadsubs:
            try:
                import subliminal
            except Exception as e:
                self.downloadsubs = False
                log.exception(
                    "Subliminal is not installed, automatically downloading of subs has been disabled."
                )
        self.subproviders = config.get(section, 'sub-providers').lower()
        if self.subproviders == '':
            self.downloadsubs = False
            log.warning(
                "You must specifiy at least one subtitle provider to downlaod subs automatically, subtitle downloading disabled."
            )
        else:
            self.subproviders = self.subproviders.lower().replace(
                ' ', '').split(',')

        self.embedsubs = config.getboolean(section, 'embed-subs')

        self.permissions = config.get(section, 'permissions')
        try:
            self.permissions = int(self.permissions, 8)
        except:
            self.log.exception("Invalid permissions, defaulting to 777.")
            self.permissions = int("0777", 8)

        try:
            self.postprocess = config.getboolean(section, 'post-process')
        except:
            self.postprocess = False

        self.aac_adtstoasc = config.getboolean(section, 'aac_adtstoasc')

        # Setup variable for maximum audio channels
        self.maxchannels = config.get(section, 'max-audio-channels')
        if self.maxchannels == "":
            self.maxchannels = None
        else:
            try:
                self.maxchannels = int(self.maxchannels)
            except:
                log.exception("Invalid number of audio channels specified.")
                self.maxchannels = None
        if self.maxchannels is not None and self.maxchannels < 1:
            log.warning("Must have at least 1 audio channel.")
            self.maxchannels = None

        self.vcodec = config.get(section, "video-codec")
        if self.vcodec == '':
            self.vcodec == ['h264', 'x264']
        else:
            self.vcodec = self.vcodec.lower().replace(' ', '').split(',')

        self.vbitrate = config.get(section, "video-bitrate")
        if self.vbitrate == '':
            self.vbitrate = None
        else:
            try:
                self.vbitrate = int(self.vbitrate)
                if not (self.vbitrate > 0):
                    self.vbitrate = None
                    log.warning(
                        "Video bitrate must be greater than 0, defaulting to no video bitrate cap."
                    )
            except:
                log.exception(
                    "Invalid video bitrate, defaulting to no video bitrate cap."
                )
                self.vbitrate = None

        self.vwidth = config.get(section, "video-max-width")
        if self.vwidth == '':
            self.vwidth = None
        else:
            try:
                self.vwidth = int(self.vwidth)
            except:
                log.exception("Invalid video width, defaulting to none.")
                self.vwidth = None

        self.h264_level = config.get(section, "h264-max-level")
        if self.h264_level == '':
            self.h264_level = None
        else:
            try:
                self.h264_level = float(self.h264_level)
            except:
                log.exception("Invalid h264 level, defaulting to none.")
                self.h264_level = None

        self.qsv_decoder = config.getboolean(
            section, "use-qsv-decoder-with-encoder"
        )  # Use Intel QuickSync Decoder when using QuickSync Encoder
        self.pix_fmt = config.get(section, "pix-fmt").strip().lower()
        if self.pix_fmt == '':
            self.pix_fmt = None
        else:
            self.pix_fmt = self.pix_fmt.replace(' ', '').split(',')

        self.awl = config.get(section, 'audio-language').strip().lower(
        )  # List of acceptable languages for audio streams to be carried over from the original file, separated by a comma. Blank for all
        if self.awl == '':
            self.awl = None
        else:
            self.awl = self.awl.replace(' ', '').split(',')

        self.scodec = config.get(section, 'subtitle-codec').strip().lower()
        if not self.scodec or self.scodec == "":
            if self.embedsubs:
                self.scodec = ['mov_text']
            else:
                self.scodec = ['srt']
            log.warning("Invalid subtitle codec, defaulting to '%s'." %
                        self.scodec)
        else:
            self.scodec = self.scodec.replace(' ', '').split(',')

        if self.embedsubs:
            if len(self.scodec) > 1:
                log.warning(
                    "Can only embed one subtitle type, defaulting to 'mov_text'."
                )
                self.scodec = ['mov_text']
            if self.scodec[0] not in valid_internal_subcodecs:
                log.warning(
                    "Invalid interal subtitle codec %s, defaulting to 'mov_text'."
                    % self.scodec[0])
                self.scodec = ['mov_text']
        else:
            for codec in self.scodec:
                if codec not in valid_external_subcodecs:
                    log.warning(
                        "Invalid external subtitle codec %s, ignoring." %
                        codec)
                    self.scodec.remove(codec)

            if len(self.scodec) == 0:
                log.warning(
                    "No valid subtitle formats found, defaulting to 'srt'.")
                self.scodec = ['srt']

        self.swl = config.get(section, 'subtitle-language').strip().lower(
        )  # List of acceptable languages for subtitle streams to be carried over from the original file, separated by a comma. Blank for all
        if self.swl == '':
            self.swl = None
        else:
            self.swl = self.swl.replace(' ', '').split(',')

        self.subencoding = config.get(section,
                                      'subtitle-encoding').strip().lower()
        if self.subencoding == '':
            self.subencoding = None

        self.adl = config.get(section, 'audio-default-language').strip().lower(
        )  # What language to default an undefinied audio language tag to. If blank, it will remain undefined. This is useful for single language releases which tend to leave things tagged as und
        if self.adl == "" or len(self.adl) > 3:
            self.adl = None

        self.sdl = config.get(section, 'subtitle-default-language').strip(
        ).lower(
        )  # What language to default an undefinied subtitle language tag to. If blank, it will remain undefined. This is useful for single language releases which tend to leave things tagged as und
        if self.sdl == "" or len(self.sdl) > 3:
            self.sdl = None
        # Prevent incompatible combination of settings
        if self.output_dir == "" and self.delete is False:
            log.error(
                "You must specify an alternate output directory if you aren't going to delete the original file."
            )
            sys.exit()
        # Create output directory if it does not exist
        if self.output_dir is not None:
            if not os.path.isdir(self.output_dir):
                os.makedirs(self.output_dir)
        self.processMP4 = config.getboolean(
            section, "convert-mp4"
        )  # Determine whether or not to reprocess mp4 files or just tag them
        self.fullpathguess = config.getboolean(
            section, "fullpathguess")  # Guess using the full path or not
        self.tagfile = config.getboolean(section,
                                         "tagfile")  # Tag files with metadata
        self.taglanguage = config.get(
            section, "tag-language").strip().lower()  # Language to tag files
        if len(self.taglanguage) > 2:
            try:
                babel = Language(self.taglanguage)
                self.taglanguage = babel.alpha2
            except:
                log.exception(
                    "Unable to set tag language, defaulting to English.")
                self.taglanguage = 'en'
        elif len(self.taglanguage) < 2:
            log.exception("Unable to set tag language, defaulting to English.")
            self.taglanguage = 'en'
        self.artwork = config.get(
            section, "download-artwork").lower()  # Download and embed artwork
        if self.artwork == "poster":
            self.artwork = True
            self.thumbnail = False
        elif self.artwork == "thumb" or self.artwork == "thumbnail":
            self.artwork = True
            self.thumbnail = True
        else:
            self.thumbnail = False
            try:
                self.artwork = config.getboolean(section, "download-artwork")
            except:
                self.artwork = True
                self.log.error(
                    "Invalid download-artwork value, defaulting to 'poster'.")

        # Read relevant CouchPotato section information
        section = "CouchPotato"
        self.CP = {}
        self.CP['host'] = config.get(section, "host")
        self.CP['port'] = config.get(section, "port")
        self.CP['username'] = config.get(section, "username")
        self.CP['password'] = config.get(section, "password")
        self.CP['apikey'] = config.get(section, "apikey")
        self.CP['delay'] = config.get(section, "delay")
        self.CP['method'] = config.get(section, "method")
        self.CP['web_root'] = config.get(section, "web_root")

        try:
            self.CP['delay'] = float(self.CP['delay'])
        except ValueError:
            self.CP['delay'] = 60
        try:
            self.CP['delete_failed'] = config.getboolean(
                section, "delete_failed")
        except (configparser.NoOptionError, ValueError):
            self.CP['delete_failed'] = False
        try:
            if config.getboolean(section, 'ssl'):
                self.CP['protocol'] = "https://"
            else:
                self.CP['protocol'] = "http://"
        except (configparser.NoOptionError, ValueError):
            self.CP['protocol'] = "http://"

        # Read relevant uTorrent section information
        section = "uTorrent"
        self.uTorrent = {}
        self.uTorrent['cp'] = config.get(section, "couchpotato-label").lower()
        self.uTorrent['sb'] = config.get(section, "sickbeard-label").lower()
        self.uTorrent['sr'] = config.get(section, "sickrage-label").lower()
        self.uTorrent['sonarr'] = config.get(section, "sonarr-label").lower()
        self.uTorrent['bypass'] = config.get(section, "bypass-label").lower()
        try:
            self.uTorrent['convert'] = config.getboolean(section, "convert")
        except:
            self.uTorrent['convert'] = False
        self.uTorrentWebUI = config.getboolean(section, "webui")
        self.uTorrentActionBefore = config.get(section,
                                               "action_before").lower()
        self.uTorrentActionAfter = config.get(section, "action_after").lower()
        self.uTorrentHost = config.get(section, "host").lower()
        self.uTorrentUsername = config.get(section, "username")
        self.uTorrentPassword = config.get(section, "password")

        # Read relevant Deluge section information
        section = "Deluge"
        self.deluge = {}
        self.deluge['cp'] = config.get(section, "couchpotato-label").lower()
        self.deluge['sb'] = config.get(section, "sickbeard-label").lower()
        self.deluge['sr'] = config.get(section, "sickrage-label").lower()
        self.deluge['sonarr'] = config.get(section, "sonarr-label").lower()
        self.deluge['bypass'] = config.get(section, "bypass-label").lower()
        try:
            self.deluge['convert'] = config.getboolean(section, "convert")
        except:
            self.deluge['convert'] = False
        self.deluge['host'] = config.get(section, "host").lower()
        self.deluge['port'] = config.get(section, "port")
        self.deluge['user'] = config.get(section, "username")
        self.deluge['pass'] = config.get(section, "password")

        # Read relevant Sonarr section information
        section = "Sonarr"
        self.Sonarr = {}
        self.Sonarr['host'] = config.get(section, "host")
        self.Sonarr['port'] = config.get(section, "port")
        self.Sonarr['apikey'] = config.get(section, "apikey")
        self.Sonarr['ssl'] = config.get(section, "ssl")
        self.Sonarr['web_root'] = config.get(section, "web_root")

        # Read Sickbeard section information
        section = "SickBeard"
        self.Sickbeard = {}
        self.Sickbeard['host'] = config.get(section, "host")  # Server Address
        self.Sickbeard['port'] = config.get(section, "port")  # Server Port
        self.Sickbeard['api_key'] = config.get(section,
                                               "api_key")  # Sickbeard API key
        self.Sickbeard['web_root'] = config.get(
            section, "web_root")  # Sickbeard webroot
        self.Sickbeard['ssl'] = config.getboolean(section, "ssl")  # SSL
        self.Sickbeard['user'] = config.get(section, "username")
        self.Sickbeard['pass'] = config.get(section, "password")

        # Read Sickrage section information
        section = "Sickrage"
        self.Sickrage = {}
        self.Sickrage['host'] = config.get(section, "host")  # Server Address
        self.Sickrage['port'] = config.get(section, "port")  # Server Port
        self.Sickrage['api_key'] = config.get(section,
                                              "api_key")  # Sickbeard API key
        self.Sickrage['web_root'] = config.get(section,
                                               "web_root")  # Sickbeard webroot
        self.Sickrage['ssl'] = config.getboolean(section, "ssl")  # SSL
        self.Sickrage['user'] = config.get(section, "username")
        self.Sickrage['pass'] = config.get(section, "password")

        # Read SAB section information
        section = "SABNZBD"
        self.SAB = {}
        try:
            self.SAB['convert'] = config.getboolean(section,
                                                    "convert")  # Convert
        except:
            self.SAB['convert'] = False
        self.SAB['cp'] = config.get(section, "Couchpotato-category").lower()
        self.SAB['sb'] = config.get(section, "Sickbeard-category").lower()
        self.SAB['sr'] = config.get(section, "Sickrage-category").lower()
        self.SAB['sonarr'] = config.get(section, "Sonarr-category").lower()
        self.SAB['bypass'] = config.get(section, "Bypass-category").lower()

        # Read Plex section information
        section = "Plex"
        self.Plex = {}
        self.Plex['host'] = config.get(section, "host")
        self.Plex['port'] = config.get(section, "port")
        try:
            self.Plex['refresh'] = config.getboolean(section, "refresh")
        except:
            self.Plex['refresh'] = False
        self.Plex['token'] = config.get(section, "token")
        if self.Plex['token'] == '':
            self.Plex['token'] = None

        # Pass the values on
        self.config = config
        self.configFile = configFile
Example #36
0
 def convert(self, value, param, ctx):
     try:
         return Language.fromietf(value)
     except BabelfishError:
         self.fail('%s is not a valid language' % value)
Example #37
0
 def test_wrong_language(self):
     self.assertRaises(ValueError, lambda: Language('zzz'))
Example #38
0
 def test_fromietf_wrong_country(self):
     self.assertRaises(ValueError, lambda: Language.fromietf('fra-YZ'))
Example #39
0
 def test_fromietf_wrong_script(self):
     self.assertRaises(ValueError, lambda: Language.fromietf('fra-FR-Wxyz'))
Example #40
0
 def test_fromietf_alpha2_language(self):
     language = Language.fromietf('fr-Latn')
     self.assertEqual(language.alpha3, 'fra')
     self.assertIsNone(language.country)
     self.assertEqual(language.script, Script('Latn'))
Example #41
0
 def test_fromietf_wrong_language(self):
     self.assertRaises(ValueError, lambda: Language.fromietf('xyz-FR'))
Example #42
0
 def test_fromietf_no_country_no_script(self):
     language = Language.fromietf('fra-FR')
     self.assertEqual(language.alpha3, 'fra')
     self.assertEqual(language.country, Country('FR'))
     self.assertIsNone(language.script)
Example #43
0
 def test_fromietf_country_script(self):
     language = Language.fromietf('fra-FR-Latn')
     self.assertEqual(language.alpha3, 'fra')
     self.assertEqual(language.country, Country('FR'))
     self.assertEqual(language.script, Script('Latn'))
Example #44
0
    def test_converter_opensubtitles(self):
        self.assertEqual(
            Language('fra').opensubtitles,
            Language('fra').alpha3b)
        self.assertEqual(Language('por', 'BR').opensubtitles, 'pob')
        self.assertEqual(Language.fromopensubtitles('fre'), Language('fra'))
        self.assertEqual(Language.fromopensubtitles('pob'),
                         Language('por', 'BR'))
        self.assertEqual(Language.fromopensubtitles('pb'),
                         Language('por', 'BR'))
        # Montenegrin is not recognized as an ISO language (yet?) but for now it is
        # unofficially accepted as Serbian from Montenegro
        self.assertEqual(Language.fromopensubtitles('mne'),
                         Language('srp', 'ME'))
        self.assertEqual(Language.fromcode('pob', 'opensubtitles'),
                         Language('por', 'BR'))
        self.assertRaises(LanguageReverseError,
                          lambda: Language.fromopensubtitles('zzz'))
        self.assertRaises(LanguageConvertError,
                          lambda: Language('aaa').opensubtitles)
        self.assertEqual(len(language_converters['opensubtitles'].codes), 606)

        # test with all the LANGUAGES from the opensubtitles api
        # downloaded from: http://www.opensubtitles.org/addons/export_languages.php
        f = resource_stream('babelfish', 'data/opensubtitles_languages.txt')
        f.readline()
        for l in f:
            idlang, alpha2, _, upload_enabled, web_enabled = l.decode(
                'utf-8').strip().split('\t')
            if not int(upload_enabled) and not int(web_enabled):
                # do not test LANGUAGES that are too esoteric / not widely available
                continue
            self.assertEqual(
                Language.fromopensubtitles(idlang).opensubtitles, idlang)
            if alpha2:
                self.assertEqual(Language.fromopensubtitles(idlang),
                                 Language.fromopensubtitles(alpha2))
        f.close()
Example #45
0
 def test_script(self):
     self.assertEqual(Language('srp', script='Latn').script, Script('Latn'))
     self.assertEqual(
         Language('srp', script=Script('Cyrl')).script, Script('Cyrl'))
Example #46
0
 def test_unknown_language(self):
     self.assertEqual(Language('zzzz', unknown='und'), Language('und'))
Example #47
0
 def languages(self):
     return {
         Language.fromietf(l)
         for l in json.loads(self.config.get('general', 'languages'))
     }
Example #48
0
class OpenSubtitlesProvider(Provider):
    """OpenSubtitles Provider.

    :param str username: username.
    :param str password: password.

    """
    languages = {
        Language.fromopensubtitles(l)
        for l in language_converters['opensubtitles'].codes
    }

    def __init__(self, username=None, password=None):
        self.server = ServerProxy('https://api.opensubtitles.org/xml-rpc',
                                  TimeoutSafeTransport(10))
        if username and not password or not username and password:
            raise ConfigurationError('Username and password must be specified')
        # None values not allowed for logging in, so replace it by ''
        self.username = username or ''
        self.password = password or ''
        self.token = None

    def initialize(self):
        logger.info('Logging in')
        response = checked(
            self.server.LogIn(self.username, self.password, 'eng',
                              'Grabber v1'))
        self.token = response['token']
        logger.debug('Logged in with token %r', self.token)

    def terminate(self):
        logger.info('Logging out')
        checked(self.server.LogOut(self.token))
        self.server.close()
        self.token = None
        logger.debug('Logged out')

    def no_operation(self):
        logger.debug('No operation')
        checked(self.server.NoOperation(self.token))

    def query(self,
              languages,
              hash=None,
              size=None,
              imdb_id=None,
              query=None,
              season=None,
              episode=None,
              tag=None):
        # fill the search criteria
        criteria = []
        if hash and size:
            criteria.append({'moviehash': hash, 'moviebytesize': str(size)})
        if imdb_id:
            criteria.append({'imdbid': imdb_id[2:]})
        if tag:
            criteria.append({'tag': tag})
        if query and season and episode:
            criteria.append({
                'query': query.replace('\'', ''),
                'season': season,
                'episode': episode
            })
        elif query:
            criteria.append({'query': query.replace('\'', '')})
        if not criteria:
            raise ValueError('Not enough information')

        # add the language
        for criterion in criteria:
            criterion['sublanguageid'] = ','.join(
                sorted(l.opensubtitles for l in languages))

        # query the server
        logger.info('Searching subtitles %r', criteria)
        response = checked(self.server.SearchSubtitles(self.token, criteria))
        subtitles = []

        # exit if no data
        if not response['data']:
            logger.debug('No subtitles found')
            return subtitles

        # loop over subtitle items
        for subtitle_item in response['data']:
            # read the item
            language = Language.fromopensubtitles(
                subtitle_item['SubLanguageID'])
            hearing_impaired = bool(int(subtitle_item['SubHearingImpaired']))
            page_link = subtitle_item['SubtitlesLink']
            subtitle_id = int(subtitle_item['IDSubtitleFile'])
            matched_by = subtitle_item['MatchedBy']
            movie_kind = subtitle_item['MovieKind']
            hash = subtitle_item['MovieHash']
            movie_name = subtitle_item['MovieName']
            movie_release_name = subtitle_item['MovieReleaseName']
            movie_year = int(subtitle_item['MovieYear']
                             ) if subtitle_item['MovieYear'] else None
            movie_imdb_id = 'tt' + subtitle_item['IDMovieImdb']
            series_season = int(subtitle_item['SeriesSeason']
                                ) if subtitle_item['SeriesSeason'] else None
            series_episode = int(subtitle_item['SeriesEpisode']
                                 ) if subtitle_item['SeriesEpisode'] else None
            filename = subtitle_item['SubFileName']
            encoding = subtitle_item.get('SubEncoding') or None

            subtitle = OpenSubtitlesSubtitle(
                language, hearing_impaired, page_link, subtitle_id, matched_by,
                movie_kind, hash, movie_name, movie_release_name, movie_year,
                movie_imdb_id, series_season, series_episode, filename,
                encoding)
            logger.debug('Found subtitle %r by %s', subtitle, matched_by)
            subtitles.append(subtitle)

        return subtitles

    def list_subtitles(self, video, languages):
        season = episode = None
        if isinstance(video, Episode):
            query = video.series
            season = video.season
            episode = video.episode
        else:
            query = video.title

        return self.query(languages,
                          hash=video.hashes.get('opensubtitles'),
                          size=video.size,
                          imdb_id=video.imdb_id,
                          query=query,
                          season=season,
                          episode=episode,
                          tag=os.path.basename(video.name))

    def download_subtitle(self, subtitle):
        logger.info('Downloading subtitle %r', subtitle)
        response = checked(
            self.server.DownloadSubtitles(self.token,
                                          [str(subtitle.subtitle_id)]))
        subtitle.content = fix_line_ending(
            zlib.decompress(base64.b64decode(response['data'][0]['data']), 47))
Example #49
0
    def query(self,
              languages,
              hash=None,
              size=None,
              imdb_id=None,
              query=None,
              season=None,
              episode=None,
              tag=None):
        # fill the search criteria
        criteria = []
        if hash and size:
            criteria.append({'moviehash': hash, 'moviebytesize': str(size)})
        if imdb_id:
            criteria.append({'imdbid': imdb_id[2:]})
        if tag:
            criteria.append({'tag': tag})
        if query and season and episode:
            criteria.append({
                'query': query.replace('\'', ''),
                'season': season,
                'episode': episode
            })
        elif query:
            criteria.append({'query': query.replace('\'', '')})
        if not criteria:
            raise ValueError('Not enough information')

        # add the language
        for criterion in criteria:
            criterion['sublanguageid'] = ','.join(
                sorted(l.opensubtitles for l in languages))

        # query the server
        logger.info('Searching subtitles %r', criteria)
        response = checked(self.server.SearchSubtitles(self.token, criteria))
        subtitles = []

        # exit if no data
        if not response['data']:
            logger.debug('No subtitles found')
            return subtitles

        # loop over subtitle items
        for subtitle_item in response['data']:
            # read the item
            language = Language.fromopensubtitles(
                subtitle_item['SubLanguageID'])
            hearing_impaired = bool(int(subtitle_item['SubHearingImpaired']))
            page_link = subtitle_item['SubtitlesLink']
            subtitle_id = int(subtitle_item['IDSubtitleFile'])
            matched_by = subtitle_item['MatchedBy']
            movie_kind = subtitle_item['MovieKind']
            hash = subtitle_item['MovieHash']
            movie_name = subtitle_item['MovieName']
            movie_release_name = subtitle_item['MovieReleaseName']
            movie_year = int(subtitle_item['MovieYear']
                             ) if subtitle_item['MovieYear'] else None
            movie_imdb_id = 'tt' + subtitle_item['IDMovieImdb']
            series_season = int(subtitle_item['SeriesSeason']
                                ) if subtitle_item['SeriesSeason'] else None
            series_episode = int(subtitle_item['SeriesEpisode']
                                 ) if subtitle_item['SeriesEpisode'] else None
            filename = subtitle_item['SubFileName']
            encoding = subtitle_item.get('SubEncoding') or None

            subtitle = OpenSubtitlesSubtitle(
                language, hearing_impaired, page_link, subtitle_id, matched_by,
                movie_kind, hash, movie_name, movie_release_name, movie_year,
                movie_imdb_id, series_season, series_episode, filename,
                encoding)
            logger.debug('Found subtitle %r by %s', subtitle, matched_by)
            subtitles.append(subtitle)

        return subtitles
Example #50
0
class SubsCenterProvider(Provider):
    """SubsCenter Provider."""
    languages = {Language.fromalpha2(l) for l in ['he']}
    server_url = 'http://subscenter.cinemast.com/he/'

    def __init__(self, username=None, password=None):
        if username is not None and password is None or username is None and password is not None:
            raise ConfigurationError('Username and password must be specified')

        self.username = username
        self.password = password
        self.logged_in = False

    def initialize(self):
        self.session = Session()
        self.session.headers[
            'User-Agent'] = 'Subliminal/%s' % __short_version__

        # login
        if self.username is not None and self.password is not None:
            logger.debug('Logging in')
            url = self.server_url + 'subscenter/accounts/login/'

            # retrieve CSRF token
            self.session.get(url)
            csrf_token = self.session.cookies['csrftoken']

            # actual login
            data = {
                'username': self.username,
                'password': self.password,
                'csrfmiddlewaretoken': csrf_token
            }
            r = self.session.post(url, data, allow_redirects=False, timeout=10)

            if r.status_code != 302:
                raise AuthenticationError(self.username)

            logger.info('Logged in')
            self.logged_in = True

    def terminate(self):
        # logout
        if self.logged_in:
            logger.info('Logging out')
            r = self.session.get(self.server_url +
                                 'subscenter/accounts/logout/',
                                 timeout=10)
            r.raise_for_status()
            logger.info('Logged out')
            self.logged_in = False

        self.session.close()

    @region.cache_on_arguments(expiration_time=SHOW_EXPIRATION_TIME)
    def _search_url_titles(self, title):
        """Search the URL titles by kind for the given `title`.

        :param str title: title to search for.
        :return: the URL titles by kind.
        :rtype: collections.defaultdict

        """
        # make the search
        logger.info('Searching title name for %r', title)
        r = self.session.get(self.server_url + 'subtitle/search/',
                             params={'q': title},
                             timeout=10)
        r.raise_for_status()

        # get the suggestions
        soup = ParserBeautifulSoup(r.content, ['lxml', 'html.parser'])
        links = soup.select('#processes div.generalWindowTop a')
        logger.debug('Found %d suggestions', len(links))
        url_titles = defaultdict(list)
        for link in links:
            parts = link.attrs['href'].split('/')
            url_titles[parts[-3]].append(parts[-2])

        return url_titles

    def query(self, title, season=None, episode=None):
        # search for the url title
        url_titles = self._search_url_titles(title)

        # episode
        if season and episode:
            if 'series' not in url_titles:
                logger.error('No URL title found for series %r', title)
                return []
            url_title = url_titles['series'][0]
            logger.debug('Using series title %r', url_title)
            url = self.server_url + 'cinemast/data/series/sb/{}/{}/{}/'.format(
                url_title, season, episode)
            page_link = self.server_url + 'subtitle/series/{}/{}/{}/'.format(
                url_title, season, episode)
        else:
            if 'movie' not in url_titles:
                logger.error('No URL title found for movie %r', title)
                return []
            url_title = url_titles['movie'][0]
            logger.debug('Using movie title %r', url_title)
            url = self.server_url + 'cinemast/data/movie/sb/{}/'.format(
                url_title)
            page_link = self.server_url + 'subtitle/movie/{}/'.format(
                url_title)

        # get the list of subtitles
        logger.debug('Getting the list of subtitles')
        r = self.session.get(url)
        r.raise_for_status()
        results = json.loads(r.text)

        # loop over results
        subtitles = {}
        for language_code, language_data in results.items():
            for quality_data in language_data.values():
                for quality, subtitles_data in quality_data.items():
                    for subtitle_item in subtitles_data.values():
                        # read the item
                        language = Language.fromalpha2(language_code)
                        hearing_impaired = bool(
                            subtitle_item['hearing_impaired'])
                        subtitle_id = subtitle_item['id']
                        subtitle_key = subtitle_item['key']
                        downloaded = subtitle_item['downloaded']
                        release = subtitle_item['subtitle_version']

                        # add the release and increment downloaded count if we already have the subtitle
                        if subtitle_id in subtitles:
                            logger.debug(
                                'Found additional release %r for subtitle %d',
                                release, subtitle_id)
                            bisect.insort_left(subtitles[subtitle_id].releases,
                                               release)  # deterministic order
                            subtitles[subtitle_id].downloaded += downloaded
                            continue

                        # otherwise create it
                        subtitle = SubsCenterSubtitle(
                            language, hearing_impaired, page_link, title,
                            season, episode, title, subtitle_id, subtitle_key,
                            downloaded, [release])
                        logger.debug('Found subtitle %r', subtitle)
                        subtitles[subtitle_id] = subtitle

        return subtitles.values()

    def list_subtitles(self, video, languages):
        season = episode = None
        title = video.title

        if isinstance(video, Episode):
            title = video.series
            season = video.season
            episode = video.episode

        return [
            s for s in self.query(title, season, episode)
            if s.language in languages
        ]

    def download_subtitle(self, subtitle):
        # download
        url = self.server_url + 'subtitle/download/{}/{}/'.format(
            subtitle.language.alpha2, subtitle.subtitle_id)
        params = {'v': subtitle.releases[0], 'key': subtitle.subtitle_key}
        r = self.session.get(url,
                             params=params,
                             headers={'Referer': subtitle.page_link},
                             timeout=10)
        r.raise_for_status()

        # open the zip
        with zipfile.ZipFile(io.BytesIO(r.content)) as zf:
            # remove some filenames from the namelist
            namelist = [n for n in zf.namelist() if not n.endswith('.txt')]
            if len(namelist) > 1:
                raise ProviderError('More than one file to unzip')

            subtitle.content = fix_line_ending(zf.read(namelist[0]))
Example #51
0
 def test_eq(self):
     self.assertEqual(Language('eng'), Language('eng'))
Example #52
0
import glob, os
from shutil import copyfile, move
from subliminal import download_best_subtitles, save_subtitles, scan_videos
from babelfish import Language

#Vuse Torrent Default: userProf + "/Documents/Vuze Downloads"
#BiglyBT Torrent Default: userProf + "/Documents/BiglyBT Downloads"

__author__ = "TooSlepy"
__PyVer__ = "3.6"
userProf = os.getenv("userprofile")
videoSaveLoc = userProf + "/Documents/Vuze Downloads"
saveLoc = "D:/Moviex/"
lang = "eng"

os.chdir(videoSaveLoc)
for x in ("*.avi", "*.mkv", "*.mp4", "*.mpg"):
    for file in glob.glob(x):
        fileDir = file.rsplit('.', 1)[0]
        if not os.path.exists(fileDir):
            os.mkdir(fileDir)
        move(file, fileDir + "/" + file)
        # scan for videos in a folder
        videos = scan_videos(fileDir)
        # download best subtitles
        subtitles = download_best_subtitles(videos, {Language(lang)})
        # save them to disk, next to the video
        for v in videos:
            save_subtitles(v, subtitles[v])
        move(fileDir, saveLoc + "/" + fileDir)
        print(file + " has been moved and subtitled.")
Example #53
0
 def test_ne(self):
     self.assertNotEqual(Language('fra'), Language('eng'))
     self.assertIsNotNone(Language('fra'))
Example #54
0
 def test_nonzero(self):
     self.assertFalse(bool(Language('und')))
     self.assertTrue(bool(Language('eng')))
    def generateOptions(self, inputfile, original=None):
        # Get path information from the input file
        input_dir, filename, input_extension = self.parseFile(inputfile)

        info = Converter(self.FFMPEG_PATH, self.FFPROBE_PATH).probe(inputfile)

        # Video stream
        self.log.info("Reading video stream.")
        self.log.info("Video codec detected: %s." % info.video.codec)

        try:
            vbr = self.estimateVideoBitrate(info)
        except:
            vbr = info.format.bitrate / 1000

        if info.video.codec.lower() in self.video_codec:
            vcodec = 'copy'
        else:
            vcodec = self.video_codec[0]
        vbitrate = self.video_bitrate if self.video_bitrate else vbr

        self.log.info("Pix Fmt: %s." % info.video.pix_fmt)
        if self.pix_fmt and info.video.pix_fmt.lower() not in self.pix_fmt:
            self.log.debug(
                "Overriding video pix_fmt. Codec cannot be copied because pix_fmt is not approved."
            )
            vcodec = self.video_codec[0]
            pix_fmt = self.pix_fmt[0]
            if self.video_profile:
                vprofile = self.video_profile[0]
        elif self.pix_fmt:
            pix_fmt = self.pix_fmt[0]
        else:
            pix_fmt = None

        if self.video_bitrate is not None and vbr > self.video_bitrate:
            self.log.debug(
                "Overriding video bitrate. Codec cannot be copied because video bitrate is too high."
            )
            vcodec = self.video_codec[0]
            vbitrate = self.video_bitrate

        if self.video_width is not None and self.video_width < info.video.video_width:
            self.log.debug(
                "Video width is over the max width, it will be downsampled. Video stream can no longer be copied."
            )
            vcodec = self.video_codec[0]
            vwidth = self.video_width
        else:
            vwidth = self.video_width

        if '264' in info.video.codec.lower(
        ) and self.h264_level and info.video.video_level and (
                info.video.video_level / 10 > self.h264_level):
            self.log.info("Video level %0.1f." % (info.video.video_level / 10))
            vcodec = self.video_codec[0]

        self.log.debug("Video codec: %s." % vcodec)
        self.log.debug("Video bitrate: %s." % vbitrate)

        self.log.info("Profile: %s." % info.video.profile)
        if self.video_profile and info.video.profile.lower().replace(
                " ", "") not in self.video_profile:
            self.log.debug(
                "Video profile is not supported. Video stream can no longer be copied."
            )
            vcodec = self.video_codec[0]
            vprofile = self.video_profile[0]
            if self.pix_fmt:
                pix_fmt = self.pix_fmt[0]
        elif self.video_profile:
            vprofile = self.video_profile[0]
        else:
            vprofile = None

        # Audio streams
        self.log.info("Reading audio streams.")

        overrideLang = True
        for a in info.audio:
            try:
                if a.metadata['language'].strip(
                ) == "" or a.metadata['language'] is None:
                    a.metadata['language'] = 'und'
            except KeyError:
                a.metadata['language'] = 'und'
            if (a.metadata['language'] == 'und' and self.adl) or (
                    self.awl and a.metadata['language'].lower() in self.awl):
                overrideLang = False
                break

        if overrideLang:
            self.awl = None
            self.log.info(
                "No audio streams detected in any appropriate language, relaxing restrictions so there will be some audio stream present."
            )

        audio_settings = {}
        blocked_audio_languages = []
        l = 0
        for a in info.audio:
            try:
                if a.metadata['language'].strip(
                ) == "" or a.metadata['language'] is None:
                    a.metadata['language'] = 'und'
            except KeyError:
                a.metadata['language'] = 'und'

            self.log.info("Audio detected for stream #%s: %s [%s]." %
                          (a.index, a.codec, a.metadata['language']))

            if self.output_extension in valid_tagging_extensions and a.codec.lower(
            ) == 'truehd' and self.ignore_truehd and len(
                    info.audio
            ) > 1:  # Need to skip it early so that it flags the next track as default.
                self.log.info(
                    "MP4 containers do not support truehd audio, and converting it is inconsistent due to video/audio sync issues. Skipping stream %s as typically the 2nd audio track is the AC3 core of the truehd stream."
                    % a.index)
                continue

            # Set undefined language to default language if specified
            if self.adl is not None and a.metadata['language'] == 'und':
                self.log.debug(
                    "Undefined language detected, defaulting to [%s]." %
                    self.adl)
                a.metadata['language'] = self.adl

            # Proceed if no whitelist is set, or if the language is in the whitelist
            iosdata = None
            if self.awl is None or (a.metadata['language'].lower() in self.awl
                                    and a.metadata['language'].lower()
                                    not in blocked_audio_languages):
                # Create iOS friendly audio stream if the default audio stream has too many channels (iOS only likes AAC stereo)
                if self.iOS and a.audio_channels > 2:
                    iOSbitrate = 256 if (self.audio_bitrate * 2) > 256 else (
                        self.audio_bitrate * 2)

                    # Bitrate calculations/overrides
                    if self.audio_bitrate is 0:
                        self.log.debug(
                            "Attempting to set ios stream bitrate based on source stream bitrate."
                        )
                        try:
                            iOSbitrate = (
                                (a.bitrate / 1000) / a.audio_channels) * 2
                        except:
                            self.log.warning(
                                "Unable to determine iOS audio bitrate from source stream %s, defaulting to 256 per channel."
                                % a.index)
                            iOSbitrate = audio_channels * 256

                    self.log.info(
                        "Creating audio stream %s from source audio stream %s [iOS-audio]."
                        % (str(l), a.index))
                    self.log.debug("Audio codec: %s." % self.iOS[0])
                    self.log.debug("Channels: 2.")
                    self.log.debug("Filter: %s." % self.iOS_filter)
                    self.log.debug("Bitrate: %s." % iOSbitrate)
                    self.log.debug("Language: %s." % a.metadata['language'])
                    iosdata = {
                        'map': a.index,
                        'codec': self.iOS[0],
                        'channels': 2,
                        'bitrate': iOSbitrate,
                        'filter': self.iOS_filter,
                        'language': a.metadata['language'],
                        'disposition': 'none',
                    }
                    if not self.iOSLast:
                        audio_settings.update({l: iosdata})
                        l += 1
                # If the iOS audio option is enabled and the source audio channel is only stereo, the additional iOS channel will be skipped and a single AAC 2.0 channel will be made regardless of codec preference to avoid multiple stereo channels
                self.log.info(
                    "Creating audio stream %s from source stream %s." %
                    (str(l), a.index))
                if self.iOS and a.audio_channels <= 2:
                    self.log.debug(
                        "Overriding default channel settings because iOS audio is enabled but the source is stereo [iOS-audio]."
                    )
                    acodec = 'copy' if a.codec in self.iOS else self.iOS[0]
                    audio_channels = a.audio_channels
                    afilter = self.iOS_filter
                    abitrate = a.audio_channels * 128 if (
                        a.audio_channels * self.audio_bitrate) > (
                            a.audio_channels * 128) else (a.audio_channels *
                                                          self.audio_bitrate)
                else:
                    # If desired codec is the same as the source codec, copy to avoid quality loss
                    acodec = 'copy' if a.codec.lower(
                    ) in self.audio_codec else self.audio_codec[0]
                    # Audio channel adjustments
                    if self.maxchannels and a.audio_channels > self.maxchannels:
                        audio_channels = self.maxchannels
                        if acodec == 'copy':
                            acodec = self.audio_codec[0]
                        abitrate = self.maxchannels * self.audio_bitrate
                    else:
                        audio_channels = a.audio_channels
                        abitrate = a.audio_channels * self.audio_bitrate
                        afilter = self.audio_filter

                # Bitrate calculations/overrides
                if self.audio_bitrate is 0:
                    self.log.debug(
                        "Attempting to set bitrate based on source stream bitrate."
                    )
                    try:
                        abitrate = ((a.bitrate / 1000) /
                                    a.audio_channels) * audio_channels
                    except:
                        self.log.warning(
                            "Unable to determine audio bitrate from source stream %s, defaulting to 256 per channel."
                            % a.index)
                        abitrate = audio_channels * 256

                self.log.debug("Audio codec: %s." % acodec)
                self.log.debug("Channels: %s." % audio_channels)
                self.log.debug("Bitrate: %s." % abitrate)
                self.log.debug("Language: %s" % a.metadata['language'])
                self.log.debug("Filter: %s" % afilter)

                # If the iOSFirst option is enabled, disable the iOS option after the first audio stream is processed
                if self.iOS and self.iOSFirst:
                    self.log.debug(
                        "Not creating any additional iOS audio streams.")
                    self.iOS = False

                audio_settings.update({
                    l: {
                        'map': a.index,
                        'codec': acodec,
                        'channels': audio_channels,
                        'bitrate': abitrate,
                        'filter': afilter,
                        'language': a.metadata['language'],
                        'disposition': 'none',
                    }
                })

                if acodec == 'copy' and a.codec == 'aac' and self.aac_adtstoasc:
                    audio_settings[l]['bsf'] = 'aac_adtstoasc'
                l += 1

                # Add the iOS track last instead
                if self.iOSLast and iosdata:
                    audio_settings.update({l: iosdata})
                    l += 1

                if self.audio_copyoriginal and acodec != 'copy':
                    self.log.info(
                        "Adding copy of original audio track in format %s" %
                        a.codec)
                    audio_settings.update({
                        l: {
                            'map': a.index,
                            'codec': 'copy',
                            'language': a.metadata['language'],
                            'disposition': 'none',
                        }
                    })

                # Remove the language if we only want the first track from a given language
                if self.audio_first_language_track and self.awl:
                    try:
                        blocked_audio_languages.append(
                            a.metadata['language'].lower())
                        self.log.debug(
                            "Removing language from whitelist to prevent multiple tracks of the same: %s."
                            % a.metadata['language'])
                    except:
                        self.log.error(
                            "Unable to remove language %s from whitelist." %
                            a.metadata['language'])

        # Audio Default
        if len(audio_settings) > 0 and self.adl:
            try:
                default_track = [
                    x for x in audio_settings.values()
                    if x['language'] == self.adl
                ][0]
                default_track['disposition'] = 'default'
            except:
                audio_settings[0]['disposition'] = 'default'
        else:
            self.log.error("Audio language array is empty.")

        # Subtitle streams
        subtitle_settings = {}
        l = 0
        self.log.info("Reading subtitle streams.")
        for s in info.subtitle:
            try:
                if s.metadata['language'].strip(
                ) == "" or s.metadata['language'] is None:
                    s.metadata['language'] = 'und'
            except KeyError:
                s.metadata['language'] = 'und'

            self.log.info("Subtitle detected for stream #%s: %s [%s]." %
                          (s.index, s.codec, s.metadata['language']))

            # Set undefined language to default language if specified
            if self.sdl is not None and s.metadata['language'] == 'und':
                self.log.debug(
                    "Undefined language detected, defaulting to [%s]." %
                    self.sdl)
                s.metadata['language'] = self.sdl
            # Make sure its not an image based codec
            if self.embedsubs and s.codec.lower(
            ) not in self.bad_internal_subtitle_codecs:

                # Proceed if no whitelist is set, or if the language is in the whitelist
                if self.swl is None or s.metadata['language'].lower(
                ) in self.swl:
                    subtitle_settings.update({
                        l: {
                            'map': s.index,
                            'codec': self.scodec[0],
                            'language': s.metadata['language'],
                            'encoding': self.subencoding,
                            'disposition': 'none',
                            # 'forced': s.sub_forced,
                            # 'default': s.sub_default
                        }
                    })
                    self.log.info(
                        "Creating subtitle stream %s from source stream %s." %
                        (l, s.index))
                    l = l + 1
            elif not self.embedsubs and s.codec.lower(
            ) not in self.bad_external_subtitle_codecs:
                if self.swl is None or s.metadata['language'].lower(
                ) in self.swl:

                    for codec in self.scodec:
                        ripsub = {
                            0: {
                                'map': s.index,
                                'codec': codec,
                                'language': s.metadata['language']
                            }
                        }
                        options = {
                            'format': codec,
                            'subtitle': ripsub,
                        }

                        try:
                            extension = subtitle_codec_extensions[codec]
                        except:
                            self.log.info(
                                "Wasn't able to determine subtitle file extension, defaulting to '.srt'."
                            )
                            extension = 'srt'

                        forced = ".forced" if s.sub_forced else ""

                        input_dir, filename, input_extension = self.parseFile(
                            inputfile)
                        output_dir = input_dir if self.output_dir is None else self.output_dir
                        outputfile = os.path.join(
                            output_dir, filename + "." +
                            s.metadata['language'] + forced + "." + extension)

                        i = 2
                        while os.path.isfile(outputfile):
                            self.log.debug(
                                "%s exists, appending %s to filename." %
                                (outputfile, i))
                            outputfile = os.path.join(
                                output_dir,
                                filename + "." + s.metadata['language'] +
                                forced + "." + str(i) + "." + extension)
                            i += 1
                        try:
                            self.log.info(
                                "Ripping %s subtitle from source stream %s into external file."
                                % (s.metadata['language'], s.index))
                            conv = Converter(self.FFMPEG_PATH,
                                             self.FFPROBE_PATH).convert(
                                                 inputfile,
                                                 outputfile,
                                                 options,
                                                 timeout=None)
                            for timecode in conv:
                                pass

                            self.log.info("%s created." % outputfile)
                        except FFMpegConvertError:
                            self.log.error(
                                "Unabled to create external %s subtitle file for stream %s, may be an incompatible format."
                                % (extension, s.index))
                            self.removeFile(outputfile)
                            continue
                        except:
                            self.log.exception(
                                "Unabled to create external subtitle file for stream %s."
                                % (s.index))

                        try:
                            os.chmod(outputfile, self.permissions
                                     )  # Set permissions of newly created file
                        except:
                            self.log.exception(
                                "Unable to set new file permissions.")

        # Attempt to download subtitles if they are missing using subliminal
        languages = set()
        try:
            if self.swl:
                for alpha3 in self.swl:
                    languages.add(Language(alpha3))
            elif self.sdl:
                languages.add(Language(self.sdl))
            else:
                self.downloadsubs = False
                self.log.error(
                    "No valid subtitle language specified, cannot download subtitles."
                )
        except:
            self.log.exception(
                "Unable to verify subtitle languages for download.")
            self.downloadsubs = False

        if self.downloadsubs:
            import subliminal
            self.log.info("Attempting to download subtitles.")

            # Attempt to set the dogpile cache
            try:
                subliminal.region.configure('dogpile.cache.memory')
            except:
                pass

            try:
                video = subliminal.scan_video(os.path.abspath(inputfile),
                                              subtitles=True,
                                              embedded_subtitles=True)
                subtitles = subliminal.download_best_subtitles(
                    [video],
                    languages,
                    hearing_impaired=False,
                    providers=self.subproviders)
                try:
                    subliminal.save_subtitles(video, subtitles[video])
                except:
                    # Support for older versions of subliminal
                    subliminal.save_subtitles(subtitles)
                    self.log.info(
                        "Please update to the latest version of subliminal.")
            except Exception as e:
                self.log.info("Unable to download subtitles.", exc_info=True)
                self.log.debug("Unable to download subtitles.", exc_info=True)
        # External subtitle import
        if self.embedsubs and not self.embedonlyinternalsubs:  # Don't bother if we're not embeddeding subtitles and external subtitles
            src = 1  # FFMPEG input source number
            for dirName, subdirList, fileList in os.walk(input_dir):
                for fname in fileList:
                    subname, subextension = os.path.splitext(fname)
                    # Watch for appropriate file extension
                    if subextension[1:] in valid_subtitle_extensions:
                        x, lang = os.path.splitext(subname)
                        lang = lang[1:]
                        # Using bablefish to convert a 2 language code to a 3 language code
                        if len(lang) is 2:
                            try:
                                babel = Language.fromalpha2(lang)
                                lang = babel.alpha3
                            except:
                                pass
                        # If subtitle file name and input video name are the same, proceed
                        if x == filename:
                            self.log.info(
                                "External %s subtitle file detected." % lang)
                            if self.swl is None or lang in self.swl:

                                self.log.info(
                                    "Creating subtitle stream %s by importing %s."
                                    % (l, fname))

                                subtitle_settings.update({
                                    l: {
                                        'path': os.path.join(dirName, fname),
                                        'source': src,
                                        'map': 0,
                                        'codec': 'mov_text',
                                        'disposition': 'none',
                                        'language': lang
                                    }
                                })

                                self.log.debug("Path: %s." %
                                               os.path.join(dirName, fname))
                                self.log.debug("Source: %s." % src)
                                self.log.debug("Codec: mov_text.")
                                self.log.debug("Langauge: %s." % lang)

                                l = l + 1
                                src = src + 1

                                self.deletesubs.add(
                                    os.path.join(dirName, fname))

                            else:
                                self.log.info(
                                    "Ignoring %s external subtitle stream due to language %s."
                                    % (fname, lang))

        # Subtitle Default
        if len(subtitle_settings) > 0 and self.sdl:
            try:
                default_track = [
                    x for x in subtitle_settings.values()
                    if x['language'] == self.sdl
                ][0]
                default_track['disposition'] = 'default'
            except:
                subtitle_settings[0]['disposition'] = 'default'
        else:
            self.log.warning("Subtitle stream array is empty.")

        # Collect all options
        options = {
            'format': self.output_format,
            'video': {
                'codec': vcodec,
                'map': info.video.index,
                'bitrate': vbitrate,
                'level': self.h264_level,
                'profile': vprofile,
                'pix_fmt': pix_fmt
            },
            'audio': audio_settings,
            'subtitle': subtitle_settings,
            'preopts': [],
            'postopts': ['-threads', self.threads]
        }

        # If a CRF option is set, override the determine bitrate
        if self.vcrf:
            del options['video']['bitrate']
            options['video']['crf'] = self.vcrf

        if len(options['subtitle']) > 0:
            options['preopts'].append('-fix_sub_duration')

        if self.preopts:
            options['preopts'].extend(self.preopts)

        if self.postopts:
            options['postopts'].extend(self.postopts)

        if self.dxva2_decoder:  # DXVA2 will fallback to CPU decoding when it hits a file that it cannot handle, so we don't need to check if the file is supported.
            options['preopts'].extend(['-hwaccel', 'dxva2'])
        elif info.video.codec.lower() == "hevc" and self.hevc_qsv_decoder:
            options['preopts'].extend(['-vcodec', 'hevc_qsv'])
        elif vcodec == "h264qsv" and info.video.codec.lower(
        ) == "h264" and self.qsv_decoder and (info.video.video_level / 10) < 5:
            options['preopts'].extend(['-vcodec', 'h264_qsv'])

        # Add width option
        if vwidth:
            options['video']['width'] = vwidth

        # HEVC Tagging for copied streams
        if info.video.codec.lower() in ['x265', 'h265', 'hevc'
                                        ] and vcodec == 'copy':
            options['postopts'].extend(['-tag:v', 'hvc1'])
            self.log.info("Tagging copied video stream as hvc1")

        self.options = options
        return options
Example #56
0
 def test_language_hasattr(self):
     self.assertTrue(hasattr(Language('fra'), 'alpha3'))
     self.assertTrue(hasattr(Language('fra'), 'alpha2'))
     self.assertFalse(hasattr(Language('bej'), 'alpha2'))
Example #57
0
    def query(self, title, season=None, episode=None):
        # search for the url title
        url_titles = self._search_url_titles(title)

        # episode
        if season and episode:
            if 'series' not in url_titles:
                logger.error('No URL title found for series %r', title)
                return []
            url_title = url_titles['series'][0]
            logger.debug('Using series title %r', url_title)
            url = self.server_url + 'cinemast/data/series/sb/{}/{}/{}/'.format(
                url_title, season, episode)
            page_link = self.server_url + 'subtitle/series/{}/{}/{}/'.format(
                url_title, season, episode)
        else:
            if 'movie' not in url_titles:
                logger.error('No URL title found for movie %r', title)
                return []
            url_title = url_titles['movie'][0]
            logger.debug('Using movie title %r', url_title)
            url = self.server_url + 'cinemast/data/movie/sb/{}/'.format(
                url_title)
            page_link = self.server_url + 'subtitle/movie/{}/'.format(
                url_title)

        # get the list of subtitles
        logger.debug('Getting the list of subtitles')
        r = self.session.get(url)
        r.raise_for_status()
        results = json.loads(r.text)

        # loop over results
        subtitles = {}
        for language_code, language_data in results.items():
            for quality_data in language_data.values():
                for quality, subtitles_data in quality_data.items():
                    for subtitle_item in subtitles_data.values():
                        # read the item
                        language = Language.fromalpha2(language_code)
                        hearing_impaired = bool(
                            subtitle_item['hearing_impaired'])
                        subtitle_id = subtitle_item['id']
                        subtitle_key = subtitle_item['key']
                        downloaded = subtitle_item['downloaded']
                        release = subtitle_item['subtitle_version']

                        # add the release and increment downloaded count if we already have the subtitle
                        if subtitle_id in subtitles:
                            logger.debug(
                                'Found additional release %r for subtitle %d',
                                release, subtitle_id)
                            bisect.insort_left(subtitles[subtitle_id].releases,
                                               release)  # deterministic order
                            subtitles[subtitle_id].downloaded += downloaded
                            continue

                        # otherwise create it
                        subtitle = SubsCenterSubtitle(
                            language, hearing_impaired, page_link, title,
                            season, episode, title, subtitle_id, subtitle_key,
                            downloaded, [release])
                        logger.debug('Found subtitle %r', subtitle)
                        subtitles[subtitle_id] = subtitle

        return subtitles.values()
Example #58
0
 def test_country(self):
     self.assertEqual(Language('por', 'BR').country, Country('BR'))
     self.assertEqual(Language('eng', Country('US')).country, Country('US'))
Example #59
0
 def test_converter_scope(self):
     self.assertEqual(language_converters['scope'].codes,
                      set(['I', 'S', 'M']))
     self.assertEqual(Language('eng').scope, 'individual')
     self.assertEqual(Language('und').scope, 'special')
Example #60
0
 def on_task_output(self, task, config):
     """
     Configuration::
         subliminal:
             languages: List of languages (as IETF codes) in order of preference. At least one is required.
             alternatives: List of second-choice languages; subs will be downloaded but entries rejected.
             exact_match: Use file hash only to search for subs, otherwise Subliminal will try to guess by filename.
             providers: List of providers from where to download subtitles.
             single: Download subtitles in single mode (no language code added to subtitle filename).
     """
     if not task.accepted:
         log.debug('nothing accepted, aborting')
         return
     from babelfish import Language
     from dogpile.cache.exception import RegionAlreadyConfigured
     import subliminal
     try:
         subliminal.region.configure('dogpile.cache.dbm',
                                     arguments={
                                         'filename':
                                         os.path.join(
                                             tempfile.gettempdir(),
                                             'cachefile.dbm'),
                                         'lock_factory':
                                         subliminal.cli.MutexLock
                                     })
     except RegionAlreadyConfigured:
         pass
     logging.getLogger("subliminal").setLevel(logging.CRITICAL)
     logging.getLogger("enzyme").setLevel(logging.WARNING)
     langs = set(
         [Language.fromietf(s) for s in config.get('languages', [])])
     alts = set(
         [Language.fromietf(s) for s in config.get('alternatives', [])])
     # keep all downloaded subtitles and save to disk when done (no need to write every time)
     downloaded_subtitles = collections.defaultdict(list)
     providers_list = config.get('providers', None)
     # test if only one language was provided, if so we will download in single mode
     # (aka no language code added to subtitle filename)
     # unless we are forced not to by configuration
     # if we pass 'yes' for single in configuration but choose more than one language
     # we ignore the configuration and add the language code to the
     # potentially downloaded files
     single_mode = config.get('single', '') and len(langs | alts) <= 1
     for entry in task.accepted:
         if 'location' not in entry:
             log.warning(
                 'Cannot act on entries that do not represent a local file.'
             )
         elif not os.path.exists(entry['location']):
             entry.fail('file not found: %s' % entry['location'])
         elif '$RECYCLE.BIN' not in entry[
                 'location']:  # ignore deleted files in Windows shares
             try:
                 entry_langs = entry.get('subtitle_languages', [])
                 if not entry_langs:
                     entry_langs = langs
                 video = subliminal.scan_video(entry['location'])
                 if isinstance(video, subliminal.Episode):
                     title = video.series
                 else:
                     title = video.title
                 log.info('Name computed for %s was %s' %
                          (entry['location'], title))
                 msc = video.scores['hash'] if config['exact_match'] else 0
                 if entry_langs & video.subtitle_languages:
                     entry['subtitles_missing'] = set()
                     continue  # subs for preferred lang(s) already exists
                 else:
                     subtitle = subliminal.download_best_subtitles(
                         [video],
                         entry_langs,
                         providers=providers_list,
                         min_score=msc)
                     if subtitle:
                         downloaded_subtitles.update(subtitle)
                         log.info('Subtitles found for %s' %
                                  entry['location'])
                     else:
                         # TODO check performance hit -- this explicit check may be better on slower devices
                         # but subliminal already handles it for us, but it loops over all providers before stopping
                         remaining_alts = alts - video.subtitle_languages
                         if remaining_alts:
                             # only try to download for alternatives that aren't alread downloaded
                             subtitle = subliminal.download_best_subtitles(
                                 [video],
                                 remaining_alts,
                                 providers=providers_list,
                                 min_score=msc)
                         # this potentially just checks an already checked assignment bleh
                         if subtitle:
                             downloaded_subtitles.update(subtitle)
                             entry.fail(
                                 'subtitles found for a second-choice language.'
                             )
                         else:
                             entry.fail(
                                 'cannot find any subtitles for now.')
                     downloaded_languages = set([
                         Language.fromietf(unicode(l.language))
                         for l in subtitle[video]
                     ])
                     if entry_langs:
                         entry[
                             'subtitles_missing'] = entry_langs - downloaded_languages
             except Exception as err:
                 # don't want to abort the entire task for errors in a
                 # single video file or for occasional network timeouts
                 if err.args:
                     msg = unicode(err.args[0])
                 else:
                     # Subliminal errors don't always have a message, just use the name
                     msg = 'subliminal error: %s' % err.__class__.__name__
                 log.debug(msg)
                 entry.fail(msg)
     if downloaded_subtitles:
         # save subtitles to disk
         for k, v in downloaded_subtitles.iteritems():
             if v:
                 subliminal.save_subtitles(k, v, single=single_mode)