Ejemplo n.º 1
0
def download_subtitle(episode):
    import srt
    episode.reload()
    LOG.debug('Downloading subtitle from PMS')
    pms = episode._server
    to_dl = []
    all_subs = []

    for part in episode.iterParts():
        if part.subtitleStreams():
            for sub in part.subtitleStreams():
                if sub.key and sub.codec == 'srt':
                    to_dl.append(
                        pms.url('%s?download=1' % sub.key, includeToken=True))

    for dl_url in to_dl:
        r = requests.get(dl_url)
        r.raise_for_status()
        if r:
            try:
                a_sub = list(srt.parse(r.text))
                all_subs.append(a_sub)
            except ValueError:
                LOG.exception('Failed to parse subtitle')

    return all_subs
Ejemplo n.º 2
0
def download_subtitle(episode):

    episode.reload()
    LOG.debug('Downloading subtitle from PMS')
    pms = episode._server
    to_dl = []
    all_subs = []

    for part in episode.iterParts():
        if part.subtitleStreams():
            for sub in part.subtitleStreams():
                if sub.key and sub.codec in FILE_EXTENSION_TO_FORMAT_IDENTIFIER.values(
                ):
                    to_dl.append(
                        pms.url('%s?download=1' % sub.key, includeToken=True))

    for dl_url in to_dl:
        r = episode._server._session.get(dl_url)
        r.raise_for_status()
        if r:
            try:
                subt = [
                    sub
                    for sub in SSAFile.from_string(r.text, encoding=r.encoding)
                ]
                all_subs.append(subt)
            except (IOError, pysubs2.exceptions.UnknownFPSError,
                    pysubs2.exceptions.UnknownFormatIdentifierError,
                    pysubs2.exceptions.FormatAutodetectionError):
                LOG.exception('Failed to parse subtitle')

    return all_subs
Ejemplo n.º 3
0
 def get_themes(self):
     d = defaultdict(list)
     for n in self.names:
         try:
             rk = os.path.basename(n).split('__')[1]
             d[int(rk)].append(n)
         except (IndexError, TypeError):
             LOG.exception('Some crap happend with', n)
     return d
Ejemplo n.º 4
0
 def inner(*args, **kwargs):
     try:
         if kwargs:
             return func(*args, **kwargs)
         else:
             return func(*args)
     except:
         err = "There was an exception in "
         err += func.__name__
         LOG.exception(err)
         raise
Ejemplo n.º 5
0
def convert_and_trim(afile, fs=8000, trim=None, theme=False, filename=None):
    tmp = tempfile.NamedTemporaryFile(mode='r+b',
                                      prefix='offset_',
                                      suffix='.wav')

    tmp_name = tmp.name
    tmp.close()
    tmp_name = "%s" % tmp_name

    if os.name == 'nt' and '://' not in afile:
        q_file = '"%s"' % afile
    else:
        q_file = afile

    if trim is None:
        cmd = [
            'ffmpeg', '-i', q_file, '-ac', '1', '-ar',
            str(fs), '-acodec', 'pcm_s16le', tmp_name
        ]

    else:
        cmd = [
            'ffmpeg', '-i', q_file, '-ac', '1', '-ar',
            str(fs), '-ss', '0', '-t',
            str(trim), '-acodec', 'pcm_s16le', tmp_name
        ]

    LOG.debug('calling ffmpeg with %s' % ' '.join(cmd))

    if os.name == 'nt':
        cmd = '%s' % ' '.join(cmd)

    psox = subprocess.Popen(cmd, stderr=subprocess.PIPE)
    o, e = psox.communicate()

    if not psox.returncode == 0:  # pragma: no cover
        LOG.exception(e)
        raise Exception("FFMpeg failed")

    # Check if we passed a url.
    if '://' in afile and filename:
        filename = filename + '.wav'
        afile = os.path.join(THEMES, filename)

    if theme:
        shutil.move(tmp_name, afile)
        LOG.debug('Done converted and moved %s to %s' % (afile, THEMES))
        return afile
    else:
        LOG.debug('Done converting %s', tmp_name)
        return tmp_name
Ejemplo n.º 6
0
def create_edl_from_db(t, save_path):
    with session_scope() as se:
        db_items = se.query(Processed).all()
        for item in db_items:
            # Maybe remove this later?
            if save_path:
                loc = edl.create_edl_path(
                    os.path.join(save_path, os.path.basename(item.location)))
            else:
                loc = item.location

            try:
                t = edl.write_edl(loc, edl.db_to_edl(item, edl.TYPES[t]))
                click.echo('Wrote %s' % t)
            except:
                LOG.exception('Failed to write edl.')
Ejemplo n.º 7
0
def task(item, sessionkey):
    """Main func for processing a episode.

       Args:
            item(str): a episode's ratingkey
            sessionkey(str): streams sessionkey

       Returns:
            None
    """
    global HT
    media = PMS.fetchItem(int(item))
    LOG.debug('Found %s', media._prettyfilename())
    if media.TYPE not in ('episode', 'show', 'movie'):  # pragma: no cover
        return

    if media.TYPE == 'episode':
        LOG.debug('Download the first 10 minutes of %s as .wav',
                  media._prettyfilename())
        vid = convert_and_trim(check_file_access(media),
                               fs=11025,
                               trim=CONFIG['tv'].get('check_for_theme_sec',
                                                     600))

        process_to_db(media, vid=vid)

        try:
            os.remove(vid)
            LOG.debug('Deleted %s', vid)
        except IOError:  # pragma: no cover
            LOG.exception('Failed to delete %s', vid)

    elif media.TYPE == 'movie':
        process_to_db(media)

    try:
        IN_PROG.remove(item)
    except ValueError:  # pragma: no cover
        LOG.debug('Failed to remove %s from IN_PROG', item)

    nxt = find_next(media)
    if nxt:
        process_to_db(nxt)
Ejemplo n.º 8
0
def convert_and_trim(afile, fs=8000, trim=None, theme=False):
    tmp = tempfile.NamedTemporaryFile(mode='r+b',
                                      prefix='offset_',
                                      suffix='.wav')

    tmp_name = tmp.name
    tmp.close()
    if trim is None:
        cmd = [
            'ffmpeg', '-i', afile, '-ac', '1', '-ar',
            str(fs), '-acodec', 'pcm_s16le', tmp_name
        ]

    else:
        cmd = [
            'ffmpeg', '-i', afile, '-ac', '1', '-ar',
            str(fs), '-ss', '0', '-t',
            str(trim), '-acodec', 'pcm_s16le', tmp_name
        ]

    LOG.debug('calling ffmpeg with %s' % ' '.join(cmd))

    psox = subprocess.Popen(cmd, stderr=subprocess.PIPE)
    o, e = psox.communicate()

    if not psox.returncode == 0:
        LOG.exception(e)
        raise Exception("FFMpeg failed")

    if theme:
        shutil.move(tmp_name, afile)
        LOG.debug('Done converted and moved %s to %s' % (afile, THEMES))
        return afile
    else:
        LOG.debug('Done converting %s', tmp_name)
        return tmp_name
Ejemplo n.º 9
0
def search_for_theme_youtube(name, rk=1337, save_path=None, url=None):
    import youtube_dl

    LOG.debug('Searching youtube for name %s rk %s save_path %s url %s ' %
              (name, rk, save_path, url))

    if isinstance(name, tuple):
        name, rk, save_path = name

    if save_path is None:
        save_path = os.getcwd()

    if url and 'youtube' not in url:
        return []

    fp = os.path.join(save_path, '%s__%s__%s' % (name, rk, int(time.time())))
    fp = get_valid_filename(fp)
    # Youtuble dl requires the template to be unicode.
    t = u'%s' % fp

    ydl_opts = {
        'quiet':
        True,
        'continuedl':
        True,
        'external_downloader':
        'ffmpeg',
        #'verbose': True,
        'outtmpl':
        t + u'.%(ext)s',
        'default_search':
        'ytsearch',
        # So we select "best" here since this does not get throttled by
        # youtube. Should it be a config option for ppl with data caps?
        # Possible format could be bestaudio for those poor fuckers..
        'format':
        'best',
        'postprocessors': [{
            'key': 'FFmpegExtractAudio',
            'preferredcodec': 'wav',
            'preferredquality': '192',
        }],
        'logger':
        LOG,
    }
    # https://github.com/rg3/youtube-dl/issues/6923
    # ydl_opts['external_downloader'] = 'aria2c'
    # ydl_opts['external_downloader_args'] = []#['-x', '8', '-s', '8', '-k', '256k']

    ydl = youtube_dl.YoutubeDL(ydl_opts)

    def nothing(*args, **kwargs):
        pass

    ydl.to_screen = nothing

    name = name.replace(':', '')

    with ydl:
        try:
            if url:
                ydl.download([url])
            else:
                ydl.download([name + ' theme song'])
            return t + '.wav'

        except:  # pragma: no cover
            LOG.exception('Failed to download theme song %s' % name)

    LOG.debug('Done downloading theme for %s', name)

    return t + '.wav'
Ejemplo n.º 10
0
def client_action(offset=None,
                  sessionkey=None,
                  action='jump'):  # pragma: no cover
    """Seek the client to the offset.

       Args:
            offset(int): Default None
            sessionkey(int): So we made sure we control the correct client.

       Returns:
            None
    """
    global JUMP_LIST
    LOG.info('Called client_action with %s %s %s %s', offset, to_time(offset),
             sessionkey, action)

    def proxy_on_fail(func):
        import plexapi

        @wraps(func)
        def inner():
            try:
                func()
            except plexapi.exceptions.BadRequest:
                try:
                    LOG.info(
                        'Failed to reach the client directly, trying via server.'
                    )
                    correct_client.proxyThroughServer()
                    func()
                except:  # pragma: no cover
                    correct_client.proxyThroughServer(value=False)
                    raise

    if offset == -1:
        return

    conf_clients = CONFIG.get('general', {}).get('clients', [])
    conf_users = CONFIG.get('general', {}).get('users', [])
    correct_client = None

    clients = PMS.clients()
    for media in PMS.sessions():
        # Find the client.. This client does not have the correct address
        # or 'protocolCapabilities' so we have to get the correct one.
        # or we can proxy thru the server..
        if sessionkey and int(sessionkey) == media.sessionKey:
            client = media.players[0]
            user = media.usernames[0]
            LOG.info('client %s %s', client.title, (media.viewOffset / 1000))

            # Check that this client is allowed.
            if conf_clients and client.title not in conf_clients:
                LOG.info('Client %s is not whitelisted', client.title)
                return

            # Check that this user is allowed.
            if conf_users and user not in conf_users:
                LOG.info('User %s is not whitelisted', user)
                return

            # To stop processing. from func task if we have used to much time..
            # This will not work if/when credits etc are added. Need a better way.
            # if offset <= media.viewOffset / 1000:
            #    LOG.debug('Didnt jump because of offset')
            #    return

            for c in clients:
                LOG.info('%s %s' %
                         (c.machineIdentifier, client.machineIdentifier))
                # So we got the correct client..
                if c.machineIdentifier == client.machineIdentifier:
                    # Plex web sometimes add loopback..
                    if '127.0.0.1' in c._baseurl:
                        c._baseurl = c._baseurl.replace(
                            '127.0.0.1', client.address)
                    correct_client = c
                    break

            if correct_client:
                try:
                    LOG.info('Connectiong to %s', correct_client.title)
                    correct_client.connect()
                except requests.exceptions.ConnectionError:
                    LOG.exception('Cant connect to %s', client.title)
                    return

                if action != 'stop':
                    if ignore_ratingkey(
                            media,
                            CONFIG['general'].get('ignore_intro_ratingkeys')):
                        LOG.info(
                            'Didnt send seek command this show, season or episode is ignored'
                        )
                        return

                    # PMP seems to be really picky about timeline calls, if we dont
                    # it returns 406 errors after 90 sec.
                    if correct_client.product == 'Plex Media Player':
                        correct_client.sendCommand('timeline/poll', wait=0)

                    proxy_on_fail(correct_client.seekTo(int(offset * 1000)))
                    LOG.info('Jumped %s %s to %s %s', user, client.title,
                             offset, media._prettyfilename())
                else:
                    if not ignore_ratingkey(
                            media,
                            CONFIG['general'].get('ignore_intro_ratingkeys')):
                        proxy_on_fail(correct_client.stop())
                        # We might need to login on pms as the user..
                        # urs_pms = users_pms(PMS, user)
                        # new_media = urs_pms.fetchItem(int(media.ratingkey))
                        # new_media.markWatched()
                        # LOG.debug('Stopped playback on %s and marked %s as watched.', client.title, media._prettyfilename())

                        # Check if we just start the next ep instantly.
                        if CONFIG['tv'].get(
                                'check_credits_start_next_ep') is True:
                            nxt = find_next(
                                media)  # This is always false for movies.
                            if nxt:
                                LOG.info('Start playback on %s with %s', user,
                                         nxt._prettyfilename())
                                proxy_on_fail(correct_client.playMedia(nxt))
            else:
                LOG.info('Didnt find the correct client.')

            # Some clients needs some time..
            # time.sleep(0.2)
            # client.play()
            # JUMP_LIST.remove(sessionkey)
            # time.sleep(1)

            return
Ejemplo n.º 11
0
def find_credits(path, offset=0, fps=None, duration=None, check=7, step=1, frame_range=True):
    """Find the start/end of the credits and end in a videofile.
       This only check frames so if there is any silence in the video this is simply skipped as
       opencv only handles videofiles.

       use frame_range to so we only check frames every 1 sec.

       # TODO just ffmepg to check for silence so we calculate the correct time? :(

       Args:
            path (str): path to the videofile
            offset(int): If given we should start from this one.
            fps(float?): fps of the video file
            duration(None, int): Duration of the vfile in seconds.
            check(int): Stop after n frames with text, set a insane high number to check all.
                        end is not correct without this!
            step(int): only use every n frame
            frame_range(bool). default true, precalc the frames and only check thous frames.

       Returns:
            1, 2


    """
    # LOG.debug('%r %r %r %r %r %r %r', path, offset, fps, duration, check, step, frame_range)
    if cv2 is None:
        return
    frames = []
    start = -1
    end = -1
    LOG.debug('Trying to find the credits for %s', path)

    try:
        if fps is None:
            # we can just grab the fps from plex.
            cap = cv2.VideoCapture(path)
            fps = cap.get(cv2.CAP_PROP_FPS)
            cap.release()

        for i, (frame, millisec) in enumerate(video_frame_by_frame(path, offset=offset,
                                                                   step=step, frame_range=frame_range)):
            # LOG.debug('progress %s', millisec / 1000)
            if frame is not None:
                recs = locate_text(frame, debug=False)

                if recs:
                    frames.append(millisec)

                if check != -1 and len(frames) >= check:
                    break

        if frames:
            LOG.debug(frames)
            start = min(frames) / 1000
            end = max(frames) / 1000

        LOG.debug('credits_start %s, credits_end %s', start, end)

    except:  # pragma: no cover
        # We just want to log the exception not halt the entire process to db.
        LOG.exception('There was a error in find_credits')

    return start, end
Ejemplo n.º 12
0
def find_credits(path, offset=0, fps=None, duration=None,
                 check=7, step=1, frame_range=True, debug=False, method='east'):
    """Find the start/end of the credits and end in a videofile.
       This only check frames so if there is any silence in the video this is simply skipped as
       opencv only handles videofiles.

       use frame_range to so we only check frames every 1 sec.

       # TODO just ffmepg to check for silence so we calculate the correct time? :(

       Args:
            path (str): path to the videofile
            offset(int): If given we should start from this one.
            fps(float?): fps of the video file
            duration(None, int): Duration of the vfile in seconds.
            check(int): Stop after n frames with text, set a insane high number to check all.
                        end is not correct without this!
            step(int): only use every n frame
            frame_range(bool). default true, precalc the frames and only check thous frames.
            debug(bool): Disable the images.
            method(str): east is better but slower.

       Returns:
            1, 2


    """
    # LOG.debug('%r %r %r %r %r %r %r', path, offset, fps, duration, check, step, frame_range)
    if cv2 is None:
        return
    frames = []
    start = -1
    end = -1
    LOG.debug('Trying to find the credits for %s', path)

    if method == 'east':
        func = locate_text_east
    else:
        func = locate_text

    try:
        if fps is None:
            # we can just grab the fps from plex.
            cap = cv2.VideoCapture(path)
            fps = cap.get(cv2.CAP_PROP_FPS)
            cap.release()

        for _, (frame, millisec) in enumerate(video_frame_by_frame(path, offset=offset,
                                                                   step=step, frame_range=frame_range)):

            try:
                # LOG.debug('progress %s', millisec / 1000)
                if frame is not None:
                    # recs = locate_text(frame, debug=True)
                    recs = func(frame, debug=debug)
                    len_recs = len(recs)

                    # If we get 1 match we should verify.
                    # now this is pretty harsh but we really
                    # don't want false positives.
                    if len_recs == 0:
                        continue
                    elif len_recs == 1:
                        t = extract_text(frame)
                        if t:
                            frames.append(millisec)
                    else:
                        frames.append(millisec)

                    # check for motion here?

                    if check != -1 and len(frames) >= check:
                        break

            except DEBUG_STOP:
                break
                if hasattr(cv2, 'destroyAllWindows'):
                    cv2.destroyAllWindows()

        if frames:
            start = min(frames) / 1000
            end = max(frames) / 1000

        LOG.debug('credits_start %s, credits_end %s', start, end)

    except:  # pragma: no cover
        # We just want to log the exception not halt the entire process to db.
        LOG.exception('There was a error in find_credits')

    return start, end