예제 #1
0
def main( ):
    time0 = time.time( )
    date_now = datetime.datetime.now( ).date( )
    parser = OptionParser( )
    parser.add_option('--debug', dest='do_debug', action='store_true',
                      default = False, help = 'Run debug mode if chosen.')
    parser.add_option('--test', dest='do_test', action='store_true',
                      default = False, help = 'Send a test notification email if chosen.')
    parser.add_option('--subject', dest='subject', action='store', type=str,
                      default = 'Plex notification for %s.' % date_now.strftime( '%B %d, %Y' ),
                      help = 'Subject of notification email. Default is "%s".' %
                      ( 'Plex notification for %s.' % date_now.strftime( '%B %d, %Y' ) ) )
    parser.add_option('--body', dest='body', action='store', type=str, default = 'This is a test.',
                      help = 'Body of the email to be sent. Default is "This is a test."')
    opts, args = parser.parse_args( )
    if opts.do_debug:
        logging.basicConfig( level = logging.DEBUG )
    status, _ = plexcore.oauthCheckGoogleCredentials( )
    if not status:
        print( "Error, do not have correct Google credentials." )
        return
    val = plexcore.checkServerCredentials( doLocal = False, verify = False )
    if val is None:
        print( "Error, could not get an instance of a running Plex server on this machine." )
        return
    _, token = val
    #
    ## get mapped emails
    emails = plexcore.get_mapped_email_contacts( token, verify = False )
    name_emails = get_email_contacts_dict( emails, verify = False )
    def return_nameemail_string( name, email ):
        if name is not None:
            return "%s <%s>" % ( name, email )
        return email
    items = sorted(list(map(lambda name_email: return_nameemail_string( name_email[0], name_email[1] ),
                            name_emails)))
    finalString = '\n'.join([ 'Hello Friend,', '', opts.body ])
    htmlString = plexcore.latexToHTML( finalString )
    if htmlString is None:
        print( 'Error, %s could not be converted into email.' % opts.body )
        return

    #
    ## now do the email sending out
    print( 'processed all checks in %0.3f seconds.' % ( time.time( ) - time0 ) )
    time0 = time.time( )
    if opts.do_test:
        plexemail.send_individual_email_full(
            htmlString, opts.subject, emailAddress, name = emailName )
        print( 'processed test email in %0.3f seconds.' % ( time.time( ) - time0 ) )
    else:
        def _send_email_perproc( input_tuple ):
            name, email = input_tuple
            plexemail.send_individual_email_full(
                htmlString, opts.subject, email, name = name )
            return True
        arrs = list( map( _send_email_perproc, name_emails +
                          [ ( emailName, emailAddress ) ] ) )
        print( 'processed %d emails in %0.3f seconds.' % ( len(arrs), time.time( ) - time0 ) )
예제 #2
0
def main(debug = False, doLocal = True, verify = True ):
    app = test_tmdbgui.get_app_standalone( )
    if debug: logging.basicConfig( level = logging.DEBUG )
    fullurl, token = plexcore.checkServerCredentials(
        doLocal = doLocal, verify = verify )
    movie_data_rows = test_tmdbgui.get_movie_data_rows_standalone( )
    tmdb_mygui = plextmdb_mygui.TMDBMyGUI(
        token, movie_data_rows, verify = verify )
    result = app.exec_( )
예제 #3
0
def main( info = False, doLocal = True, verify = True ):
    app = QApplication([])
    app.setStyleSheet( qdarkstyle.load_stylesheet_pyqt( ) )
    icn = QIcon( os.path.join( mainDir, 'resources', 'icons', 'plex_tvdb_gui.png' ) )
    app.setWindowIcon( icn )
    if info: logging.basicConfig( level = logging.INFO )
    fullURL, token = plexcore.checkServerCredentials(
        doLocal = doLocal, verify = verify )
    tvdb_gui = TVDBGUI( token, fullURL, verify = verify )
    result = app.exec_( )
    return tvdb_gui
예제 #4
0
def main(debug=False, doLocal=True, verify=True):
    testDir = os.path.expanduser('~/.config/howdy/tests')
    app = returnQAppWithFonts()
    app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
    if debug: logging.basicConfig(level=logging.DEBUG)
    fullurl, token = plexcore.checkServerCredentials(doLocal=doLocal,
                                                     verify=verify)
    movie_data_rows = pickle.load(
        gzip.open(os.path.join(testDir, 'movie_data_rows.pkl.gz'), 'rb'))
    tmdb_mygui = plextmdb_mygui.TMDBMyGUI(token,
                                          movie_data_rows,
                                          verify=verify)
    result = app.exec_()
예제 #5
0
def main(info=False, doLocal=True, verify=True):
    testDir = os.path.expanduser('~/.config/howdy/tests')
    app = QApplication([])
    app.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())
    if info: logging.basicConfig(level=logging.INFO)
    fullurl, token = plexcore.checkServerCredentials(doLocal=doLocal,
                                                     verify=verify)
    movie_data_rows = pickle.load(
        gzip.open(os.path.join(testDir, 'movie_data_rows.pkl.gz'), 'rb'))
    tmdbgui = plextmdb_gui.TMDBGUI(token,
                                   fullurl,
                                   movie_data_rows,
                                   verify=verify)
    result = app.exec_()
예제 #6
0
def main(info = False, doLocal = True, doLarge = False, verify = True):
    app = returnQAppWithFonts( )
    app.setStyleSheet( qdarkstyle.load_stylesheet_pyqt( ) )
    icn = QIcon( os.path.join(
        mainDir, 'resources', 'icons', 'plex_tmdb_totgui.png' ) )
    app.setWindowIcon( icn )
    logger = logging.getLogger( )
    if info: logger.setLevel( logging.INFO )
    fullurl, token = plexcore.checkServerCredentials(
        doLocal = doLocal, verify = verify )
    tmdb_mygui = plextmdb_totgui.TMDBTotGUI(
        fullurl, token, doLarge = doLarge, verify = verify )
    result = app.exec_( )
    return tmdb_mygui
예제 #7
0
def get_token_fullURL(request):
    doLocal = request.config.option.do_local
    verify = request.config.option.do_verify
    doInfo = request.config.option.do_info
    if doInfo: logging.basicConfig(level=logging.INFO)
    if doLocal: localString = "local"
    else: localString = "non-local"
    if verify: verifyString = "verify"
    else: verifyString = "no-verify"
    print('getting fullURL and token, %s and %s SSL' %
          (localString, verifyString))
    fullURL, token = plexcore.checkServerCredentials(doLocal=doLocal,
                                                     verify=verify)
    yield fullURL, token
    print(
        'after tests, finished giving out tokens, fullURL = %s, token = %s.' %
        (fullURL, token))
예제 #8
0
def get_summary_html(preambleText='',
                     postambleText='',
                     pngDataDict={},
                     name=None,
                     token=None,
                     doLocal=True):
    data = plexcore.checkServerCredentials(doLocal=doLocal, verify=False)
    if data is None:
        print(
            'Sorry, now we need to provide an user name and password. Please get one!'
        )
        return
    fullURL, token = data
    nameSection = False
    if len(preambleText.strip()) != 0:
        nameSection = True
    if name is None:
        name = 'Friend'
    tup_formatting = (
        name,
        preambleText,
        get_summary_body(token, nameSection=nameSection, fullURL=fullURL),
        postambleText,
    )
    wholestr = open(
        os.path.join(mainDir, 'resources', 'plexstuff_template.tex'),
        'r').read()
    wholestr = wholestr % tup_formatting
    wholestr = wholestr.replace('textbf|', 'textbf{')
    wholestr = wholestr.replace('ZZX|', '}')
    htmlString = plexcore.latexToHTML(wholestr)
    htmlString = htmlString.replace('strong>', 'B>')
    #
    ## now process PNG IMG data
    htmlString = plexcore.processValidHTMLWithPNG(htmlString, pngDataDict)
    return htmlString
예제 #9
0
def main():
    time0 = time.time()
    default_time = 1000
    default_iters = 2
    default_num_threads = 2 * multiprocessing.cpu_count()
    #
    parser = OptionParser()
    parser.add_option('--maxtime',
                      dest='maxtime_in_secs',
                      type=int,
                      action='store',
                      default=default_time,
                      help=' '.join([
                          'The maximum amount of time to spend (in seconds),',
                          'per candidate magnet link,',
                          'trying to download a TV show.',
                          'Default is %d seconds.' % default_time
                      ]))
    parser.add_option(
        '--num',
        dest='num_iters',
        type=int,
        action='store',
        default=default_iters,
        help=' '.join([
            'The maximum number of different magnet links to try',
            'before giving up. Default is %d.' % default_iters
        ]))
    parser.add_option(
        '--token',
        dest='token',
        type=str,
        action='store',
        help='Optional argument. If chosen, user provided Plex access token.')
    parser.add_option('--info',
                      dest='do_info',
                      action='store_true',
                      default=False,
                      help='If chosen, then run in info mode.')
    parser.add_option('--debug',
                      dest='do_debug',
                      action='store_true',
                      default=False,
                      help='If chosen, then run in debug mode.')
    parser.add_option(
        '--numthreads',
        dest='numthreads',
        type=int,
        action='store',
        default=default_num_threads,
        help=
        'Number of threads over which to search for TV shows in my library. Default is %d.'
        % default_num_threads)
    parser.add_option(
        '--nomax',
        dest='do_restrict_maxsize',
        action='store_false',
        default=True,
        help='If chosen, do not restrict maximum size of downloaded file.')
    parser.add_option(
        '--nomin',
        dest='do_restrict_minsize',
        action='store_false',
        default=True,
        help='If chosen, do not restrict minimum size of downloaded file.')
    parser.add_option(
        '--raw',
        dest='do_raw',
        action='store_true',
        default=False,
        help='If chosen, then use the raw string to download the torrent.')
    opts, args = parser.parse_args()
    #
    logger = logging.getLogger()
    if opts.do_info: logger.setLevel(logging.INFO)
    if opts.do_debug: logger.setLevel(logging.DEBUG)
    assert (opts.maxtime_in_secs >=
            60), 'error, max time must be >= 60 seconds.'
    assert (opts.num_iters >=
            1), 'error, must have a positive number of maximum iterations.'
    step = 0
    print('%d, started on %s' %
          (step, datetime.datetime.now().strftime('%B %d, %Y @ %I:%M:%S %p')))
    step += 1
    #
    ## get plex server token
    dat = plexcore.checkServerCredentials(doLocal=True)
    if dat is None:
        print('\n'.join([
            '%d, error, could not access local Plex server in %0.3f seconds. Exiting...'
            % (step, time.time() - time0),
            finish_statement(step)
        ]))
        return
    fullURL, token = dat
    if opts.token is not None: token = opts.token
    #
    ## first find out which libraries are the TV show ones
    library_dict = plexcore.get_libraries(token, fullURL=fullURL, do_full=True)
    if library_dict is None:
        print('\n'.join([
            '%d, error, could not access libraries in plex server in %0.3f seconds. Exiting...'
            % (step, time.time() - time0),
            finish_statement(step)
        ]))
        return
    #
    valid_keys = list(
        filter(lambda key: library_dict[key][-1] == 'show', library_dict))
    if len(valid_keys) == 0:
        print('\n'.join([
            '%d, Error, could not find a TV show library in %0.3f seconds. Exiting...'
            % (time.time() - time0, step),
            finish_statement(step)
        ]))
        return
    tvlib_title = library_dict[max(valid_keys)][0]
    print('%d, found TV library: %s.' % (step, tvlib_title))
    step += 1
    #
    ## now get the TV shows
    time0 = time.time()
    tvdata = plexcore.get_library_data(tvlib_title,
                                       token=token,
                                       num_threads=opts.numthreads)
    showsToExclude = plextvdb.get_shows_to_exclude(tvdata)
    if len(showsToExclude) != 0:
        print('%d, excluding these TV shows: %s.' %
              (step, '; '.join(showsToExclude)))
        step += 1
    toGet = plextvdb.get_remaining_episodes(tvdata,
                                            showSpecials=False,
                                            showsToExclude=showsToExclude,
                                            num_threads=opts.numthreads)
    if len(toGet) == 0:
        print('\n'.join([
            '%d, no episodes to download in %0.3f seconds. Exiting...' %
            (step, time.time() - time0),
            finish_statement(step)
        ]))
        return
    print('%d, took %0.3f seconds to get list of %d episodes to download.' %
          (step, time.time() - time0,
           sum(map(lambda tvshow: len(toGet[tvshow]['episodes']), toGet))))
    step += 1
    #
    ## now download these episodes
    tvTorUnits, newdirs = plextvdb.create_tvTorUnits(
        toGet,
        restrictMaxSize=opts.do_restrict_maxsize,
        restrictMinSize=opts.do_restrict_minsize,
        do_raw=opts.do_raw)
    print('%d, here are the %d episodes to get: %s.' %
          (step, len(tvTorUnits), ', '.join(
              map(lambda tvTorUnit: tvTorUnit['torFname'], tvTorUnits))))
    step += 1
    plextvdb.download_batched_tvtorrent_shows(
        tvTorUnits,
        newdirs=newdirs,
        maxtime_in_secs=opts.maxtime_in_secs,
        num_iters=opts.num_iters)
    print('\n'.join([
        '%d, everything done in %0.3f seconds.' % (step, time.time() - time0),
        finish_statement(step)
    ]))
예제 #10
0
#
## start the application here
logging.basicConfig( level = logging.INFO )
parser = OptionParser( )
parser.add_option('-s', '--series', type=str, dest='series', action='store',
                  default='The Simpsons',
                  help = 'Name of the series to choose. Default is "The Simpsons".' )
opts, args = parser.parse_args( )
app = QApplication([])
app.setStyleSheet( qdarkstyle.load_stylesheet_pyqt( ) )
tvdata = pickle.load(
    gzip.open( os.path.join( testDir, 'tvdata.pkl.gz' ), 'rb' ) )
toGet = pickle.load( gzip.open(
    gzip.open( os.path.join( testDir, 'toGet.pkl.gz' ), 'rb' ) )
didend = pickle.load( gzip.open(
    os.path.join( testDir, 'didend.pkl.gz'), 'rb' ) )
assert( opts.series in tvdata )
_, plex_token = plexcore.checkServerCredentials(
    doLocal = False, verify = False )
tvdb_token = get_token( verify = False )
tvdb_show_gui = plextvdb_gui.TVDBShowGUI(
    opts.series, tvdata, toGet, tvdb_token, plex_token,
    verify = False )
tvdb_show_gui.setStyleSheet("""
QWidget {
font-family: Consolas;
font-size: 11;
}""" )
result = tvdb_show_gui.exec_( )
예제 #11
0
def main():
    parser = OptionParser()
    parser.add_option(
        '--libraries',
        dest='do_libraries',
        action='store_true',
        default=False,
        help=
        'If chosen, just give the sorted names of all libraries in the Plex server.'
    )
    parser.add_option(
        '--refresh',
        dest='do_refresh',
        action='store_true',
        default=False,
        help=
        'If chosen, refresh a chosen library in the Plex server. Must give a valid name for the library.'
    )
    parser.add_option(
        '--summary',
        dest='do_summary',
        action='store_true',
        default=False,
        help=
        'If chosen, perform a summary of the chosen library in the Plex server. Must give a valid name for the library.'
    )
    parser.add_option('--library',
                      dest='library',
                      type=str,
                      action='store',
                      help='Name of a (valid) library in the Plex server.')
    parser.add_option('--servername',
                      dest='servername',
                      action='store',
                      type=str,
                      help='Optional name of the server to check for.')
    parser.add_option(
        '--servernames',
        dest='do_servernames',
        action='store_true',
        default=False,
        help='If chosen, print out all the servers owned by the user.')
    parser.add_option('--noverify',
                      dest='do_verify',
                      action='store_false',
                      default=True,
                      help='Do not verify SSL transactions if chosen.')
    opts, args = parser.parse_args()
    #
    ##
    _, token = plexcore.checkServerCredentials(doLocal=False,
                                               verify=opts.do_verify)
    #
    ## only one of possible actions
    assert( len( list( filter( lambda tok: tok is True, (
        opts.do_libraries, opts.do_refresh, opts.do_summary, opts.do_servernames ) ) ) ) == 1 ), \
        "error, must choose one of --libraries, --refresh, --summary, --servernames"

    #
    ## if list of servernames, --servernames
    if opts.do_servernames:
        server_dicts = plexcore.get_all_servers(token, verify=opts.do_verify)
        if server_dicts is None:
            print('COULD FIND NO SERVERS ACCESIBLE TO USER.')
            return
        server_formatted_data = list(
            map(
                lambda name:
                (name, server_dicts[name]['owned'], server_dicts[name]['url']),
                server_dicts))
        print('\n%s\n' % tabulate(server_formatted_data,
                                  headers=['Name', 'Is Owned', 'URL']))
        return

    #
    ## check that server name we choose is owned by us.
    server_dicts = plexcore.get_all_servers(token, verify=opts.do_verify)
    server_names_owned = sorted(
        set(filter(lambda name: server_dicts[name]['owned'], server_dicts)))
    assert (len(server_names_owned) >
            0), "error, none of these Plex servers is owned by us."
    if opts.servername is None:
        opts.servername = max(server_names_owned)

    assert (opts.servername in server_names_owned
            ), "error, server %s not in list of owned servers: %s." % (
                opts.servername, server_names_owned)

    #
    ## get URL and token from server_dicts
    fullURL = server_dicts[opts.servername]['url']
    token = server_dicts[opts.servername]['access token']

    #
    ## if get list of libraries, --libraries
    if opts.do_libraries:
        library_dict = plexcore.get_libraries(token,
                                              fullURL=fullURL,
                                              do_full=True)
        print('\nHere are the %d libraries in this Plex server: %s.' %
              (len(library_dict), opts.servername))
        libraries_library_dict = dict(
            map(
                lambda keynum:
                (library_dict[keynum][0], library_dict[keynum][1]),
                library_dict.keys()))
        library_names = sorted(libraries_library_dict)
        libraries_formatted_data = list(
            map(lambda name: (name, libraries_library_dict[name]),
                library_names))
        print('\n%s\n' % tabulate(libraries_formatted_data,
                                  headers=['Name', 'Library Type']))
        return

    #
    ## now gone through here, must define a --library
    assert (opts.library is not None), "error, library must be defined."
    library_dict = plexcore.get_libraries(token, fullURL=fullURL, do_full=True)
    library_names = sorted(
        map(lambda keynum: library_dict[keynum][0], library_dict.keys()))
    assert (opts.library in library_names
            ), "error, library = %s not in %s." % (opts.library, library_names)
    library_key = max(
        filter(lambda keynum: library_dict[keynum][0] == opts.library,
               library_dict))

    #
    ## if summary is chosen, --summary
    if opts.do_summary:
        _print_summary(library_key, library_dict, token, fullURL)
        return

    #
    ## otherwise refresh is chosen, --refresh
    if opts.do_refresh:
        plexcore.refresh_library(library_key,
                                 library_dict,
                                 fullURL=fullURL,
                                 token=token)
        print('refreshed library %s.' % opts.library)
        return
예제 #12
0
def main():
    parser = OptionParser()
    parser.add_option('--username',
                      dest='username',
                      type=str,
                      action='store',
                      help='Your plex username.')
    parser.add_option('--password',
                      dest='password',
                      type=str,
                      action='store',
                      help='Your plex password.')
    parser.add_option('--friends',
                      dest='do_friends',
                      action='store_true',
                      default=False,
                      help='Get list of guests of your Plex server.')
    parser.add_option(
        '--mappedfriends',
        dest='do_mapped_friends',
        action='store_true',
        default=False,
        help='Get list of guests with mapping, of your Plex server.')
    parser.add_option(
        '--addmapping',
        dest='do_addmapping',
        action='store_true',
        default=False,
        help='If chosen, then add extra friends from Plex friends.')
    parser.add_option('--guestemail',
                      dest='guest_email',
                      action='store',
                      type=str,
                      help='Name of the Plex guest email.')
    parser.add_option(
        '--newemails',
        dest='new_emails',
        action='store',
        type=str,
        help='Name of the new emails associated with the Plex guest email.')
    parser.add_option(
        '--replace_existing',
        dest='do_replace_existing',
        action='store_true',
        default=False,
        help='If chosen, replace existing email to send newsletter to.')
    opts, args = parser.parse_args()
    assert (len(
        list(
            filter(lambda tok: tok is True,
                   (opts.do_friends, opts.do_addmapping,
                    opts.do_mapped_friends)))) == 1)
    if any(map(lambda tok: tok is None, (opts.username, opts.password))):
        var = plexcore.checkServerCredentials(doLocal=False,
                                              verify=False,
                                              checkWorkingServer=False)
        if var is None:
            print(
                'COULD NOT FIND PLEX SERVER CREDENTIALS OR INVALID USERNAME/PASSWORD COMBO'
            )
            return
        _, token = var
    else:
        assert (all(
            map(lambda tok: tok is not None,
                (opts.username,
                 opts.password)))), "must have both username and password"
        token = plexcore.getTokenForUsernamePassword(opts.username,
                                                     opts.password,
                                                     verify=False)
        if token is None:
            print('INVALID USERNAME/PASSWORD COMBO.')
            return

    if opts.do_friends:
        plex_emails = sorted(
            set(plexcore.get_email_contacts(token, verify=False)))
        _print_format_names(plex_emails, header_name='PLEX')

    elif opts.do_mapped_friends:
        mapped_emails = sorted(
            set(plexcore.get_mapped_email_contacts(token, verify=True)))
        _print_format_names(mapped_emails, header_name='MAPPED PLEX')

    elif opts.do_addmapping:
        plex_emails = sorted(
            set(plexcore.get_email_contacts(token, verify=False)))
        assert (all(
            map(lambda tok: tok is not None,
                (opts.guest_email, opts.new_emails))))
        assert (opts.guest_email in plex_emails)
        new_emails = sorted(
            set(map(lambda tok: tok.strip(), opts.new_emails.split(','))))
        assert (len(set(new_emails) & set(plex_emails)) == 0)
        plexcore.add_mapping(opts.guest_email, plex_emails, new_emails,
                             opts.do_replace_existing)
예제 #13
0
def main():
    time0 = time.time()
    parser = OptionParser()
    parser.add_option(
        '--years',
        dest='s_years',
        action='store',
        type=str,
        help='Give a list of years as a string, such as "1980,1981". Optional.'
    )
    parser.add_option('--noverify',
                      dest='do_noverify',
                      action='store_true',
                      default=False,
                      help='If chosen, do not verify the SSL connection.')
    parser.add_option('--local',
                      dest='do_local',
                      action='store_true',
                      default=False,
                      help='Check for locally running plex server.')
    parser.add_option(
        '--dirname',
        dest='dirname',
        action='store',
        type=str,
        default=os.getcwd(),
        help='Directory into which to store those plots. Default is %s.' %
        os.getcwd())
    opts, args = parser.parse_args()

    #
    ## function to do the processing

    step = 0
    print('%d, started on %s' %
          (step, datetime.datetime.now().strftime('%B %d, %Y @ %I:%M:%S %p')))
    if opts.s_years is not None:
        try:
            years = sorted(
                set(map(lambda tok: int(tok), opts.s_years.split(','))))
        except:
            step += 1
            print('%d, did not give a valid set of years.' % step)
            years = []
    else:
        years = []

    #
    ## get plex server token
    dat = plexcore.checkServerCredentials(doLocal=True)
    if dat is None:
        step += 1
        print('\n'.join([
            '%d, error, could not access local Plex server in %0.3f seconds. Exiting...'
            % (step, time.time() - time0),
            '%d, finished on %s.' %
            (step + 1,
             datetime.datetime.now().strftime('%B %d, %Y @ %I:%M:%S %p'))
        ]))
        return
    fullURL, token = dat
    #
    ## first find out which libraries are the TV show ones
    library_dict = plexcore.get_libraries(token, fullURL=fullURL, do_full=True)
    if library_dict is None:
        step += 1
        print('\n'.join([
            '%d, error, could not access libraries in plex server in %0.3f seconds. Exiting...'
            % (step, time.time() - time0),
            '%d, finished on %s.' %
            (step + 1,
             datetime.datetime.now().strftime('%B %d, %Y @ %I:%M:%S %p'))
        ]))
        return
    #
    valid_keys = list(
        filter(lambda key: library_dict[key][-1] == 'show', library_dict))
    if len(valid_keys) == 0:
        step += 1
        print('\n'.join([
            '%d, Error, could not find a TV show library in %0.3f seconds. Exiting...'
            % (time.time() - time0, step),
            '%d, finished on %s.' %
            (step + 1,
             datetime.datetime.now().strftime('%B %d, %Y @ %I:%M:%S %p'))
        ]))
        return
    tvlib_title = library_dict[max(valid_keys)][0]
    step += 1
    print('%d, found TV library: %s.' % (step, tvlib_title))
    #
    ## now get the TV shows
    tvdata = plexcore.get_library_data(tvlib_title,
                                       token=token,
                                       fullURL=fullURL,
                                       num_threads=16)
    showsToExclude = plextvdb.get_shows_to_exclude(tvdata)
    if len(showsToExclude) != 0:
        step += 1
        print('%d, excluding these TV shows: %s.' %
              (step, '; '.join(showsToExclude)))

    #
    ## now actual meat of the computation
    tvdata_date_dict = plextvdb.get_tvdata_ordered_by_date(tvdata)
    min_year = min(tvdata_date_dict.keys()).year
    max_year = max(tvdata_date_dict.keys()).year
    possible_years_set = set(map(lambda date: date.year, tvdata_date_dict))
    step += 1
    if len(years) == 0:
        years = sorted(possible_years_set)
        print('%d, no years specified. We will use %s total: %s.' %
              (step, _print_years(len(years)), ', '.join(
                  map(lambda year: '%d' % year, years))))
    else:
        cand_years = sorted(set(years) & possible_years_set)
        if len(cand_years) == 0:
            print('\n'.join([
                '%d, no intersection between the %s chosen (%s) and the %d years in the library.'
                % (step, _print_years(len(years)), ', '.join(
                    lambda yr: '%d' % year, years), len(possible_years_set)),
                'Instead, we will use %s total: %s.' %
                (_print_years(len(possible_years_set)), ', '.join(
                    map(lambda year: '%d' % year, sorted(possible_years_set))))
            ]))
            years = sorted(possible_years_set)
        else:
            print('%d, we found %s to use: %s.' %
                  (step, _print_years(len(cand_years)), ', '.join(
                      map(lambda year: '%d' % year, cand_years))))
            years = cand_years

    step += 1
    print('%d, started processing %s of TV shows after %0.3f seconds.' %
          (step, _print_years(len(years)), time.time() - time0))
    manager = Manager()
    shared_step = manager.Value('step', step)
    num_procced = manager.Value('nump', 0)
    lock = manager.RLock()
    pool = Pool(processes=cpu_count())

    def _process_year(year):
        plextvdb.create_plot_year_tvdata(tvdata_date_dict,
                                         year,
                                         shouldPlot=True,
                                         dirname=opts.dirname)
        lock.acquire()
        shared_step.value += 1
        num_procced.value += 1
        print(
            '%d, finished processing year = %d (%02d / %02d) in %0.3f seconds.'
            % (shared_step.value, year, num_procced.value, len(years),
               time.time() - time0))
        lock.release()

    _ = list(pool.map(_process_year, years))
    step = shared_step.value + 1
    print('\n'.join([
        '%d, processed all %s in %0.3f seconds.' %
        (step, _print_years(len(years)), time.time() - time0),
        '%d, finished everything on %s.' %
        (step + 1, datetime.datetime.now().strftime('%B %d, %Y @ %I:%M:%S %p'))
    ]))
def main():
    time0 = time.time()
    parser = OptionParser()
    parser.add_option('--noverify',
                      dest='do_verify',
                      action='store_false',
                      default=True,
                      help='If chosen, do not verify the SSL connection.')
    parser.add_option('--local',
                      dest='do_local',
                      action='store_true',
                      default=False,
                      help='Check for locally running plex server.')
    parser.add_option('--info',
                      dest='do_info',
                      action='store_true',
                      default=False,
                      help='If chosen, run with INFO logging mode.')
    opts, args = parser.parse_args()
    logger = logging.getLogger()
    if opts.do_info: logger.setLevel(logging.INFO)

    #
    ## function to do the processing

    step = 0
    print('%d, started on %s' %
          (step, datetime.datetime.now().strftime('%B %d, %Y @ %I:%M:%S %p')))

    #
    ## get plex server token
    dat = plexcore.checkServerCredentials(doLocal=True)
    if dat is None:
        step += 1
        print('\n'.join([
            '%d, error, could not access local Plex server in %0.3f seconds. Exiting...'
            % (step, time.time() - time0),
            '%d, finished on %s.' %
            (step + 1,
             datetime.datetime.now().strftime('%B %d, %Y @ %I:%M:%S %p'))
        ]))
        return
    fullURL, token = dat
    #
    ## first find out which libraries are the TV show ones
    library_dict = plexcore.get_libraries(token, fullURL=fullURL, do_full=True)
    if library_dict is None:
        step += 1
        print('\n'.join([
            '%d, error, could not access libraries in plex server in %0.3f seconds. Exiting...'
            % (step, time.time() - time0),
            '%d, finished on %s.' %
            (step + 1,
             datetime.datetime.now().strftime('%B %d, %Y @ %I:%M:%S %p'))
        ]))
        return
    #
    valid_keys = list(
        filter(lambda key: library_dict[key][-1] == 'show', library_dict))
    if len(valid_keys) == 0:
        step += 1
        print('\n'.join([
            '%d, Error, could not find a TV show library in %0.3f seconds. Exiting...'
            % (time.time() - time0, step),
            '%d, finished on %s.' %
            (step + 1,
             datetime.datetime.now().strftime('%B %d, %Y @ %I:%M:%S %p'))
        ]))
        return
    tvlib_title = library_dict[max(valid_keys)][0]
    step += 1
    nowdate = datetime.datetime.now().date()
    print('%d, found TV library: %s.' % (step, tvlib_title))
    #
    ## now get the future TV shows
    tvdata = plexcore.get_library_data(tvlib_title,
                                       token=token,
                                       fullURL=fullURL)
    showsToExclude = plextvdb.get_shows_to_exclude(tvdata)
    if len(showsToExclude) != 0:
        step += 1
        print('%d, excluding these TV shows: %s.' %
              (step, '; '.join(showsToExclude)))

    future_shows_dict = plextvdb.get_future_info_shows(
        tvdata,
        verify=opts.do_verify,
        showsToExclude=showsToExclude,
        fromDate=nowdate)
    for show in future_shows_dict:
        tdelta = future_shows_dict[show]['start_date'] - nowdate
        future_shows_dict[show]['days_to_new_season'] = tdelta.days

    if len(future_shows_dict) == 0:
        step += 1
        print('%d, found no TV shows with new seasons.' % step)
        print('%d,  finished on %s.' %
              (step + 1,
               datetime.datetime.now().strftime('%B %d, %Y @ %I:%M:%S %p')))
        return
    step += 1
    print(
        '%d, Found %d TV shows with new seasons after %s, in %0.3f seconds.' %
        (step, len(future_shows_dict), nowdate.strftime('%B %d, %Y'),
         time.time() - time0))
    print('\n')
    all_new_show_data = list(
        map(
            lambda show:
            (show, future_shows_dict[show]['max_last_season'],
             future_shows_dict[show]['min_next_season'], future_shows_dict[
                 show]['start_date'].strftime('%B %d, %Y'), future_shows_dict[
                     show]['days_to_new_season']),
            sorted(future_shows_dict,
                   key=lambda shw:
                   (future_shows_dict[shw]['start_date'], shw))))
    print('%s\n' % tabulate.tabulate(all_new_show_data,
                                     headers=[
                                         'SHOW', 'LAST SEASON', 'NEXT SEASON',
                                         'AIR DATE', 'DAYS TO NEW SEASON'
                                     ]))

    step += 1
    print('\n'.join([
        '%d, processed everything in %0.3f seconds.' %
        (step, time.time() - time0),
        '%d, finished everything on %s.' %
        (step + 1, datetime.datetime.now().strftime('%B %d, %Y @ %I:%M:%S %p'))
    ]))