Пример #1
0
def sync_episodes(series_id=None, send_event=True):
    logging.debug('BAZARR Starting episodes sync from Sonarr.')
    apikey_sonarr = settings.sonarr.apikey

    # Get current episodes id in DB
    current_episodes_db = TableEpisodes.select(TableEpisodes.sonarrEpisodeId,
                                               TableEpisodes.path,
                                               TableEpisodes.sonarrSeriesId)\
        .where((TableEpisodes.sonarrSeriesId == series_id) if series_id else None)\
        .dicts()

    current_episodes_db_list = [
        x['sonarrEpisodeId'] for x in current_episodes_db
    ]

    current_episodes_sonarr = []
    episodes_to_update = []
    episodes_to_add = []
    altered_episodes = []

    # Get sonarrId for each series from database
    seriesIdList = get_series_from_sonarr_api(
        series_id=series_id,
        url=url_sonarr(),
        apikey_sonarr=apikey_sonarr,
    )

    series_count = len(seriesIdList)
    for i, seriesId in enumerate(seriesIdList):
        if send_event:
            show_progress(id='episodes_progress',
                          header='Syncing episodes...',
                          name=seriesId['title'],
                          value=i,
                          count=series_count)

        # Get episodes data for a series from Sonarr
        episodes = get_episodes_from_sonarr_api(
            url=url_sonarr(),
            apikey_sonarr=apikey_sonarr,
            series_id=seriesId['sonarrSeriesId'])
        if not episodes:
            continue
        else:
            # For Sonarr v3, we need to update episodes to integrate the episodeFile API endpoint results
            if not get_sonarr_info.is_legacy():
                episodeFiles = get_episodesFiles_from_sonarr_api(
                    url=url_sonarr(),
                    apikey_sonarr=apikey_sonarr,
                    series_id=seriesId['sonarrSeriesId'])
                for episode in episodes:
                    if episode['hasFile']:
                        item = [
                            x for x in episodeFiles
                            if x['id'] == episode['episodeFileId']
                        ]
                        if item:
                            episode['episodeFile'] = item[0]

            for episode in episodes:
                if 'hasFile' in episode:
                    if episode['hasFile'] is True:
                        if 'episodeFile' in episode:
                            if episode['episodeFile']['size'] > 20480:
                                # Add episodes in sonarr to current episode list
                                current_episodes_sonarr.append(episode['id'])

                                # Parse episode data
                                if episode['id'] in current_episodes_db_list:
                                    episodes_to_update.append(
                                        episodeParser(episode))
                                else:
                                    episodes_to_add.append(
                                        episodeParser(episode))

    if send_event:
        hide_progress(id='episodes_progress')

    # Remove old episodes from DB
    removed_episodes = list(
        set(current_episodes_db_list) - set(current_episodes_sonarr))

    for removed_episode in removed_episodes:
        episode_to_delete = TableEpisodes.select(TableEpisodes.sonarrSeriesId, TableEpisodes.sonarrEpisodeId)\
            .where(TableEpisodes.sonarrEpisodeId == removed_episode)\
            .dicts()\
            .get()
        TableEpisodes.delete().where(
            TableEpisodes.sonarrEpisodeId == removed_episode).execute()
        if send_event:
            event_stream(type='episode',
                         action='delete',
                         payload=episode_to_delete['sonarrEpisodeId'])

    # Update existing episodes in DB
    episode_in_db_list = []
    episodes_in_db = TableEpisodes.select(
        TableEpisodes.sonarrSeriesId, TableEpisodes.sonarrEpisodeId,
        TableEpisodes.title, TableEpisodes.path, TableEpisodes.season,
        TableEpisodes.episode, TableEpisodes.scene_name,
        TableEpisodes.monitored, TableEpisodes.format,
        TableEpisodes.resolution, TableEpisodes.video_codec,
        TableEpisodes.audio_codec, TableEpisodes.episode_file_id,
        TableEpisodes.audio_language, TableEpisodes.file_size).dicts()

    for item in episodes_in_db:
        episode_in_db_list.append(item)

    episodes_to_update_list = [
        i for i in episodes_to_update if i not in episode_in_db_list
    ]

    for updated_episode in episodes_to_update_list:
        TableEpisodes.update(updated_episode).where(
            TableEpisodes.sonarrEpisodeId ==
            updated_episode['sonarrEpisodeId']).execute()
        altered_episodes.append([
            updated_episode['sonarrEpisodeId'], updated_episode['path'],
            updated_episode['sonarrSeriesId']
        ])

    # Insert new episodes in DB
    for added_episode in episodes_to_add:
        result = TableEpisodes.insert(added_episode).on_conflict(
            action='IGNORE').execute()
        if result > 0:
            altered_episodes.append([
                added_episode['sonarrEpisodeId'], added_episode['path'],
                added_episode['monitored']
            ])
            if send_event:
                event_stream(type='episode',
                             payload=added_episode['sonarrEpisodeId'])
        else:
            logging.debug(
                'BAZARR unable to insert this episode into the database:{}'.
                format(path_mappings.path_replace(added_episode['path'])))

    # Store subtitles for added or modified episodes
    for i, altered_episode in enumerate(altered_episodes, 1):
        store_subtitles(altered_episode[1],
                        path_mappings.path_replace(altered_episode[1]))

    logging.debug('BAZARR All episodes synced from Sonarr into database.')
Пример #2
0
def parse_video_metadata(file,
                         file_size,
                         episode_file_id=None,
                         movie_file_id=None,
                         use_cache=True):
    # Define default data keys value
    data = {
        "ffprobe": {},
        "enzyme": {},
        "file_id": episode_file_id or movie_file_id,
        "file_size": file_size,
    }

    if use_cache:
        # Get the actual cache value form database
        if episode_file_id:
            cache_key = TableEpisodes.select(TableEpisodes.ffprobe_cache)\
                .where(TableEpisodes.path == path_mappings.path_replace_reverse(file))\
                .dicts()\
                .get()
        elif movie_file_id:
            cache_key = TableMovies.select(TableMovies.ffprobe_cache)\
                .where(TableMovies.path == path_mappings.path_replace_reverse_movie(file))\
                .dicts()\
                .get()
        else:
            cache_key = None

        # check if we have a value for that cache key
        try:
            # Unpickle ffprobe cache
            cached_value = pickle.loads(cache_key['ffprobe_cache'])
        except:
            pass
        else:
            # Check if file size and file id matches and if so, we return the cached value
            if cached_value['file_size'] == file_size and cached_value[
                    'file_id'] in [episode_file_id, movie_file_id]:
                return cached_value

    # if not, we retrieve the metadata from the file
    from utils import get_binary

    ffprobe_path = get_binary("ffprobe")

    # if we have ffprobe available
    if ffprobe_path:
        api.initialize({"provider": "ffmpeg", "ffmpeg": ffprobe_path})
        data["ffprobe"] = api.know(file)
    # if not, we use enzyme for mkv files
    else:
        if os.path.splitext(file)[1] == ".mkv":
            with open(file, "rb") as f:
                try:
                    mkv = enzyme.MKV(f)
                except MalformedMKVError:
                    logger.error(
                        "BAZARR cannot analyze this MKV with our built-in MKV parser, you should install "
                        "ffmpeg/ffprobe: " + file)
                else:
                    data["enzyme"] = mkv

    # we write to db the result and return the newly cached ffprobe dict
    if episode_file_id:
        TableEpisodes.update({TableEpisodes.ffprobe_cache: pickle.dumps(data, pickle.HIGHEST_PROTOCOL)})\
            .where(TableEpisodes.path == path_mappings.path_replace_reverse(file))\
            .execute()
    elif movie_file_id:
        TableMovies.update({TableEpisodes.ffprobe_cache: pickle.dumps(data, pickle.HIGHEST_PROTOCOL)})\
            .where(TableMovies.path == path_mappings.path_replace_reverse_movie(file))\
            .execute()
    return data
Пример #3
0
def store_subtitles(file):
    logging.debug('BAZARR started subtitles indexing for this file: ' + file)
    actual_subtitles = []
    if os.path.exists(file):
        if settings.general.getboolean('use_embedded_subs'):
            logging.debug("BAZARR is trying to index embedded subtitles.")
            try:
                subtitle_languages = embedded_subs_reader.list_languages(file)
                for subtitle_language, subtitle_forced, subtitle_codec in subtitle_languages:
                    try:
                        if settings.general.getboolean(
                                "ignore_pgs_subs"
                        ) and subtitle_codec == "hdmv_pgs_subtitle":
                            logging.debug(
                                "BAZARR skipping pgs sub for language: " +
                                str(alpha2_from_alpha3(subtitle_language)))
                            continue

                        if alpha2_from_alpha3(subtitle_language) is not None:
                            lang = str(alpha2_from_alpha3(subtitle_language))
                            if subtitle_forced:
                                lang = lang + ":forced"
                            logging.debug(
                                "BAZARR embedded subtitles detected: " + lang)
                            actual_subtitles.append([lang, None])
                    except:
                        logging.debug(
                            "BAZARR unable to index this unrecognized language: "
                            + subtitle_language)
                        pass
            except Exception as e:
                logging.exception(
                    "BAZARR error when trying to analyze this %s file: %s" %
                    (os.path.splitext(file)[1], file))
                pass

        brazilian_portuguese = [".pt-br", ".pob", "pb"]
        brazilian_portuguese_forced = [
            ".pt-br.forced", ".pob.forced", "pb.forced"
        ]
        try:
            dest_folder = get_subtitle_destination_folder()
            subliminal_patch.core.CUSTOM_PATHS = [dest_folder
                                                  ] if dest_folder else []
            subtitles = search_external_subtitles(
                file,
                languages=get_language_set(),
                only_one=settings.general.getboolean('single_language'))
        except Exception as e:
            logging.exception("BAZARR unable to index external subtitles.")
            pass
        else:
            for subtitle, language in subtitles.iteritems():
                subtitle_path = get_external_subtitles_path(file, subtitle)
                if str(os.path.splitext(subtitle)[0]).lower().endswith(
                        tuple(brazilian_portuguese)):
                    logging.debug("BAZARR external subtitles detected: " +
                                  "pb")
                    actual_subtitles.append(
                        [str("pb"),
                         path_replace_reverse(subtitle_path)])
                elif str(os.path.splitext(subtitle)[0]).lower().endswith(
                        tuple(brazilian_portuguese_forced)):
                    logging.debug("BAZARR external subtitles detected: " +
                                  "pb:forced")
                    actual_subtitles.append([
                        str("pb:forced"),
                        path_replace_reverse(subtitle_path)
                    ])

                elif str(language) != 'und':
                    logging.debug("BAZARR external subtitles detected: " +
                                  str(language))
                    actual_subtitles.append(
                        [str(language),
                         path_replace_reverse(subtitle_path)])
                else:
                    if os.path.splitext(subtitle)[1] != ".sub":
                        logging.debug(
                            "BAZARR falling back to file content analysis to detect language."
                        )
                        with open(
                                os.path.join(os.path.dirname(file), subtitle),
                                'r') as f:
                            text = list(islice(f, 100))
                            text = ' '.join(text)
                            encoding = UnicodeDammit(text)
                            try:
                                text = text.decode(encoding.original_encoding)
                                detected_language = langdetect.detect(text)
                            except Exception as e:
                                logging.exception(
                                    'BAZARR Error trying to detect language for this subtitles file: '
                                    + os.path.join(os.path.dirname(file),
                                                   subtitle) +
                                    ' You should try to delete this subtitles file manually and ask Bazarr to download it again.'
                                )
                            else:
                                if len(detected_language) > 0:
                                    logging.debug(
                                        "BAZARR external subtitles detected and analysis guessed this language: "
                                        + str(detected_language))
                                    actual_subtitles.append([
                                        str(detected_language),
                                        path_replace_reverse(
                                            os.path.join(
                                                os.path.dirname(file),
                                                subtitle))
                                    ])

        update_count = TableEpisodes.update({
            TableEpisodes.subtitles:
            str(actual_subtitles)
        }).where(TableEpisodes.path == path_replace_reverse(file)).execute()
        if update_count > 0:
            logging.debug("BAZARR storing those languages to DB: " +
                          str(actual_subtitles))
        else:
            logging.debug(
                "BAZARR haven't been able to update existing subtitles to DB : "
                + str(actual_subtitles))
    else:
        logging.debug(
            "BAZARR this file doesn't seems to exist or isn't accessible.")

    logging.debug('BAZARR ended subtitles indexing for this file: ' + file)

    return actual_subtitles
Пример #4
0
def sync_one_episode(episode_id):
    logging.debug(
        'BAZARR syncing this specific episode from Sonarr: {}'.format(
            episode_id))
    url = url_sonarr()
    apikey_sonarr = settings.sonarr.apikey

    # Check if there's a row in database for this episode ID
    try:
        existing_episode = TableEpisodes.select(TableEpisodes.path, TableEpisodes.episode_file_id)\
            .where(TableEpisodes.sonarrEpisodeId == episode_id)\
            .dicts()\
            .get()
    except DoesNotExist:
        existing_episode = None

    try:
        # Get episode data from sonarr api
        episode = None
        episode_data = get_episodes_from_sonarr_api(
            url=url, apikey_sonarr=apikey_sonarr, episode_id=episode_id)
        if not episode_data:
            return

        else:
            # For Sonarr v3, we need to update episodes to integrate the episodeFile API endpoint results
            if not get_sonarr_info.is_legacy(
            ) and existing_episode and episode_data['hasFile']:
                episode_data['episodeFile'] = \
                    get_episodesFiles_from_sonarr_api(url=url, apikey_sonarr=apikey_sonarr,
                                                      episode_file_id=existing_episode['episode_file_id'])
            episode = episodeParser(episode_data)
    except Exception:
        logging.debug(
            'BAZARR cannot get episode returned by SignalR feed from Sonarr API.'
        )
        return

    # Drop useless events
    if not episode and not existing_episode:
        return

    # Remove episode from DB
    if not episode and existing_episode:
        TableEpisodes.delete().where(
            TableEpisodes.sonarrEpisodeId == episode_id).execute()
        event_stream(type='episode', action='delete', payload=int(episode_id))
        logging.debug(
            'BAZARR deleted this episode from the database:{}'.format(
                path_mappings.path_replace(existing_episode['path'])))
        return

    # Update existing episodes in DB
    elif episode and existing_episode:
        TableEpisodes.update(episode).where(
            TableEpisodes.sonarrEpisodeId == episode_id).execute()
        event_stream(type='episode', action='update', payload=int(episode_id))
        logging.debug(
            'BAZARR updated this episode into the database:{}'.format(
                path_mappings.path_replace(episode['path'])))

    # Insert new episodes in DB
    elif episode and not existing_episode:
        TableEpisodes.insert(episode).on_conflict(action='IGNORE').execute()
        event_stream(type='episode', action='update', payload=int(episode_id))
        logging.debug(
            'BAZARR inserted this episode into the database:{}'.format(
                path_mappings.path_replace(episode['path'])))

    # Storing existing subtitles
    logging.debug('BAZARR storing subtitles for this episode: {}'.format(
        path_mappings.path_replace(episode['path'])))
    store_subtitles(episode['path'],
                    path_mappings.path_replace(episode['path']))

    # Downloading missing subtitles
    logging.debug(
        'BAZARR downloading missing subtitles for this episode: {}'.format(
            path_mappings.path_replace(episode['path'])))
    episode_download_subtitles(episode_id)
Пример #5
0
def list_missing_subtitles(no=None):
    episodes_subtitles_clause = {TableShows.sonarr_series_id.is_null(False)}
    if no is not None:
        episodes_subtitles_clause = {TableShows.sonarr_series_id**no}

    episodes_subtitles = TableEpisodes.select(
        TableEpisodes.sonarr_episode_id, TableEpisodes.subtitles,
        TableShows.languages, TableShows.forced).join_from(
            TableEpisodes, TableShows,
            JOIN.LEFT_OUTER).where(episodes_subtitles_clause).objects()

    missing_subtitles_global = []
    use_embedded_subs = settings.general.getboolean('use_embedded_subs')
    for episode_subtitles in episodes_subtitles:
        actual_subtitles_temp = []
        desired_subtitles_temp = []
        actual_subtitles = []
        desired_subtitles = []
        missing_subtitles = []
        if episode_subtitles.subtitles is not None:
            if use_embedded_subs:
                actual_subtitles = ast.literal_eval(
                    episode_subtitles.subtitles)
            else:
                actual_subtitles_temp = ast.literal_eval(
                    episode_subtitles.subtitles)
                for subtitle in actual_subtitles_temp:
                    if subtitle[1] is not None:
                        actual_subtitles.append(subtitle)
        if episode_subtitles.languages is not None:
            desired_subtitles = ast.literal_eval(episode_subtitles.languages)
            if episode_subtitles.forced == "True" and desired_subtitles is not None:
                for i, desired_subtitle in enumerate(desired_subtitles):
                    desired_subtitles[i] = desired_subtitle + ":forced"
            elif episode_subtitles.forced == "Both" and desired_subtitles is not None:
                for desired_subtitle in desired_subtitles:
                    desired_subtitles_temp.append(desired_subtitle)
                    desired_subtitles_temp.append(desired_subtitle + ":forced")
                desired_subtitles = desired_subtitles_temp
        actual_subtitles_list = []
        if desired_subtitles is None:
            missing_subtitles_global.append(
                tuple(['[]', episode_subtitles.sonarr_episode_id]))
        else:
            for item in actual_subtitles:
                if item[0] == "pt-BR":
                    actual_subtitles_list.append("pb")
                elif item[0] == "pt-BR:forced":
                    actual_subtitles_list.append("pb:forced")
                else:
                    actual_subtitles_list.append(item[0])
            missing_subtitles = list(
                set(desired_subtitles) - set(actual_subtitles_list))
            missing_subtitles_global.append(
                tuple([
                    str(missing_subtitles), episode_subtitles.sonarr_episode_id
                ]))

    for missing_subtitles_item in missing_subtitles_global:
        TableEpisodes.update({
            TableEpisodes.missing_subtitles:
            missing_subtitles_item[0]
        }).where(TableEpisodes.sonarr_episode_id ==
                 missing_subtitles_item[1]).execute()
Пример #6
0
def _wanted_episode(episode):
    audio_language_list = get_audio_profile_languages(episode_id=episode['sonarrEpisodeId'])
    if len(audio_language_list) > 0:
        audio_language = audio_language_list[0]['name']
    else:
        audio_language = 'None'

    languages = []
    for language in ast.literal_eval(episode['missing_subtitles']):

        # confirm if language is still missing or if cutoff have been reached
        confirmed_missing_subs = TableEpisodes.select(TableEpisodes.missing_subtitles) \
            .where(TableEpisodes.sonarrEpisodeId == episode['sonarrEpisodeId']) \
            .dicts() \
            .get_or_none()
        if not confirmed_missing_subs:
            continue

        if language not in ast.literal_eval(confirmed_missing_subs['missing_subtitles']):
            continue

        if is_search_active(desired_language=language, attempt_string=episode['failedAttempts']):
            TableEpisodes.update({TableEpisodes.failedAttempts:
                                  updateFailedAttempts(desired_language=language,
                                                       attempt_string=episode['failedAttempts'])}) \
                .where(TableEpisodes.sonarrEpisodeId == episode['sonarrEpisodeId']) \
                .execute()

            hi_ = "True" if language.endswith(':hi') else "False"
            forced_ = "True" if language.endswith(':forced') else "False"
            languages.append((language.split(":")[0], hi_, forced_))

        else:
            logging.debug(
                f"BAZARR Search is throttled by adaptive search for this episode {episode['path']} and "
                f"language: {language}")

    for result in generate_subtitles(path_mappings.path_replace(episode['path']),
                                     languages,
                                     audio_language,
                                     str(episode['scene_name']),
                                     episode['title'],
                                     'series'):
        if result:
            message = result[0]
            path = result[1]
            forced = result[5]
            if result[8]:
                language_code = result[2] + ":hi"
            elif forced:
                language_code = result[2] + ":forced"
            else:
                language_code = result[2]
            provider = result[3]
            score = result[4]
            subs_id = result[6]
            subs_path = result[7]
            store_subtitles(episode['path'], path_mappings.path_replace(episode['path']))
            history_log(1, episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message, path,
                        language_code, provider, score, subs_id, subs_path)
            event_stream(type='series', action='update', payload=episode['sonarrSeriesId'])
            event_stream(type='episode-wanted', action='delete', payload=episode['sonarrEpisodeId'])
            send_notifications(episode['sonarrSeriesId'], episode['sonarrEpisodeId'], message)
Пример #7
0
def sync_episodes():
    notifications.write(msg='Episodes sync from Sonarr started...',
                        queue='get_episodes')
    logging.debug('BAZARR Starting episodes sync from Sonarr.')
    apikey_sonarr = settings.sonarr.apikey

    # Get current episodes id in DB
    current_episodes_db = TableEpisodes.select(TableEpisodes.sonarr_episode_id,
                                               TableEpisodes.path,
                                               TableEpisodes.sonarr_series_id)

    current_episodes_db_list = [
        x.sonarr_episode_id for x in current_episodes_db
    ]

    current_episodes_sonarr = []
    episodes_to_update = []
    episodes_to_add = []
    altered_episodes = []

    # Get sonarrId for each series from database
    seriesIdList = TableShows.select(TableShows.sonarr_series_id,
                                     TableShows.title)

    seriesIdListLength = seriesIdList.count()
    for i, seriesId in enumerate(seriesIdList, 1):
        notifications.write(msg='Getting episodes data from Sonarr...',
                            queue='get_episodes',
                            item=i,
                            length=seriesIdListLength)
        # Get episodes data for a series from Sonarr
        url_sonarr_api_episode = url_sonarr + "/api/episode?seriesId=" + str(
            seriesId.sonarr_series_id) + "&apikey=" + apikey_sonarr
        try:
            r = requests.get(url_sonarr_api_episode, timeout=60, verify=False)
            r.raise_for_status()
        except requests.exceptions.HTTPError as errh:
            logging.exception(
                "BAZARR Error trying to get episodes from Sonarr. Http error.")
            return
        except requests.exceptions.ConnectionError as errc:
            logging.exception(
                "BAZARR Error trying to get episodes from Sonarr. Connection Error."
            )
            return
        except requests.exceptions.Timeout as errt:
            logging.exception(
                "BAZARR Error trying to get episodes from Sonarr. Timeout Error."
            )
            return
        except requests.exceptions.RequestException as err:
            logging.exception(
                "BAZARR Error trying to get episodes from Sonarr.")
            return
        else:
            for episode in r.json():
                if 'hasFile' in episode:
                    if episode['hasFile'] is True:
                        if 'episodeFile' in episode:
                            if episode['episodeFile']['size'] > 20480:
                                # Add shows in Sonarr to current shows list
                                if 'sceneName' in episode['episodeFile']:
                                    sceneName = episode['episodeFile'][
                                        'sceneName']
                                else:
                                    sceneName = None

                                try:
                                    format, resolution = episode[
                                        'episodeFile']['quality']['quality'][
                                            'name'].split('-')
                                except:
                                    format = episode['episodeFile']['quality'][
                                        'quality']['name']
                                    try:
                                        resolution = str(
                                            episode['episodeFile']['quality']
                                            ['quality']['resolution']) + 'p'
                                    except:
                                        resolution = None

                                if 'mediaInfo' in episode['episodeFile']:
                                    if 'videoCodec' in episode['episodeFile'][
                                            'mediaInfo']:
                                        videoCodec = episode['episodeFile'][
                                            'mediaInfo']['videoCodec']
                                        videoCodec = SonarrFormatVideoCodec(
                                            videoCodec)
                                    else:
                                        videoCodec = None

                                    if 'audioCodec' in episode['episodeFile'][
                                            'mediaInfo']:
                                        audioCodec = episode['episodeFile'][
                                            'mediaInfo']['audioCodec']
                                        audioCodec = SonarrFormatAudioCodec(
                                            audioCodec)
                                    else:
                                        audioCodec = None
                                else:
                                    videoCodec = None
                                    audioCodec = None

                                # Add episodes in sonarr to current episode list
                                current_episodes_sonarr.append(episode['id'])

                                if episode['id'] in current_episodes_db_list:
                                    episodes_to_update.append({
                                        'sonarr_series_id':
                                        episode['seriesId'],
                                        'sonarr_episode_id':
                                        episode['id'],
                                        'title':
                                        episode['title'],
                                        'path':
                                        episode['episodeFile']['path'],
                                        'season':
                                        episode['seasonNumber'],
                                        'episode':
                                        episode['episodeNumber'],
                                        'scene_name':
                                        sceneName,
                                        'monitored':
                                        str(bool(episode['monitored'])),
                                        'format':
                                        format,
                                        'resolution':
                                        resolution,
                                        'video_codec':
                                        videoCodec,
                                        'audio_codec':
                                        audioCodec,
                                        'episode_file_id':
                                        episode['episodeFile']['id']
                                    })
                                else:
                                    episodes_to_add.append({
                                        'sonarr_series_id':
                                        episode['seriesId'],
                                        'sonarr_episode_id':
                                        episode['id'],
                                        'title':
                                        episode['title'],
                                        'path':
                                        episode['episodeFile']['path'],
                                        'season':
                                        episode['seasonNumber'],
                                        'episode':
                                        episode['episodeNumber'],
                                        'scene_name':
                                        sceneName,
                                        'monitored':
                                        str(bool(episode['monitored'])),
                                        'format':
                                        format,
                                        'resolution':
                                        resolution,
                                        'video_codec':
                                        videoCodec,
                                        'audio_codec':
                                        audioCodec,
                                        'episode_file_id':
                                        episode['episodeFile']['id']
                                    })

    # Update existing episodes in DB
    episode_in_db_list = []
    episodes_in_db = TableEpisodes.select(
        TableEpisodes.sonarr_series_id, TableEpisodes.sonarr_episode_id,
        TableEpisodes.title, TableEpisodes.path, TableEpisodes.season,
        TableEpisodes.episode, TableEpisodes.scene_name,
        TableEpisodes.monitored, TableEpisodes.format,
        TableEpisodes.resolution, TableEpisodes.video_codec,
        TableEpisodes.audio_codec, TableEpisodes.episode_file_id).dicts()

    for item in episodes_in_db:
        episode_in_db_list.append(item)

    episodes_to_update_list = [
        i for i in episodes_to_update if i not in episode_in_db_list
    ]

    for updated_episode in episodes_to_update_list:
        TableEpisodes.update(updated_episode).where(
            TableEpisodes.sonarr_episode_id ==
            updated_episode['sonarr_episode_id']).execute()
        altered_episodes.append([
            updated_episode['sonarr_episode_id'], updated_episode['path'],
            updated_episode['sonarr_series_id']
        ])

    # Insert new episodes in DB
    for added_episode in episodes_to_add:
        TableEpisodes.insert(added_episode).on_conflict_ignore().execute()
        altered_episodes.append([
            added_episode['sonarr_episode_id'], added_episode['path'],
            added_episode['sonarr_series_id']
        ])

    # Remove old episodes from DB
    removed_episodes = list(
        set(current_episodes_db_list) - set(current_episodes_sonarr))

    for removed_episode in removed_episodes:
        TableEpisodes.delete().where(
            TableEpisodes.sonarr_episode_id == removed_episode).execute()

    # Store subtitles for added or modified episodes
    for i, altered_episode in enumerate(altered_episodes, 1):
        notifications.write(msg='Indexing episodes embedded subtitles...',
                            queue='get_episodes',
                            item=i,
                            length=len(altered_episodes))
        store_subtitles(path_replace(altered_episode[1]))
        list_missing_subtitles(altered_episode[2])

    logging.debug('BAZARR All episodes synced from Sonarr into database.')

    # Search for desired subtitles if no more than 5 episodes have been added.
    if len(altered_episodes) <= 5:
        logging.debug(
            "BAZARR No more than 5 episodes were added during this sync then we'll search for subtitles."
        )
        for altered_episode in altered_episodes:
            episode_download_subtitles(altered_episode[0])
    else:
        logging.debug(
            "BAZARR More than 5 episodes were added during this sync then we wont search for subtitles right now."
        )
Пример #8
0
def store_subtitles(original_path, reversed_path, use_cache=True):
    logging.debug('BAZARR started subtitles indexing for this file: ' + reversed_path)
    actual_subtitles = []
    if os.path.exists(reversed_path):
        if settings.general.getboolean('use_embedded_subs'):
            logging.debug("BAZARR is trying to index embedded subtitles.")
            item = TableEpisodes.select(TableEpisodes.episode_file_id, TableEpisodes.file_size)\
                .where(TableEpisodes.path == original_path)\
                .dicts()\
                .get_or_none()
            if not item:
                logging.exception(f"BAZARR error when trying to select this episode from database: {reversed_path}")
            else:
                try:
                    subtitle_languages = embedded_subs_reader(reversed_path,
                                                              file_size=item['file_size'],
                                                              episode_file_id=item['episode_file_id'],
                                                              use_cache=use_cache)
                    for subtitle_language, subtitle_forced, subtitle_hi, subtitle_codec in subtitle_languages:
                        try:
                            if (settings.general.getboolean("ignore_pgs_subs") and subtitle_codec.lower() == "pgs") or \
                                    (settings.general.getboolean("ignore_vobsub_subs") and subtitle_codec.lower() ==
                                     "vobsub") or \
                                    (settings.general.getboolean("ignore_ass_subs") and subtitle_codec.lower() ==
                                     "ass"):
                                logging.debug("BAZARR skipping %s sub for language: %s" % (subtitle_codec, alpha2_from_alpha3(subtitle_language)))
                                continue

                            if alpha2_from_alpha3(subtitle_language) is not None:
                                lang = str(alpha2_from_alpha3(subtitle_language))
                                if subtitle_forced:
                                    lang = lang + ":forced"
                                if subtitle_hi:
                                    lang = lang + ":hi"
                                logging.debug("BAZARR embedded subtitles detected: " + lang)
                                actual_subtitles.append([lang, None])
                        except Exception as error:
                            logging.debug("BAZARR unable to index this unrecognized language: %s (%s)", subtitle_language, error)
                except Exception:
                    logging.exception(
                        "BAZARR error when trying to analyze this %s file: %s" % (os.path.splitext(reversed_path)[1],
                                                                                  reversed_path))
                    pass
        try:
            dest_folder = get_subtitle_destination_folder()
            core.CUSTOM_PATHS = [dest_folder] if dest_folder else []
            subtitles = search_external_subtitles(reversed_path, languages=get_language_set(),
                                                  only_one=settings.general.getboolean('single_language'))
            full_dest_folder_path = os.path.dirname(reversed_path)
            if dest_folder:
                if settings.general.subfolder == "absolute":
                    full_dest_folder_path = dest_folder
                elif settings.general.subfolder == "relative":
                    full_dest_folder_path = os.path.join(os.path.dirname(reversed_path), dest_folder)
            subtitles = guess_external_subtitles(full_dest_folder_path, subtitles)
        except Exception:
            logging.exception("BAZARR unable to index external subtitles.")
        else:
            for subtitle, language in subtitles.items():
                valid_language = False
                if language:
                    if hasattr(language, 'alpha3'):
                        valid_language = alpha2_from_alpha3(language.alpha3)
                else:
                    logging.debug(f"Skipping subtitles because we are unable to define language: {subtitle}")
                    continue

                if not valid_language:
                    logging.debug(f'{language.alpha3} is an unsupported language code.')
                    continue

                subtitle_path = get_external_subtitles_path(reversed_path, subtitle)

                custom = CustomLanguage.found_external(subtitle, subtitle_path)
                if custom is not None:
                    actual_subtitles.append([custom, path_mappings.path_replace_reverse(subtitle_path)])

                elif str(language) != 'und':
                    if language.forced:
                        language_str = str(language)
                    elif language.hi:
                        language_str = str(language) + ':hi'
                    else:
                        language_str = str(language)
                    logging.debug("BAZARR external subtitles detected: " + language_str)
                    actual_subtitles.append([language_str, path_mappings.path_replace_reverse(subtitle_path)])

        TableEpisodes.update({TableEpisodes.subtitles: str(actual_subtitles)})\
            .where(TableEpisodes.path == original_path)\
            .execute()
        matching_episodes = TableEpisodes.select(TableEpisodes.sonarrEpisodeId, TableEpisodes.sonarrSeriesId)\
            .where(TableEpisodes.path == original_path)\
            .dicts()

        for episode in matching_episodes:
            if episode:
                logging.debug("BAZARR storing those languages to DB: " + str(actual_subtitles))
                list_missing_subtitles(epno=episode['sonarrEpisodeId'])
            else:
                logging.debug("BAZARR haven't been able to update existing subtitles to DB : " + str(actual_subtitles))
    else:
        logging.debug("BAZARR this file doesn't seems to exist or isn't accessible.")

    logging.debug('BAZARR ended subtitles indexing for this file: ' + reversed_path)

    return actual_subtitles
Пример #9
0
def list_missing_subtitles(no=None, epno=None, send_event=True):
    if epno is not None:
        episodes_subtitles_clause = (TableEpisodes.sonarrEpisodeId == epno)
    elif no is not None:
        episodes_subtitles_clause = (TableEpisodes.sonarrSeriesId == no)
    else:
        episodes_subtitles_clause = None
    episodes_subtitles = TableEpisodes.select(TableShows.sonarrSeriesId,
                                              TableEpisodes.sonarrEpisodeId,
                                              TableEpisodes.subtitles,
                                              TableShows.profileId,
                                              TableEpisodes.audio_language)\
        .join(TableShows, on=(TableEpisodes.sonarrSeriesId == TableShows.sonarrSeriesId))\
        .where(episodes_subtitles_clause)\
        .dicts()
    if isinstance(episodes_subtitles, str):
        logging.error("BAZARR list missing subtitles query to DB returned this instead of rows: " + episodes_subtitles)
        return

    use_embedded_subs = settings.general.getboolean('use_embedded_subs')

    for episode_subtitles in episodes_subtitles:
        missing_subtitles_text = '[]'
        if episode_subtitles['profileId']:
            # get desired subtitles
            desired_subtitles_temp = get_profiles_list(profile_id=episode_subtitles['profileId'])
            desired_subtitles_list = []
            if desired_subtitles_temp:
                for language in desired_subtitles_temp['items']:
                    if language['audio_exclude'] == "True":
                        if language_from_alpha2(language['language']) in ast.literal_eval(
                                episode_subtitles['audio_language']):
                            continue
                    desired_subtitles_list.append([language['language'], language['forced'], language['hi']])

            # get existing subtitles
            actual_subtitles_list = []
            if episode_subtitles['subtitles'] is not None:
                if use_embedded_subs:
                    actual_subtitles_temp = ast.literal_eval(episode_subtitles['subtitles'])
                else:
                    actual_subtitles_temp = [x for x in ast.literal_eval(episode_subtitles['subtitles']) if x[1]]

                for subtitles in actual_subtitles_temp:
                    subtitles = subtitles[0].split(':')
                    lang = subtitles[0]
                    forced = False
                    hi = False
                    if len(subtitles) > 1:
                        if subtitles[1] == 'forced':
                            forced = True
                            hi = False
                        elif subtitles[1] == 'hi':
                            forced = False
                            hi = True
                    actual_subtitles_list.append([lang, str(forced), str(hi)])

            # check if cutoff is reached and skip any further check
            cutoff_met = False
            cutoff_temp_list = get_profile_cutoff(profile_id=episode_subtitles['profileId'])

            if cutoff_temp_list:
                for cutoff_temp in cutoff_temp_list:
                    cutoff_language = [cutoff_temp['language'], cutoff_temp['forced'], cutoff_temp['hi']]
                    if cutoff_temp['audio_exclude'] == 'True' and language_from_alpha2(cutoff_temp['language']) in \
                            ast.literal_eval(episode_subtitles['audio_language']):
                        cutoff_met = True
                    elif cutoff_language in actual_subtitles_list:
                        cutoff_met = True
                    elif cutoff_language and [cutoff_language[0], 'True', 'False'] in actual_subtitles_list:
                        cutoff_met = True
                    elif cutoff_language and [cutoff_language[0], 'False', 'True'] in actual_subtitles_list:
                        cutoff_met = True

            if cutoff_met:
                missing_subtitles_text = str([])
            else:
                # if cutoff isn't met or None, we continue

                # get difference between desired and existing subtitles
                missing_subtitles_list = []
                for item in desired_subtitles_list:
                    if item not in actual_subtitles_list:
                        missing_subtitles_list.append(item)

                # remove missing that have hi subtitles for this language in existing
                for item in actual_subtitles_list:
                    if item[2] == 'True':
                        try:
                            missing_subtitles_list.remove([item[0], 'False', 'False'])
                        except ValueError:
                            pass

                # make the missing languages list looks like expected
                missing_subtitles_output_list = []
                for item in missing_subtitles_list:
                    lang = item[0]
                    if item[1] == 'True':
                        lang += ':forced'
                    elif item[2] == 'True':
                        lang += ':hi'
                    missing_subtitles_output_list.append(lang)

                missing_subtitles_text = str(missing_subtitles_output_list)

        TableEpisodes.update({TableEpisodes.missing_subtitles: missing_subtitles_text})\
            .where(TableEpisodes.sonarrEpisodeId == episode_subtitles['sonarrEpisodeId'])\
            .execute()

        if send_event:
            event_stream(type='episode', payload=episode_subtitles['sonarrEpisodeId'])
            event_stream(type='badges')