4.3. Howdy Core API

This document describes the lower level Plex core API, upon which all the command line and GUI tools are based. It lives in howdy.core.

4.3.1. howdy.core module

This module implements the lower-level functionality that does or has the following:

  • access and retrieve configuration and other data from an SQLite3 database using SQLAlchemy object relational mapping (ORM) classes. The SQLite3 database is stored in ~/.config/howdy/app.db.
  • PlexConfig is an ORM class that stores configuration information.
  • PlexGuestEmailMapping is an ORM class that stores all the email addresses that will receive Howdy email notifications.
  • LastNewsletterDate is an ORM class that store one member (or row) – the datetime of when the Howdy newsletter was last updated.
  • create_all instantiates necessary SQLite3 tables in the configuration table if they don’t already exist.
  • low level PyQt5 derived widgets used for the other GUIs in Howdy: ProgressDialog, QDialogWithPrinting, and QLabelWithSave.
  • initialization, in order to check for necessary prerequisites (see Prerequisites) and to install missing Python modules and packages (see Installation). This initialization is handled via a HowdyInitialization singleton object.
class howdy.core.HtmlView(parent, htmlString='')

A convenient PyQt5 widget that displays rich and interactive HTML (HTML with CSS and Javascript). This extends QWebEngineView.

class howdy.core.LastNewsletterDate(**kwargs)

This SQLAlchemy ORM class contains the date at which the last newsletter was sent. It is not used much, and now that Tautulli has newsletter functionality, I very likely won’t use this at all. Stored into the lastnewsletterdate table in the SQLite3 configuration database.

Variables:date – the datetime when the last newsletter was sent. This is a Column containing a Date object.
class howdy.core.PlexConfig(**kwargs)

This SQLAlchemy ORM class contains the configuration data used for running all the Howdy tools. Stored into the plexconfig table in the SQLite3 configuration database.

Variables:
  • service – the name of the configuration service we store. Index on this unique key. This is a Column containing a String of size 65536.
  • data

    the JSON formatted information on the data stored here. For instance, username and password can be stored in the following way

    { 'username' : USERNAME,
      'password' : PASSWORD }
    

    This is a Column containing a JSON object.

class howdy.core.PlexGuestEmailMapping(**kwargs)

This SQLAlchemy ORM class contains mapping of emails of Plex server users, to other email addresses. This is used to determine other email addresses to which Howdy one-off or newsletter emails are delivered. Stored in the plexguestemailmapping table in the SQLite3 configuration database. The structure of each row in this table is straightforward. Each column in the table is a member of the object in this ORM class.

Variables:
  • plexemail – this is the main column, which must be the email of a Plex user who has access to the Plex server. This is a Column containing a String.
  • plexmapping – this is a collection of different email addresses, to which the emails are sent. For example, if a Plex user with email address A@email.com would like to send email to B@mail.com and C@mail.com, the plexmapping column would be B@mail.com,C@mail.com. NONE of the mapped emails will match email. This is a Column containing a String of size 65536.
  • plexreplaceexisting – this is a boolean that determines whether email also goes to the Plex user’s email address. From the example above, if True then a Plex user at A@mail.com will have email delivered ONLY to B@mail.com and C@mail.com. If False, then that same Plex user will have email delivered to all three email addresses (A@mail.com, B@mail.com, and C@mail.com). This is a Column containing a Boolean.
class howdy.core.ProgressDialog(parent, windowTitle='', doQuit=True)

A convenient PyQt5 widget, inheriting from QDialogWithPrinting, that acts as a GUI blocking progress window for longer lasting operations. Like its parent class, this dialog widget is also resizable. This shows the passage of the underlying slow process in 5 second increments.

This progress dialog exposes three methods – addText, stopDialog, and startDialog – to which a custom QThread object can connect.

  • startDialog is triggered on long operation start, sometimes with an initial message.
  • addText is triggered when some intermediate progress text must be returned.
  • stopDialog is triggered on process end.
Parameters:
  • parent (QWidget) – the parent QWidget on which this dialog widget blocks.
  • windowTitle (str) – the label to put on this progress dialog in an internal QLabel.
  • doQuit (bool) – if True, then using the quit shortcuts (Esc or Ctrl+Shift+Q) will cause the underlying program to exit. Otherwise, hide the progress dialog.
Variables:
  • mainDialog (QTextEdit) – the main dialog widget in this GUI.
  • parsedHTML (BeautifulSoup) – the BeautifulSoup structure that contains the indexable tree of progress dialogs.
  • elapsedTime (QLabel) – the bottom QLabel widget that displays how much time (in seconds) has passed.
  • timer (QTimer) – the QTimer sub-thread that listens every 5 seconds before emitting a signal.
  • t0 (float) – the UNIX time, in seconds with resolution of microseconds.
addText(text)

adds some text to this progress dialog window.

Parameters:text (str) – the text to add.
showTime()

method connected to the internal timer that prints out how many seconds have passed, on the underlying elapsedTime QLabel.

startDialog(initString='')

starts running the progress dialog, with an optional labeling string, and starts the timer.

Parameters:initString (str) – optional internal labeling string.
stopDialog()

stops running, and hides, this progress dialog.

class howdy.core.ProgressDialogThread(parent, title)

This subclassing of QThread provides a convenient scaffolding to run, in a non-blocking fashion, some long-running processes with an asssociated ProgressDialog widget.

Subclasses of this object need to have a particular structure for their __init__ method. The first three arguments MUST be self, parent, self is a reference to this object. parent is the parent QWidget to which the ProgressDialog attribute, named progress_dialog, is the child. title is the title of progress_dialog. Here is an example, where an example class named ProgressDialogThreadChildClass inherits from ProgressDialogThread.

def __init__( self, parent, *args, **kwargs ):
    super( ProgressDialogThreadChildClass, self ).__init__( parent, title )

    # own code to initialize based on *args and **kwargs

This thing has an associated run method that is expected to be partially implemented in the following manner for subclasses of ProgressDialogThread.

  • It must start with self.progress_dialog.show( ) to show the progress dialog widget.
  • It must end with this command, self.stopDialog.emit( ) to hide the progress dialog widget.

Here is an example.

def run( self ):
    self.progress_dialog.show( )
    # run its own way
    self.stopDialog.emit( )

In the run method, if one wants to print out something into progess_dialog, then there should be these types of commands in run: self.emitString.emit( mystr ), where mystr is a str message to show in progress_dialog, and emitString is a pyqtsignal connected to the progress_dialog object’s addText( ).

Parameters:
  • parent (QWidget) – the parent widget for which this long-lasting process will pop up a progress dialog.
  • title (str) – the title for the progress_dialog widget.
Variables:
  • emitString – the signal, with str signature, that is triggered to send progress messages into progress_dialog.
  • stopDialog – the signal that is triggered to stop the progress_dialog, calling stopDialog.
  • startDialog – the signal, with str signature, that is triggered to restart the progress_dialog widget, calling startDialog.
  • progress_dialog – the GUI that shows, in a non-blocking fashion, the progress on some longer-running method.
  • time0 (int) – a convenience attribute, the UTC time at which the progress_dialog object was first instantiated. Can be used to determine the time each submethod takes (for example, time.time( ) - self.time0).

See also

ProgressDialog.

class howdy.core.QDialogWithPrinting(parent, isIsolated=True, doQuit=True)

A convenient PyQt5 widget, inheriting from QDialog, that allows for screen grabs and keyboard shortcuts to either hide this dialog window or quit the underlying program. This PyQt5 widget is also resizable, in relative increments of 5% larger or smaller, to a maximum of \(1.05^5\) times the initial size, and to a minimum of \(1.05^{-5}\) times the initial size.

Parameters:
  • parent (QWidget) – the parent QWidget to this dialog widget.
  • isIsolated (bool) – If True, then this widget is detached from its parent. If False, then this widget is embedded into a layout in the parent widget.
  • doQuit (bool) – if True, then using the quit shortcuts (Esc or Ctrl+Shift+Q) will cause the underlying program to exit. Otherwise, hide the progress dialog.
Variables:
  • indexScalingSignal – a pyqtSignal that can be connected to other PyQt5 events or methods, if resize events want to be recorded.
  • initWidth (int) – the initial width of the GUI in pixels.
  • initHeight (int) – the initial heigth of the GUI in pixels.
makeBigger()

makes the widget incrementally 5% larger, for a maximum of \(1.05^5\), or approximately 28% larger than, the initial size.

makeSmaller()

makes the widget incrementally 5% smaller, for a minimum of \(1.05^{-5}\), or approximately 28% smaller than, the initial size.

resetSize()

reset the widget size to the initial size.

reset_sizes()

Sets the default widget size to the current size.

screenGrab()

take a screen shot of itself and saver to a PNG file through a QFileDialog widget.

class howdy.core.QLabelWithSave(parent=None)

A convenient PyQt5 widget that inherits from QLabel, but allows screen shots.

contextMenuEvent(event)

Constructs a context menu with a single action, Save Pixmap, that takes a screen shot of this widget, using screenGrab.

Parameters:event (QEvent) – default QEvent argument needed to create a context menu. Is not used in this reimplementation.
screenGrab()

take a screen shot of itself and save to a PNG file through a QFileDialog widget.

howdy.core.check_valid_RST(myString)

Checks to see whether the input string is valid reStructuredText.

Parameters:myString (str) – the candidate reStructuredText input.
Returns:True if valid, otherwise False.
Return type:bool

See also

convert_string_RST.

howdy.core.convert_string_RST(myString)

Converts a valid reStructuredText input string into rich HTML.

Parameters:myString (str) – the candidate reStructuredText input.
Returns:If the input string is valid reStructuredText, returns the rich HTML as a string. Otherwise emits a logging error message and returns None.
Return type:str

See also

check_valid_RST.

howdy.core.create_all()

creates the necessary SQLite3 tables into the database file ~/.config/howdy/app.db if they don’t already exist, but only if not building documentation in Read the docs.

howdy.core.geoip_reader = <geoip2.database.Reader object>

This contains an on-disk MaxMind database, of type geoip2.database.Reader, containing location information for IP addresses.

howdy.core.get_formatted_duration(totdur)

This routine spits out a nice, formatted string representation of the duration, which is of type datetime.

Parameters:totdur (datetime) – a length of time, represented as a datetime.
Returns:Formatted representation of that length of time.
Return type:str
howdy.core.get_formatted_size(totsizebytes)

This routine spits out a nice, formatted string representation of a file size, which is represented in int.

This method works like this.

get_formatted_size( int(2e3) ) = '1.953 kB' # kilobytes
get_formatted_size( int(2e6) ) = '1.907 MB' # megabytes
get_formatted_size( int(2e9) ) = '1.863 GB' # gigabytes
Parameters:totsizebytes (int) – size of a file in bytes.
Returns:Formatted representation of that file size.
Return type:str
howdy.core.get_formatted_size_MB(totsizeMB)

Same as get_formatted_size, except this operates on file sizes in units of megabytes rather than bytes.

Parameters:totsizeMB (int) – size of the file in megabytes.
Returns:Formatted representation of that file size.
Return type:str

See also

get_formatted_size.

howdy.core.get_lastupdated_string(dt=datetime.datetime(2021, 1, 3, 19, 57, 35, 541365))

Returns a string representation of a datetime object.

Parameters:dt – the date and time, of type datetime.
Returns:a str with this format, Saturday, 28 September 2019, at 3:41 AM.
Return type:str

See also

get_summary_body.

howdy.core.get_maximum_matchval(check_string, input_string)

Returns the Levenshtein distance of two strings, implemented using A perfect match is a score of 100.0.

Parameters:
  • check_string (str) – first string.
  • input_string (str) – second string.
Returns:

the Levenshtein distance between the two strings.

Return type:

float

howdy.core.get_popularity_color(hpop, alpha=1.0)

Get a color that represents some darkish cool looking color interpolated between 0 and 1.

Parameters:
  • hpop (float) – the value (between 0 and 1) of the color.
  • alpha (float) – The \(lpha\) value of the color (between 0 and 1).
Returns:

a QColor object to put into a QWidget.

Return type:

QColor

howdy.core.returnQAppWithFonts()

returns a customized QApplication with all custom fonts loaded.

howdy.core.return_error_raw(msg)

Returns a default tuple of type None, msg, where msg is a str.

Parameters:msg (str) – the error message.
Returns:a tuple with structure (None, msg). msg should NEVER be SUCCESS.
Return type:tuple
howdy.core.splitall(path_init)

This routine is used by get_path_data_on_tvshow to split a TV show file path into separate directory delimited tokens.

Parameters:path_init (str) – The absolute path of the file.
Returns:each subdirectory, and file basename, in the path
Return type:list

4.3.2. howdy.core.core module

This module implements the functionality to do the following:

  • access your Plex server, and determine those Plex servers to which you have access.
  • retrieve and summarize data in the Plex libraries.
  • miscellaneous functionalities, such as the following: getting formatted date strings from a date object.
howdy.core.core.add_mapping(plex_email, plex_emails, new_emails, replace_existing)

Changes the mapping of one member of the Plex server’s emails from an old set of emails to a new set of emails. The command line tool, howdy_core_cli, is a front end to the lower-level functionality implemented here.

Parameters:
  • plex_email (str) – the email of a Plex server member whose mapping of emails is to be changed.
  • plex_emails (list) – the emails of all members of the Plex server.
  • new_emails (list) – the mapping to be done. None of the emails in new_emails can be in the Plex server.
howdy.core.core.checkServerCredentials(doLocal=False, verify=True, checkWorkingServer=False)

Returns get a local or remote URL and Plex access token to allow for API access to the server. If there is already a VALID token in the SQLite3 configuration database, then uses that. Otherwise, tries to acquire a Plex access token.

Parameters:
  • doLocal (bool) – optional argument, whether to get a local (http://localhost:32400) or remote URL. Default is False (look for the remote URL).
  • verify (bool) – optional argument, whether to verify SSL connections. Default is True.
  • checkWorkingServer (bool) – optional argument, whether to check if the server is working. Default is True.
Returns:

a tuple of server URL and Plex access token.

Return type:

tuple

See also

getCredentials.

howdy.core.core.check_imgurl_credentials(clientID, clientSECRET, clientREFRESHTOKEN, verify=True)

validate the Imgur API credentials with the provided API client ID, secret, and refresh token.

Parameters:
  • clientID (str) – the Imgur client ID.
  • clientSECRET (str) – the Imgur client secret.
  • clientREFRESHTOKEN (str) – the Imgur client refresh token.
  • verify (bool) – optional argument, whether to verify SSL connections. Default is True.
Returns:

whether the new credentials are correct.

Return type:

bool

howdy.core.core.check_jackett_credentials(url, apikey, verify=True)

validate the Jackett server credentials with the provided URL and API key.

Parameters:
  • url (str) – the Jackett server’s URL.
  • apikey (str) – the Jackett server’s API key.
  • verify (bool) – optional argument, whether to verify SSL connections. Default is True.
Returns:

a tuple. If successful, this is a tuple of the server’s URL and the string "SUCCESS". If unsuccessful, this is a tuple of NoneType and error message string.

Return type:

tuple

howdy.core.core.fill_out_movies_stuff(token, fullURL='http://localhost:32400', verify=True)

Creates a tuple. The first element of the tuple is a list of movies from this Plex server. Each element in that list is a dict with the following structure with 12 keys and values, as shown in this example

{ 'title': 'Blue Collar',
  'rating': 10.0,
  'contentrating': 'R',
  'picurl': 'https://24.5.231.186:32400/library/metadata/46001/art/1569656413',
  'releasedate': datetime.date(1978, 2, 10),
  'addedat': datetime.date(2019, 6, 6),
  'summary': "Fed up with mistreatment at the hands of both management and union brass, and coupled with financial hardships on each man's end, three auto assembly line workers hatch a plan to rob a safe at union headquarters.",
  'duration': 6820.394,
  'totsize': 935506414.0,
  'localpic': True,
  'imdb_id': 'tt0077248',
  'genre': 'drama'
}

The second element of the tuple is a list of movie genres on the Plex server.

Parameters:
  • token (str) – the Plex server access token.
  • fullURL (str) – the Plex server address.
  • verify (bool) – optional argument, whether to verify SSL connections. Default is True.
Returns:

a tuple of two lists. The first is a list of all the movies on the Plex server. The second is a list of all the movie genres found on the Plex server.

Return type:

tuple.

howdy.core.core.getCredentials(verify=True, checkWorkingServer=True)

Returns the Plex user account information stored in ~/.config/howdy/app.db.

Parameters:
  • verify (bool) – optional argument, whether to use SSL verification. Default is True.
  • checkWorkingServer (bool) – optional argument, whether to check if the server is working. Default is True.
Returns:

the Plex account tuple of (username, password).

Return type:

tuple

See also

pushCredentials.

howdy.core.core.getTokenForUsernamePassword(username, password, verify=True)

get the Plex access token for the Plex server given an username and password for the user account.

Parameters:
  • username (str) – the Plex account username.
  • password (str) – the Plex account password.
  • verify (bool) – optional argument, whether to verify SSL connections. Default is True.
Returns:

the Plex access token.

Return type:

str

howdy.core.core.get_all_servers(token, verify=True)

Find all the Plex servers for which you have access.

Parameters:
  • token (str) – the Plex str access token, returned by checkServerCredentials.
  • verify (bool) – optional bool argument, whether to verify SSL connections. Default is True.
Returns:

a dictionary of servers accessible to you. Each key is the Plex server’s name, and the value is a dict that looks like this.

{
  'owned'        : OWNED, # boolean on whether server owned by you
  'access token' : TOKEN, # string access token to server
  'url'          : URL    # remote URL of the form https://IP-ADDRESS:PORT
}
Return type:dict
howdy.core.core.get_current_date_newsletter()

the last date and time at which the Howdy email newsletter was updated.

Returns:the date and time of the most recent previous email newsletter.
Return type:datetime.
howdy.core.core.get_date_from_datestring(dstring)

Returns a date object from a date string with the format, “January 1, 2000”.

Parameters:dstring (str) – the initial date string.
Returns:its date object representation.
Return type:date
howdy.core.core.get_email_contacts(token, verify=True)

list of all email addresses of friends who have stream access to your Plex server.

Parameters:
  • token (str) – Plex access token.
  • verify (bool) – optional argument, whether to verify SSL connections. Default is True.
Returns:

list of email addresses of Plex friends.

Return type:

list

howdy.core.core.get_imgurl_credentials()

retrieves the Imgur API credentials from the SQLite3 configuration database.

Returns:a dict of the Imgur API credentials. Its structure is,
{ 'clientID': XXXX,
  'clientSECRET': XXXX,
  'clientREFRESHTOKEN': XXXX,
  'mainALBUMID': XXXX,
  'mainALBUMTITLE': XXXX }
Return type:dict
howdy.core.core.get_jackett_credentials()

retrieves the Jackett server’s API credentials from the SQLite3 configuration database.

Returns:a tuple of the Jackett server’s API credentials. First element is the URL of the Jackett server. Second element is the API key.
Return type:tuple
howdy.core.core.get_lastN_movies(lastN, token, fullURL='http://localhost:32400', useLastNewsletterDate=True)

Returns the last \(N\) movies that were uploaded to the Plex server, either after the last date at which a newsletter was sent out or not.

Parameters:
  • lastN (int) – the last \(N\) movies to be sent out. Must be \(\ge 1\).
  • token (str) – the Plex server access token.
  • fullURL (str) – the Plex server address.
  • useLastNewsletterDate (bool) – if True, then find the last movies after the date of the previous newsletter. If False. don’t make that restriction.
Returns:

a list of Plex movies. Each element in the list is tuple of the movie: title, year, datetime, and The Movie Database <TMDB_> URL of the movie.

Return type:

dict.

howdy.core.core.get_libraries(token, fullURL='http://localhost:32400', do_full=False, timeout=None)

Gets the dict of libraries on the Plex server. The key is the library number, while the value can be either the name of the library or a tuple of library name and library type

Parameters:
  • token (str) – the Plex server access token.
  • fullURL (str) – the Plex server address.
  • do_full (bool) – if False, then the values are the names of the Plex libraries. If True, then the values are tuple of the library name and library type. The library type can be one of movie, show, or artist.
Returns:

a dictionary of libraries on the Plex server.

Return type:

dict.

howdy.core.core.get_library_data(title, token, fullURL='http://localhost:32400', num_threads=2, timeout=None)

Returns the data on the specific Plex library, as a dict. This lower level functionality lives in the same space as PlexAPI. Three types of library data can be returned: movies, TV shows, and music.

  • Movie data has this JSON like structure. moviedata is an example movie data dictionary.
    • moviedata is a dict whose keys (of type str) are the main movie genres in the Plex library, such as comedy.
    • Each value in moviedata[<genre>] is a list of movies of that (main) genre.
    • Each movie in moviedata[<genre>] is a dict with the following ten keys and values.
      • title: str movie’s name.
      • rating: float the movie’s quality rating as a number between 0.0 and 10.0.
      • contentrating: str the MPAA content rating for the movie (such as PG, PG-13, R, or NR).
      • picurl: str URL of the movie’s poster on the Plex server.
      • releasedate: date of when the movie was first released.
      • addedat: date of when the movie was added to the Plex server.
      • summary: str plot summary of the movie.
      • duration: float movie’s duration in seconds.
      • totsize: int size of the movie file in bytes.
      • localpic: bool currently unused variable, always True.
    • An example moviedata dict with one genre (comedy) and ten highly rated movies can be found in moviedata example in JSON format.
  • TV data has this JSON like structure. tvdata is an example TV data dictionary.
    • tvdata is a dict whose keys are the individual TV shows.
    • Each value in tvdata[<showname>] is a dictionary with four keys: title (str name of the show, <showname>), summary (str description of the show), picturl (str URL of the poster for the show), and seasons (dict whose keys are the seasons of the show).
    • tvdata[<showname>]['seasons'] is a dict whose keys are the seasons. If the show has specials, then those episodes are in season 0.
      • this dict has two keys: seasonpicurl (str URL of the poster for the season), and episodes (dict of the episodes for that season).
      • tvdata[<showname>]['seasons']['episodes'] is a dict whose keys are the episode numbers, and whose value is a dict with the following nine keys and values.
        • title: str title of the episode.
        • episodepicurl: str URL of the poster of the episode.
        • date aired: date of when the episode first aired.
        • summary: str summary of the episode’s plot.
        • duration:: float episode duration in seconds.
        • size: int size of the episode file in bytes.
        • path: str path, on the Plex server, of the episode file.
        • director: list of the episode’s directors.
        • writer: list of the episode’s writers.
    • An example tvdata dict with one finished HBO show, The Brink, can be found in tvdata example in JSON format.
  • Music data has this JSON like structure. musicdata is an example music data dictionary.
    • musicdata is a dict whose keys are the individual artists or groups (such as The Beatles).
    • Each value in musicdata[<artistname>] is a dict of albums associated with <artistname> on the Plex server.
    • The key is the album name, and the value is another dict with three keys.
      • year: the year of release.
      • picurl: the URL of album cover on the Plex_server.
      • ``tracks``L contains the list of songs associated with this artist and album on the Plex server.
    • musicdata[<artistname>][<albumname>]['tracks'] is a list of dicts representing each song that is on the Plex server. Each song dict has the following keys and values.
      • track_name: the song name.
      • curdate: the date on which this song was added to the Plex music library.
      • duration: song length in seconds.
      • size: song file size in bytes.
      • track: track number in the album.
      • file: the song file’s location on the Plex server.
    • An example musicdata dict with one artist, The Beatles, can be found in musicdata example. Since date is not JSON-serializable, I have represented each curdate as a string in this format: DAY MONTH YEAR (such as 13 January 2020).
Parameters:
  • title (str) – the name of the library.
  • token (str) – the Plex server access token.
  • fullURL (str) – the Plex server address.
  • num_threads (int) – the number of concurrent threads used to access the Plex server and get the library data.
  • timeout (int) – optional time, in seconds, to wait for an HTTP conection to the Plex server.
Returns:

a dict of library data on the Plex server.

Return type:

dict

howdy.core.core.get_library_stats(key, token, fullURL='http://localhost:32400', sinceDate=None)

Gets summary data on a specific library on the Plex server. returned as a dict. The Plex library mediatype can be one of movie (Movies), show (TV shows), or artist (Music). The common part of the dict looks like the following.

{
  'fullURL'   : FULLURL,   # URL of the Plex server in the form of https://IP-ADDRESS:PORT
  'title'     : TITLE,     # name of the Plex library
  'mediatype' : MEDIATYPE, # one of "movie", "show", or "artist"
}

Here are what the extra parts of this dictionary look like.

  • If the library is a movie (Movies), then

    {
      'num_movies' : num_movies,     # total number of movies in this library.
      'totdur'     : totdur,         # total duration in seconds of all movies.
      'totsize'    : totsize,        # total size in bytes of all movies.
      'genres'     : sorted_by_genre # another dictionary subdivided by, showing # movies, size, and duration by genre.
    }
    

    sorted_by_genre is also a dict, whose keys are the separate movie genres in this Plex library (such as action, horror, comedy). Each value in this dictionary is another dictionary that looks like this.

    {
      'num_movies' : num_movies_gen, # total number of movies in this genre.
      'totdur'     : totdur_gen,     # total duration in seconds of movies in this genre.
      'totsize'    : totsize_gen,    # total size in bytes of movies in this genre.
    }
    
  • If the library is a show (TV Shows), then

    {
      'num_tveps'   : num_tveps,   # total number of TV episodes in this library.
      'num_tvshows' : num_tvshows, # total number of TV shows in this library.
      'totdur'      : totdur,      # total duration in seconds of all TV shows.
      'totsize'     : totsize      # otal size in bytes of all TV shows.
    }
    
  • If the library is an artist (Music), then

    {
      'num_songs'   : num_songs,   # total number of songs in this library.
      'num_albums'  : num_albums,  # total number of albums in this library.
      'num_artists' : num_artists, # total number of unique artists in this library.
      'totdur'      : totdur,      # total duration in seconds of all songs.
      'totsize'     : totsize      # total size in bytes of all songs.
    }
    
Parameters:
  • key (int) – the key number of the library in the Plex server.
  • token (str) – the Plex server access token.
  • fullURL (str) – the Plex server address.
  • sinceDate – If defined, only tally the library media that was added after this date. This is of type date.
Returns:

a dict of summary statistics on the Plex library.

Return type:

dict

See also

get_library_data.

howdy.core.core.get_mapped_email_contacts(token, verify=True)

list of all email addresses (including Plex server friends and mapped emails) to send Howdy related emails.

Parameters:
  • token (str) – Plex access token.
  • verify (bool) – optional argument, whether to verify SSL connections. Default is True.
Returns:

a list of email addresses for Howdy emails.

Return type:

list

howdy.core.core.get_movies_libraries(token, fullURL='http://localhost:32400')

Returns a list of the key numbers of all Plex movie libraries on the Plex server.

Parameters:
  • token (str) – the Plex server access token.
  • fullURL (str) – the Plex server address.
Returns:

a list of the Plex movie library key numbers.

Return type:

list.

howdy.core.core.get_pic_data(plexPICURL, token=None)

Get the PNG data as a Response object, from a movie picture URL on the Plex server.

Parameters:
  • plexPICURL (str) – the movie picture URL.
  • token (str) – the Plex access token.
Returns:

the PNG data for the movie image.

Return type:

Response

howdy.core.core.get_updated_at(token, fullURL='https://localhost:32400')

Get the date and time at which the Plex server was last updated, as a datetime object.

Parameters:
  • token (str) – the Plex access token.
  • fullURL (str) – the Plex server URL.
Returns:

the date and time of the Plex server’s last update.

Return type:

datetime

howdy.core.core.oauthCheckGoogleCredentials(bypass=False)

Checks whether the Google OAuth2 authentication settings exist in the SQLite3 configuration database. The format of the authentication data in the configuration database is,

{ 'access_token': XXXX,
  'client_id': YYYY,
  'client_secret': ZZZZ,
  'refresh_token': AAAA,
  'token_expiry': BBBB,
  'token_uri': 'https://accounts.google.com/o/oauth2/token',
  'user_agent': None,
  'revoke_uri': 'https://oauth2.googleapis.com/revoke',
  'id_token': None,
  'id_token_jwt': None,
  'token_response': { 'access_token': XXXX,
    'expires_in': 3600,
    'refresh_token': AAAA,
    'scope': 'https://www.googleapis.com/auth/gmail.send https://www.googleapis.com/auth/contacts.readonly https://www.googleapis.com/auth/youtube.readonly https://www.googleapis.com/auth/musicmanager https://www.googleapis.com/auth/spreadsheets',
    'token_type': 'Bearer' },
  'scopes': ['https://www.googleapis.com/auth/gmail.send',
    'https://spreadsheets.google.com/feeds',
    'https://www.googleapis.com/auth/youtube.readonly',
    'https://www.googleapis.com/auth/contacts.readonly',
    'https://www.googleapis.com/auth/musicmanager'],
  'token_info_uri': 'https://oauth2.googleapis.com/tokeninfo',
  'invalid': False,
  '_class': 'OAuth2Credentials',
  '_module': 'oauth2client.client' }

If data is this dictionary, note the scopes defined in the data['scopes'] and data['token_response']['scope'].

Parameters:bypass (bool) – if True, then ignore what exists in the database under google crdentials.
Returns:a tuple of status and message. If the settings are in the database, returns ( True, 'SUCCESS' ). If they are not, returns ( False, 'GOOGLE AUTHENTICATION CREDENTIALS DO NOT EXIST.' ).
Return type:tuple.
howdy.core.core.oauthGetGoogleCredentials(verify=True)

Gets the Google Oauth2 credentials, stored in the SQLite3 configuration database, in the form of a refreshed Credentials object. This OAuth2 authentication method is used for ALL the services accessed by Howdy.

Parameters:verify (bool) – optional argument, whether to verify SSL connections. Default is True.
Returns:a Credentials form of the Google Oauth2 credentials for various Oauth2 services.
Return type:Credentials
howdy.core.core.oauthGetOauth2ClientGoogleCredentials()

Gets the Google Oauth2 credentials, stored in the SQLite3 configuration database, in the form of a refreshed AccessTokenCredentials object. This OAuth2 authentication method IS used for all the services accessed by Howdy.

Returns:a AccessTokenCredentials form of the Google Oauth2 credentials for various Oauth2 services.
Return type:AccessTokenCredentials
howdy.core.core.oauth_generate_google_permission_url()

Generates a Google OAuth2 web-based flow for all the Google services used in Howdy. The authentication process that uses this flow is described in this subsection. Here are the programmatic steps to finally generate an AccessTokenCredentials object.

  1. Get the OAuth2WebServerFlow and authentication URI.

    flow, auth_uri = oauth_generate_google_permission_url( )
    
  2. Go to the URL, auth_uri, in a browser, grant permissions, and copy the authorization code in the browser window. This authorization code is referred to as authorization_code.

  3. Create the AccessTokenCredentials using authorization_code.

    credentials = flow.step2_exchange( authorization_code )
    
Returns:a tuple of two elements. The first element is an OAuth2WebServerFlow web server flow object. The second element is the redirection URI string that redirects the user to begin the authorization flow.
Return type:tuple
howdy.core.core.oauth_store_google_credentials(credentials)

Store the Google OAuth2 credentials, in the form of a AccessTokenCredentials object, into the SQLite3 configuration database.

Parameters:credentials – the AccessTokenCredentials object to store into the database.
howdy.core.core.processValidHTMLWithPNG(html, pngDataDict, doEmbed=False)

Returns a prettified HTML document, using BeautifulSoup, including all PNG image data (whether URLs or Base 64 encoded data) in <img> tags.

Parameters:
  • html (str) – the initial HTML document into which images are to be embedded.
  • pngDataDict (dict) – dictionary of PNG data. Key is the name of the PNG file (must end in .png). Value is a tuple of type (b64data, widthInCM, url). b64data is the Base 64 encoded binary representation of the PNG image. widthInCm is the image width in cm. url is the URL address of the image.
  • doEmbed (bool) – If True, then the image source tag uses the Base 64 encoded data. If False, the image source tag is the URL.
Returns:

prettified HTML document with the images located in it.

Return type:

str

howdy.core.core.pushCredentials(username, password)

replace the Plex server credentials, located in ~/.config/howdy/app.db, with a new username and password.

Parameters:
  • username (str) – the Plex account username.
  • password (str) – the Plex account password.
Returns:

if successful, return a string, SUCCESS. If unsuccessful, returns a string reason of why it failed.

Return type:

str

See also

getCredentials.

howdy.core.core.refresh_library(key, library_dict, token, fullURL='http://localhost:32400')

Lower level front-end to plex_resynclibs.py that refreshes a Plex server library. Here I use instructions found in this Plex forum article on URL commands.

Parameters:
  • key (int) – the library number on the Plex server.
  • library_dict (dict) – the dictionary of libraries. The key is Plex server library number, and the value is the library name.
  • token (str) – the Plex access token.
  • fullURL (str) – the Plex server URL.
howdy.core.core.rstToHTML(rstString)

Converts a reStructuredText string into HTML, then prettifies the intermediate HTML using BeautifulSoup.

Parameters:rstString (str) – the initial restructuredText string.
Returns:the final prettified, formatted HTML string.
Return type:str
howdy.core.core.set_date_newsletter()

sets the date of the Plex newsletter to now().

howdy.core.core.store_imgurl_credentials(clientID, clientSECRET, clientREFRESHTOKEN, verify=True, mainALBUMID=None, mainALBUMNAME=None)

stores the Imgur API credentials into the SQLite3 configuration database.

Parameters:
  • clientID (str) – the Imgur client ID.
  • clientSECRET (str) – the Imgur client secret.
  • clientREFRESHTOKEN (str) – the Imgur client refresh token.
  • verify (bool) – optional argument, whether to verify SSL connections. Default is True.
  • mainALBUMID (str) – optional argument. If given, then store this album hash into the database.
  • mainALBUMNAME (str) – optional argument. If given, then store this album hash into the database.
Returns:

the string "SUCCESS" if could store the new Imgur credentials. Otherwise, the string 'ERROR, COULD NOT STORE IMGURL CREDENTIALS.'.

Return type:

str

howdy.core.core.store_jackett_credentials(url, apikey, verify=True)

stores the Jackett server’s API credentials into the SQLite3 configuration database.

Parameters:
  • url (str) – the Jackett server’s URL.
  • apikey (str) – the Jackett server’s API key.
  • verify (bool) – optional argument, whether to verify SSL connections. Default is True.
Returns:

the string "SUCCESS" if could store the new Jackett server credentials. Otherwise, some illuminating error message.

Return type:

str

4.3.3. howdy.core.core_deluge module

This module implements the functionality to interact with a Seedhost seedbox Deluge torrent server, by copying a minimal set of the functionality of a Deluge torrent client. The data formatting in this module is largely or wholly copied from the Deluge SDK. The much reduced Deluge torrent client, howdy_deluge_console, is a CLI front-end to this module.

howdy.core.core_deluge.create_deluge_client(url, port, username, password)

Creates a minimal Deluge torrent client to the Deluge seedbox server.

Parameters:
  • url (str) – URL of the Deluge server.
  • port (int) – port used to access the Deluge server.
  • username (str) – server account username.
  • password (str) – server account password.
Returns:

a lightweight Deluge RPC client.

howdy.core.core_deluge.deluge_add_magnet_file(client, magnet_uri)

Uploads a Magnet URI to the Deluge server through the Deluge RPC client.

Parameters:
Returns:

if successful, returns the MD5 hash of the uploaded torrent as a str. If unsuccessful, returns None.

howdy.core.core_deluge.deluge_add_torrent_file(client, torrent_file_name)

Higher level method that takes a torrent file on disk and uploads to the Deluge server through the Deluge RPC client.

Parameters:
Returns:

if successful, returns the MD5 hash of the uploaded torrent as a str. If unsuccessful, returns None.

howdy.core.core_deluge.deluge_add_torrent_file_as_data(client, torrent_file_name, torrent_file_data)

Higher level method that takes a torrent file name, and its byte data representation, and uploads to the Deluge server through the Deluge RPC client.

Parameters:
  • client – the Deluge RPC client.
  • torrent_file_name (str) – name of the candidate file.
  • torrent_file_data (byte) – byte representation of the torrent file data.
Returns:

if successful, returns the MD5 hash of the uploaded torrent as a str. If unsuccessful, returns None.

howdy.core.core_deluge.deluge_add_url(client, torrent_url)

Adds a torrent file via URL to the Deluge server through the Deluge RPC client. If the URL is valid, then added. If the URL is invalid, then nothing happens.

Parameters:
howdy.core.core_deluge.deluge_format_info(status, torrent_id)

Returns a nicely formatted representation of the status of a torrent.

>>> print( '%s' % deluge_format_info( status, 'ed53ba61555cab24946ebf2f346752805601a7fb' ) )

Name: ubuntu-19.10-beta-desktop-amd64.iso
ID: ed53ba61555cab24946ebf2f346752805601a7fb
State: Downloading
Down Speed: 73.4 MiB/s Up Speed: 0.0 KiB/s ETA: 0 days 00:00:23
Seeds: 24 (67) Peers: 1 (4) Availability: 24.22
Size: 474.5 MiB/2.1 GiB Ratio: 0.000
Seed time: 0 days 00:00:00 Active: 0 days 00:00:05
Tracker status: ubuntu.com: Announce OK
Progress: 21.64% [##################################~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~]
Parameters:
Returns:

a nicely formatted representation of that torrent on the Deluge server.

Return type:

str

howdy.core.core_deluge.deluge_get_matching_torrents(client, torrent_id_strings)

Given a list of possibly truncated MD5 hashes of candidate torrents on the Deluge server, returns a list of MD5 sums of torrents that match what was provided.

Parameters:
  • client – the Deluge RPC client.
  • torrent_id_strings – the candidate list of truncated MD5 hashes on the Deluge server. The [ '*' ] input means to look for all torrents on the Deluge server.
Returns:

a list of candidate torrents, as their MD5 hash, tat match torrent_id_strings. If torrent_id_strings == ['*'], then return all the torrents (as MD5 hashes) on the Deluge server.

Return type:

list

howdy.core.core_deluge.deluge_get_torrents_info(client)

Returns a dict of status info for every torrent on the Deluge server through the Deluge RPC client. The key in this dict is the MD5 hash of the torrent, and its value is a status dict. For each torrent, here are the keys in the status dict: active_time, all_time_download, distributed_copies, download_location, download_payload_rate, eta, file_priorities, file_progress, files, is_finished, is_seed, last_seen_complete, name, next_announce, num_peers, num_pieces, num_seeds, peers, piece_length, progress, ratio, seed_rank, seeding_time, state, time_added, time_since_transfer, total_done, total_payload_download, total_payload_upload, total_peers, total_seeds, total_size, total_uploaded, tracker_host, tracker_status, upload_payload_rate.

Parameters:client – the Deluge RPC client.
Returns:a dict of status dict for each torrent on the Deluge server.
Return type:dict
howdy.core.core_deluge.deluge_is_torrent_file(torrent_file_name)

Check if a file is a torrent file.

Parameters:torrent_file_name (str) – name of the candidate file.
Returns:True if it is a torrent file, False otherwise.
Return type:bool
howdy.core.core_deluge.deluge_is_url(torrent_url)

Checks whether an URL is valid, following this prescription.

Parameters:torrent_url (str) – candidate URL.
Returns:True if it is a valid URL, False otherwise.
Return type:bool
howdy.core.core_deluge.deluge_pause_torrent(client, torrent_ids)

Pauses torrents on the Deluge server. Unlike other methods here, this does not use the Deluge RPC client lower-level RPC calls, but system command line calls to the deluge-console client. If the deluge-console executable cannot be found, then this does nothing. I do not know the Deluge RPC client is not working.

Parameters:
  • client – the Deluge RPC client. In this case, only the configuration info (username, password, URL, and port) are used.
  • torrent_idslist of MD5 hashes on the Deluge server.
howdy.core.core_deluge.deluge_remove_torrent(client, torrent_ids, remove_data=False)

Remove torrents from the Deluge server through the Deluge RPC client.

Parameters:
  • client – the Deluge RPC client.
  • torrent_idslist of MD5 hashes on the Deluge server.
  • remove_data (bool) – if True, remove the torrent and delete all data associated with the torrent on disk. If False, just remove the torrent.
howdy.core.core_deluge.deluge_resume_torrent(client, torrent_ids)

Resumes torrents on the Deluge server. Unlike other methods here, this does not use the Deluge RPC client lower-level RPC calls, but system command line calls to the deluge-console client. If the deluge-console executable cannot be found, then this does nothing. I do not know the Deluge RPC client is not working.

Parameters:
  • client – the Deluge RPC client. In this case, only the configuration info (username, password, URL, and port) are used.
  • torrent_idslist of MD5 hashes on the Deluge server.
howdy.core.core_deluge.format_progressbar(progress, width)

Returns a string of a progress bar. This code has been copied from deluge's format_progressbar.

Parameters:progress (float) – a value between 0-100.
Returns:str, a progress bar based on width.
Return type:str

Usage

>>> format_progressbar( 87.6, 100 )
'[######################################################################################~~~~~~~~~~~~]'
howdy.core.core_deluge.format_size(fsize_b)

Formats the bytes value into a string with KiB, MiB or GiB units. This code has been copied from deluge's format_size.

Parameters:fsize_b (int) – the filesize in bytes.
Returns:formatted string in KiB, MiB or GiB units.
Return type:str

Usage

>>> format_size( 112245 )
'109.6 KiB'
howdy.core.core_deluge.format_speed(bps)

Formats a string to display a transfer speed utilizing fsize(). This is code has been copied from deluge's format_speed.

Parameters:bps (int) – bytes per second.
Returns:a formatted string representing transfer speed
Return type:str

Usage

>>> format_speed( 43134 )
'42.1 KiB/s'
howdy.core.core_deluge.format_time(seconds)

Formats the time, in seconds, to a nice format. Unfortunately, the datetime class is too unwieldy for this type of formatting. This code is copied from deluge's format_time.

Parameters:seconds (int) – number of seconds.
Returns:formatted string in the form of 1 days 03:05:04.
Return type:str

Usage

>>> format_time( 97262 )
'1 days 03:01:02'
howdy.core.core_deluge.get_deluge_client()

Using a minimal Deluge torrent client from server credentials stored in the SQLite3 configuration database.

Returns:a tuple. If successful, the first element is a lightweight Deluge RPC client and the second element is the string 'SUCCESS'. If unsuccessful, the first element is None and the second element is an error string.
Return type:tuple
howdy.core.core_deluge.get_deluge_credentials()

Gets the Deluge server credentials from the SQLite3 configuration database. The data looks like this.

{ 'url': XXXX,
  'port': YYYY,
  'username': AAAA,
  'password': BBBB }
Returns:dictionary of Deluge server settings.
Return type:dict
howdy.core.core_deluge.push_deluge_credentials(url, port, username, password)

Stores the Deluge server credentials into the SQLite3 configuration database.

Parameters:
  • url (str) – URL of the Deluge server.
  • port (int) – port used to access the Deluge server.
  • username (str) – server account username.
  • password (str) – server account password.
Returns:

if successful (due to correct Deluge server settings), returns 'SUCCESS'. If unsuccessful, returns 'ERROR, INVALID SETTINGS FOR DELUGE CLIENT.'

Return type:

str

4.3.4. howdy.core.core_rsync module

This module implements the functionality to interact with a Seedhost seedbox SSH server to download or upload files and directories using the rsync protocol tunneled through SSH. rsync_subproc is a CLI front-end to this module.

howdy.core.core_rsync.check_credentials(local_dir, sshpath, password, subdir=None)

Checks whether one can download to (or upload from) the directory local_dir on the Plex server to the remote SSH server.

Parameters:
  • local_dir (str) – the local directory, on the Plex server, into which to download files from the remote SSH server.
  • sshpath (str) – the full path with username and host name for the SSH server. Format is 'username@hostname'.
  • password (str) – the password to connect as the SSH server.
  • subdir (str) – if not None, the subdirectory on the remote SSH server from which to download files.
Returns:

if everything works, return 'SUCCESS'. If fails, return specific illuminating error messages.

Return type:

str

howdy.core.core_rsync.download_upload_files(glob_string, numtries=10, debug_string=False, do_reverse=False)

Run the system process, using rsync, to download files and directories from, or upload to, the remote SSH server. On completion, the source files are all deleted.

Parameters:
  • glob_string (str) – the description of files and directories (such as '*.mkv' to represent all MKV files) to upload/download.
  • numtries (int) – the number of attempts to run rsync before giving up on uploading or downloading.
  • debug_string (bool) – if True, then print out the password-stripped rsync command that is being run.
  • do_reverse (bool) – if True, then upload files to remote SSH server. If False (the default), then download files from the SSH server.
howdy.core.core_rsync.get_credentials()

Returns the rsync’ing setup from the SQLite3 configuration database as a dict in the following form.

{ 'local_dir' : XXXX,
  'sshpath' : YYYY@ZZZZ,
  'password' : AAAA,
  'subdir' : BBBB }
Returns:the dict of the rsync’ing setup if in the SQLite3 configuration database, otherwise None.
Return type:dict
howdy.core.core_rsync.get_rsync_command(data, mystr, do_download=True)

Returns a tuple of the actual rsync command, and the command with password information obscured, to download from or upload to the remote SSH server from the Plex server. We require that sshpass_ exists and is accessible on this system.

Parameters:
  • data – the dict of rsync’ing configuration, described in get_credentials.
  • mystr (str) – the specific rsync syntax describing those files/folders to upload or download. See, e.g., this website for some practical examples of file or directory listings.
  • do_download (bool) – if True, then download from remote SSH server. If False, then upload to remote SSH server.
Returns:

a tuple of the actual rsync command, and the command with password information obscured, to download from or upload to the remote SSH server from the Plex server.

Return type:

tuple

howdy.core.core_rsync.push_credentials(local_dir, sshpath, password, subdir=None)

Push the rsync’ing setup (local_dir, sshpath, password, and subdir), if working, into the SQLite3 configuration database.

Parameters:
  • local_dir (str) – the local directory, on the Plex server, into which to download files from the remote SSH server.
  • sshpath (str) – the full path with username and host name for the SSH server. Format is 'username@hostname'.
  • password (str) – the password to connect as the SSH server.
  • subdir (str) – if not None, the subdirectory on the remote SSH server from which to download files.
Returns:

if successful, return 'SUCCESS'. If not, return error messages.

Return type:

str

4.3.5. howdy.core.core_torrents module

This is a newer module. It implements higher level interfaces to the Jackett torrent searching server to search for ebooks. get_book_tor is its only CLI front-end.

howdy.core.core_torrents.get_book_torrent_jackett(name, maxnum=10, keywords=[], verify=True)

Returns a tuple of candidate book Magnet links found using the main Jackett torrent searching service and the string "SUCCESS", if successful.

Parameters:
  • name (str) – the book to search for.
  • maxnum (int) – optional argumeent, the maximum number of magnet links to return. Default is 10. Must be \(\ge 5\).
  • keywords (list) – optional argument. If not empty, the title of the candidate element must have at least one of the keywords in keywords.
  • verify (bool) – optional argument, whether to verify SSL connections. Default is True.
Returns:

if successful, then returns a two member tuple the first member is a list of elements that match the searched episode, ordered from most seeds and leechers to least. The second element is the string "SUCCESS". The keys in each element of the list are,

  • title is the name of the candidate book to download, and in parentheses the size of the candidate in MB or GB.
  • rawtitle also the name of the candidate episode to download.
  • seeders is the number of seeds for this Magnet link.
  • leechers is the number of leeches for this Magnet link.
  • link is the Magnet URI link.
  • torrent_size is the size of this torrent in Megabytes.

If this is unsuccessful, then returns an error tuple of the form returned by return_error_raw.

Return type:tuple