2021-04-09 09:13:06 -05:00
# main.py
from logging import error
import sys
from trakt import init
from trakt . movies import get_recommended_movies
import trakt . core
import os
from trakt . tv import TVShow
import csv
from datetime import datetime
import time
from tinydb import TinyDB , Query
import json
import re
import sys
2021-04-09 11:13:11 -05:00
# Adjust this value to increase/decrease your requests between episodes.
# Make sure it's above 1 seconds to remain within the rate limit.
DELAY_BETWEEN_EPISODES_IN_SECONDS = 5
2021-04-09 09:13:06 -05:00
# Create a database to keep track of completed processes
database = TinyDB ( ' localStorage.json ' )
syncedEpisodesTable = database . table ( ' SyncedEpisodes ' )
userMatchedShowsTable = database . table ( ' TvTimeTraktUserMatched ' )
class Expando ( object ) :
pass
def getConfiguration ( ) :
configEx = Expando ( )
with open ( ' config.json ' ) as f :
data = json . load ( f )
configEx . TRAKT_USERNAME = data [ " TRAKT_USERNAME " ]
configEx . CLIENT_ID = data [ " CLIENT_ID " ]
configEx . CLIENT_SECRET = data [ " CLIENT_SECRET " ]
configEx . GDPR_WORKSPACE_PATH = data [ " GDPR_WORKSPACE_PATH " ]
2021-04-09 16:05:46 -05:00
CONFIG_SINGLETON = configEx
return CONFIG_SINGLETON
config = getConfiguration ( )
# Return the path to the CSV file contain the watched episode data from TV Time
def getWatchedShowsPath ( ) :
return config . GDPR_WORKSPACE_PATH + " /seen_episode.csv "
2021-04-09 18:39:10 -05:00
def getFollowedShowsPath ( ) :
return config . GDPR_WORKSPACE_PATH + " /followed_tv_show.csv "
2021-04-09 16:05:46 -05:00
def initTraktAuth ( ) :
# Set the method of authentication
trakt . core . AUTH_METHOD = trakt . core . OAUTH_AUTH
return init ( config . TRAKT_USERNAME , store = True , client_id = config . CLIENT_ID , client_secret = config . CLIENT_SECRET )
# With a given title, check if it contains a year (e.g Doctor Who (2005))
# and then return this value, with the title and year removed to improve
# the accuracy of Trakt results.
2021-04-09 09:13:06 -05:00
def getYearFromTitle ( title ) :
ex = Expando ( )
try :
2021-04-09 16:05:46 -05:00
# Use a regex expression to get the value within the brackets e.g The Americans (2017)
2021-04-09 09:13:06 -05:00
yearSearch = re . search ( r " \ (([A-Za-z0-9_]+) \ ) " , title )
yearValue = yearSearch . group ( 1 )
2021-04-09 16:05:46 -05:00
# Then, get the title without the year value included
2021-04-09 09:13:06 -05:00
titleValue = title . split ( ' ( ' ) [ 0 ] . strip ( )
2021-04-09 16:05:46 -05:00
# Put this together into an object
2021-04-09 09:13:06 -05:00
ex . titleWithoutYear = titleValue
ex . yearValue = int ( yearValue )
return ex
except :
2021-04-09 16:05:46 -05:00
# If the above failed, then the title doesn't include a year
# so return the object as is.
2021-04-09 09:13:06 -05:00
ex . titleWithoutYear = title
ex . yearValue = - 1
return ex
2021-04-09 16:05:46 -05:00
# Shows in TV Time are often different to Trakt.TV - in order to improve results and automation,
# calculate how many words are in the title, and return true if more than 50% of the title is a match,
# It seems to improve automation, and reduce manual selection....
2021-04-09 09:13:06 -05:00
def checkTitleNameMatch ( tvTimeTitle , traktTitle ) :
2021-04-09 16:05:46 -05:00
# If the name is a complete match, then don't bother comparing them!
2021-04-09 09:13:06 -05:00
if tvTimeTitle == traktTitle :
return True
# Split the TvTime title
tvTimeTitleSplit = tvTimeTitle . split ( )
2021-04-09 16:05:46 -05:00
# Create an array of words which are found in the Trakt title
2021-04-09 09:13:06 -05:00
wordsMatched = [ ]
2021-04-09 16:05:46 -05:00
# Go through each word of the TV Time title, and check if it's in the Trakt title
2021-04-09 09:13:06 -05:00
for word in tvTimeTitleSplit :
if word in traktTitle :
wordsMatched . append ( word )
2021-04-09 16:05:46 -05:00
# Then calculate what percentage of words matched
2021-04-09 09:13:06 -05:00
quotient = len ( wordsMatched ) / len ( traktTitle . split ( ) )
percentage = quotient * 100
2021-04-09 16:05:46 -05:00
# If more than 50% of words in the TV Time title exist in the Trakt title,
# then return the title as a possibility to use
2021-04-09 09:13:06 -05:00
return percentage > 50
2021-04-09 16:05:46 -05:00
# Using TV Time data (Name of Show, Season No and Episode) - find the corresponding show
# in Trakt.TV either by automation, or asking the user to confirm.
2021-04-09 09:13:06 -05:00
def getShowByName ( name , seasonNo , episodeNo ) :
2021-04-09 16:05:46 -05:00
# Parse the TV Show's name for year, if one is present in the string
2021-04-09 09:13:06 -05:00
titleObj = getYearFromTitle ( name )
2021-04-09 16:05:46 -05:00
# Create a boolean to indicate if the title contains a year,
# this is used later on to improve the accuracy of picking
# from search results
2021-04-09 09:13:06 -05:00
doesTitleIncludeYear = titleObj . yearValue != - 1
2021-04-09 16:05:46 -05:00
# If the title contains a year, then replace the local variable with the stripped version
2021-04-09 09:13:06 -05:00
if doesTitleIncludeYear :
name = titleObj . titleWithoutYear
2021-04-09 16:05:46 -05:00
# Request the Trakt API for search results, using the name
2021-04-09 09:13:06 -05:00
tvSearch = TVShow . search ( name )
2021-04-09 16:05:46 -05:00
# Create an array of shows which have been matched
2021-04-09 09:13:06 -05:00
showsWithSameName = [ ]
2021-04-09 16:05:46 -05:00
# Go through each result from the search
2021-04-09 09:13:06 -05:00
for show in tvSearch :
2021-04-09 16:05:46 -05:00
# Check if the title is a match, based on our conditions (e.g over 50% of words match)
2021-04-09 09:13:06 -05:00
if checkTitleNameMatch ( name , show . title ) :
2021-04-09 16:05:46 -05:00
# If the title included the year of broadcast, then we can be more picky in the results
# to look for a show with a broadcast year that matches
2021-04-09 09:13:06 -05:00
if doesTitleIncludeYear == True :
2021-04-09 16:05:46 -05:00
# If the show title is a 1:1 match, with the same broadcast year, then bingo!
2021-04-09 09:13:06 -05:00
if ( name == show . title ) and ( show . year == titleObj . yearValue ) :
2021-04-09 16:05:46 -05:00
# Clear previous results, and only use this one
2021-04-09 09:13:06 -05:00
showsWithSameName = [ ]
showsWithSameName . append ( show )
break
2021-04-09 16:05:46 -05:00
# Otherwise, only add the show if the broadcast year matches
2021-04-09 09:13:06 -05:00
if show . year == titleObj . yearValue :
showsWithSameName . append ( show )
2021-04-09 16:05:46 -05:00
# If the program doesn't have the broadcast year, then add all the results
2021-04-09 09:13:06 -05:00
else :
showsWithSameName . append ( show )
2021-04-09 16:05:46 -05:00
# Sweep through the results once more for 1:1 title name matches,
# then if the list contains one entry with a 1:1 match, then clear the array
# and only use this one!
2021-04-09 09:13:06 -05:00
completeMatchNames = [ ]
for nameFromSearch in showsWithSameName :
if nameFromSearch . title == name :
completeMatchNames . append ( nameFromSearch )
if ( len ( completeMatchNames ) == 1 ) :
showsWithSameName = completeMatchNames
2021-04-09 16:05:46 -05:00
# If the search contains multiple results, then we need to confirm with the user which show
# the script should use, or access the local database to see if the user has already provided
# a manual selection
2021-04-09 09:13:06 -05:00
if len ( showsWithSameName ) > 1 :
2021-04-09 16:05:46 -05:00
# Query the local database for existing selection
2021-04-09 09:13:06 -05:00
userMatchedQuery = Query ( )
queryResult = userMatchedShowsTable . search (
userMatchedQuery . ShowName == name )
2021-04-09 16:05:46 -05:00
# If the local database already contains an entry for a manual selection
# then don't bother prompting the user to select it again!
2021-04-09 09:13:06 -05:00
if len ( queryResult ) == 1 :
2021-04-09 16:05:46 -05:00
# Get the first result from the query
2021-04-09 09:13:06 -05:00
firstMatch = queryResult [ 0 ]
2021-04-09 16:05:46 -05:00
# Get the value contains the selection index
2021-04-09 09:13:06 -05:00
firstMatchSelectedIndex = int ( firstMatch . get ( ' UserSelectedIndex ' ) )
2021-04-09 16:05:46 -05:00
# Check if the user previously requested to skip the show
2021-04-09 09:13:06 -05:00
skipShow = firstMatch . get ( ' SkipShow ' )
2021-04-09 16:05:46 -05:00
# If the user did not skip, but provided an index selection, get the
# matching show
2021-04-09 09:13:06 -05:00
if skipShow == False :
return showsWithSameName [ firstMatchSelectedIndex ]
2021-04-09 16:05:46 -05:00
# Otherwise, return None, which will trigger the script to skip
# and move onto the next show
2021-04-09 09:13:06 -05:00
else :
return None
2021-04-09 16:05:46 -05:00
# If the user has not provided a manual selection already in the process
# then prompt the user to make a selection
2021-04-09 09:13:06 -05:00
else :
print (
2021-04-09 16:05:46 -05:00
f " INFO - MANUAL INPUT REQUIRED: The TV Time data for Show ' { name } ' (Season { seasonNo } , Episode { episodeNo } ) has { len ( showsWithSameName ) } matching Trakt shows with the same name. " )
2021-04-09 09:13:06 -05:00
2021-04-09 16:05:46 -05:00
# Output each show for manual selection
2021-04-09 09:13:06 -05:00
for idx , item in enumerate ( showsWithSameName ) :
2021-04-09 16:05:46 -05:00
# Display the show's title, broadcast year, amount of seasons and a link to the Trakt page.
# This will provide the user with enough information to make a selection.
2021-04-09 09:13:06 -05:00
print (
2021-04-09 18:39:10 -05:00
f " ( { idx + 1 } ) { item . title } - { item . year } - { len ( item . seasons ) } Season(s) - More Info: https://trakt.tv/ { item . ext } " )
2021-04-09 09:13:06 -05:00
while ( True ) :
try :
2021-04-09 16:05:46 -05:00
# Get the user's selection, either a numerical input, or a string 'SKIP' value
2021-04-09 09:13:06 -05:00
indexSelected = ( input (
f " Please make a selection from above (or enter SKIP): " ) )
if indexSelected != ' SKIP ' :
2021-04-09 16:05:46 -05:00
# Since the value isn't 'skip', check that the result is numerical
2021-04-09 18:39:10 -05:00
indexSelected = int ( indexSelected ) - 1
2021-04-09 16:05:46 -05:00
# Exit the selection loop
2021-04-09 09:13:06 -05:00
break
2021-04-09 16:05:46 -05:00
# Otherwise, exit the loop
2021-04-09 09:13:06 -05:00
else :
break
2021-04-09 16:05:46 -05:00
# Still allow the user to provide the exit input, and kill the program
2021-04-09 09:13:06 -05:00
except KeyboardInterrupt :
sys . exit ( " Cancel requested... " )
2021-04-09 16:05:46 -05:00
# Otherwise, the user has entered an invalid value, warn the user to try again
2021-04-09 09:13:06 -05:00
except :
print (
f " Sorry! Please select a value between 0 to { len ( showsWithSameName ) } " )
2021-04-09 16:05:46 -05:00
# If the user entered 'SKIP', then exit from the loop with no selection, which
# will trigger the program to move onto the next episode
2021-04-09 09:13:06 -05:00
if ( indexSelected == ' SKIP ' ) :
2021-04-09 16:05:46 -05:00
# Record that the user has skipped the TV Show for import, so that
# manual input isn't required everytime
2021-04-09 09:13:06 -05:00
userMatchedShowsTable . insert (
{ ' ShowName ' : name , ' UserSelectedIndex ' : 0 , ' SkipShow ' : True } )
return None
2021-04-09 16:05:46 -05:00
# Otherwise, return the selection which the user made from the list
2021-04-09 09:13:06 -05:00
else :
selectedShow = showsWithSameName [ int ( indexSelected ) ]
userMatchedShowsTable . insert (
{ ' ShowName ' : name , ' UserSelectedIndex ' : indexSelected , ' SkipShow ' : False } )
return selectedShow
else :
2021-04-09 18:39:10 -05:00
if ( len ( showsWithSameName ) > 0 ) :
# If the search returned only one result, then awesome!
# Return the show, so the import automation can continue.
return showsWithSameName [ 0 ]
else :
return None
2021-04-09 09:13:06 -05:00
2021-04-09 16:05:46 -05:00
# Since the Trakt.Py starts the indexing of seasons in the array from 0 (e.g Season 1 in Index 0), then
# subtract the TV Time numerical value by 1 so it starts from 0 as well. However, when a TV series includes
# a 'special' season, Trakt.Py will place this as the first season in the array - so, don't subtract, since
# this will match TV Time's existing value.
2021-04-09 09:13:06 -05:00
def parseSeasonNo ( seasonNo , traktShowObj ) :
2021-04-09 16:05:46 -05:00
# Parse the season number into a numerical value
2021-04-09 09:13:06 -05:00
seasonNo = int ( seasonNo )
2021-04-09 16:05:46 -05:00
# Then get the Season Number from the first item in the array
2021-04-09 09:13:06 -05:00
firstSeasonNo = traktShowObj . seasons [ 0 ] . number
2021-04-09 16:05:46 -05:00
# If the season number is 0, then the Trakt show contains a "special" season
2021-04-09 09:13:06 -05:00
if firstSeasonNo == 0 :
2021-04-09 16:05:46 -05:00
# No need to modify the value, as the TV Time value will match Trakt
2021-04-09 09:13:06 -05:00
return seasonNo
2021-04-09 16:05:46 -05:00
# Otherwise, if the Trakt seasons start with no specials, then return the seasonNo,
# but subtracted by one (e.g Season 1 in TV Time, will be 0)
2021-04-09 09:13:06 -05:00
else :
2021-04-09 16:05:46 -05:00
# Only subtract is the TV Time season number is greater than 0.
2021-04-09 09:13:06 -05:00
if seasonNo != 0 :
return seasonNo - 1
2021-04-09 16:05:46 -05:00
# Otherwise, the TV Time season is a special! Then you don't need to change the starting position
2021-04-09 09:13:06 -05:00
else :
return seasonNo
def processWatchedShows ( ) :
2021-04-09 16:05:46 -05:00
# Total amount of rows which have been processed in the CSV file
2021-04-09 09:13:06 -05:00
rowsCount = 0
2021-04-09 16:05:46 -05:00
# Total amount of rows in the CSV file
2021-04-09 09:13:06 -05:00
rowsTotal = 0
2021-04-09 16:05:46 -05:00
# Total amount of errors which have occurred in one streak
2021-04-09 09:13:06 -05:00
errorStreak = 0
2021-04-09 16:05:46 -05:00
# Get the total amount of rows in the CSV file,
# which is helpful for keeping track of progress.
# However, if you have a VERY large CSV file (e.g above 100,000 rows)
# then it might be a good idea to remove this due to the performance
# overhead.
2021-04-09 09:13:06 -05:00
with open ( getWatchedShowsPath ( ) ) as f :
rowsTotal = sum ( 1 for line in f )
2021-04-09 16:05:46 -05:00
# Open the CSV file within the GDPR exported data
2021-04-09 09:13:06 -05:00
with open ( getWatchedShowsPath ( ) , newline = ' ' ) as csvfile :
2021-04-09 16:05:46 -05:00
# Create the CSV reader, which will break up the fields using the delimiter ','
2021-04-09 09:13:06 -05:00
showsReader = csv . reader ( csvfile , delimiter = ' , ' )
2021-04-09 16:05:46 -05:00
# Loop through each line/record of the CSV file
2021-04-09 09:13:06 -05:00
for row in showsReader :
2021-04-09 16:05:46 -05:00
# Increment the row counter to keep track of progress completing the
# records during the import process.
2021-04-09 09:13:06 -05:00
rowsCount + = 1
2021-04-09 16:05:46 -05:00
# Get the name of the TV show
2021-04-09 09:13:06 -05:00
tvShowName = row [ 4 ]
2021-04-09 16:05:46 -05:00
# Ignore the header row
2021-04-09 09:13:06 -05:00
if tvShowName != " tv_show_name " :
2021-04-09 16:05:46 -05:00
# Get the TV Time Episode Id
2021-04-09 09:13:06 -05:00
tvShowEpisodeId = row [ 1 ]
2021-04-09 16:05:46 -05:00
# Get the TV Time Season Number
2021-04-09 09:13:06 -05:00
tvShowSeasonNo = row [ 7 ]
2021-04-09 16:05:46 -05:00
# Get the TV Time Episode Number
2021-04-09 09:13:06 -05:00
tvShowEpisodeNo = row [ 8 ]
2021-04-09 16:05:46 -05:00
# Get the date which the show was marked 'watched' in TV Time
2021-04-09 09:13:06 -05:00
tvShowDateWatched = row [ 5 ]
2021-04-09 16:05:46 -05:00
# Parse the watched date value into a Python type
2021-04-09 09:13:06 -05:00
tvShowDateWatchedConverted = datetime . strptime (
tvShowDateWatched , ' % Y- % m- %d % H: % M: % S ' )
2021-04-09 16:05:46 -05:00
# Query the local database for previous entries indicating that
# the episode has already been imported in the past. Which will
# ease pressure on TV Time's API server during a retry of the import
# process, and just save time overall without needing to create network requests
2021-04-09 09:13:06 -05:00
episodeCompletedQuery = Query ( )
queryResult = syncedEpisodesTable . search (
episodeCompletedQuery . episodeId == tvShowEpisodeId )
2021-04-09 16:05:46 -05:00
# If the query returned no results, then continue to import it into Trakt
2021-04-09 09:13:06 -05:00
if len ( queryResult ) == 0 :
2021-04-09 16:05:46 -05:00
# Create a repeating loop, which will break on success, but repeats on failures
2021-04-09 09:13:06 -05:00
while True :
2021-04-09 16:05:46 -05:00
# If more than 10 errors occurred in one streak, whilst trying to import the episode
# then give up, and move onto the next episode, but warn the user.
2021-04-09 09:13:06 -05:00
if ( errorStreak > 10 ) :
print (
2021-04-09 16:05:46 -05:00
f " WARNING: An error occurred 10 times in a row... skipping episode... " )
2021-04-09 09:13:06 -05:00
break
try :
2021-04-09 16:05:46 -05:00
# Sleep for a second between each process, before going onto the next watched episode.
# This is required to remain within the API rate limit, and use the API server fairly.
# Other developers share the service, for free - so be considerate of your usage.
2021-04-09 11:13:11 -05:00
time . sleep ( DELAY_BETWEEN_EPISODES_IN_SECONDS )
2021-04-09 16:05:46 -05:00
# Search Trakt for the TV show matching TV Time's title value
2021-04-09 09:13:06 -05:00
traktShowObj = getShowByName (
tvShowName , tvShowSeasonNo , tvShowEpisodeNo )
2021-04-09 16:05:46 -05:00
# If the method returned 'None', then this is an indication to skip the episode, and
# move onto the next one
2021-04-09 09:13:06 -05:00
if traktShowObj == None :
break
2021-04-09 16:05:46 -05:00
# Show the progress of the import on-screen
print (
f " ( { rowsCount } / { rowsTotal } ) Processing Show { tvShowName } on Season { tvShowSeasonNo } - Episode { tvShowEpisodeNo } " )
# Get the season from the Trakt API
2021-04-09 09:13:06 -05:00
season = traktShowObj . seasons [ parseSeasonNo (
tvShowSeasonNo , traktShowObj ) ]
2021-04-09 16:05:46 -05:00
# Get the episode from the season
2021-04-09 09:13:06 -05:00
episode = season . episodes [ int ( tvShowEpisodeNo ) - 1 ]
2021-04-09 16:05:46 -05:00
# Mark the episode as watched!
2021-04-09 09:13:06 -05:00
episode . mark_as_seen ( tvShowDateWatchedConverted )
2021-04-09 16:05:46 -05:00
# Add the episode to the local database as imported, so it can be skipped,
# if the process is repeated
2021-04-09 09:13:06 -05:00
syncedEpisodesTable . insert (
{ ' episodeId ' : tvShowEpisodeId } )
2021-04-09 16:05:46 -05:00
# Clear the error streak on completing the method without errors
2021-04-09 09:13:06 -05:00
errorStreak = 0
break
2021-04-09 16:05:46 -05:00
# Catch errors which occur because of an incorrect array index. This occurs when
# an incorrect Trakt show has been selected, with season/episodes which don't match TV Time.
# It can also occur due to a bug in Trakt Py, whereby some seasons contain an empty array of episodes.
2021-04-09 09:13:06 -05:00
except IndexError :
2021-04-09 16:05:46 -05:00
print (
f " ( { rowsCount } / { rowsTotal } ) WARNING: { tvShowName } Season { tvShowSeasonNo } , Episode { tvShowEpisodeNo } does not exist (season/episode index) in Trakt! " )
2021-04-09 09:13:06 -05:00
break
2021-04-09 16:05:46 -05:00
# Catch any errors which are raised because a show could not be found in Trakt
2021-04-09 09:13:06 -05:00
except trakt . errors . NotFoundException :
2021-04-09 16:05:46 -05:00
print (
f " ( { rowsCount } / { rowsTotal } ) WARNING: { tvShowName } Season { tvShowSeasonNo } , Episode { tvShowEpisodeNo } does not exist (search) in Trakt! " )
2021-04-09 09:13:06 -05:00
break
2021-04-09 16:05:46 -05:00
# Catch errors because of the program breaching the Trakt API rate limit
2021-04-09 09:13:06 -05:00
except trakt . errors . RateLimitException :
print (
2021-04-09 16:05:46 -05:00
" WARNING: The program is running too quickly and has hit Trakt ' s API rate limit! Please increase the delay between " +
" episdoes via the variable ' DELAY_BETWEEN_EPISODES_IN_SECONDS ' . The program will now wait 60 seconds before " +
" trying again. " )
2021-04-09 09:13:06 -05:00
time . sleep ( 60 )
2021-04-09 16:05:46 -05:00
# Mark the exception in the error streak
2021-04-09 09:13:06 -05:00
errorStreak + = 1
2021-04-09 16:05:46 -05:00
# Catch a JSON decode error - this can be raised when the API server is down and produces a HTML page, instead of JSON
2021-04-09 09:13:06 -05:00
except json . decoder . JSONDecodeError :
print (
2021-04-09 16:05:46 -05:00
f " ( { rowsCount } / { rowsTotal } ) WARNING: A JSON decode error occuring whilst processing { tvShowName } " +
f " Season { tvShowSeasonNo } , Episode { tvShowEpisodeNo } ! This might occur when the server is down and has produced " +
" a HTML document instead of JSON. The script will wait 60 seconds before trying again. " )
# Wait 60 seconds
2021-04-09 09:13:06 -05:00
time . sleep ( 60 )
2021-04-09 16:05:46 -05:00
# Mark the exception in the error streak
2021-04-09 09:13:06 -05:00
errorStreak + = 1
2021-04-09 16:05:46 -05:00
# Catch a CTRL + C keyboard input, and exits the program
2021-04-09 09:13:06 -05:00
except KeyboardInterrupt :
sys . exit ( " Cancel requested... " )
2021-04-09 16:05:46 -05:00
# Skip the episode
2021-04-09 09:13:06 -05:00
else :
2021-04-09 16:05:46 -05:00
print (
f " ( { rowsCount } / { rowsTotal } ) Skipping ' { tvShowName } ' Season { tvShowSeasonNo } Episode { tvShowEpisodeNo } . It ' s already been imported. " )
2021-04-09 09:13:06 -05:00
2021-04-09 18:39:10 -05:00
# def processMaintainRecords():
# # Total amount of rows which have been processed in the CSV file
# rowsCount = 0
# # Total amount of rows in the CSV file
# rowsTotal = 0
# with open(getFollowedShowsPath()) as f:
# rowsTotal = sum(1 for line in f)
# with open(getFollowedShowsPath(), newline='') as csvfile:
# # Create the CSV reader, which will break up the fields using the delimiter ','
# showsReader = csv.reader(csvfile, delimiter=',')
# # Loop through each line/record of the CSV file
# for row in showsReader:
# try:
# # Increment the row count
# rowsCount += 1
# # Get the TV Show name
# tvShowName = row[10]
# # Search Trakt for the TV show matching TV Time's title value
# traktShowObj = getShowByName(tvShowName, 0, 0)
# # If no show was found, then skip the row
# if traktShowObj == None:
# continue
# # Show progress on screen
# print(
# f"({rowsCount}/{rowsTotal}) Removing '{tvShowName}' from Collection & Library in Trakt")
# # Hide the show
# traktShowObj.remove_from_collection()
# time.sleep(1)
# traktShowObj.remove_from_library()
# time.sleep(1)
# # Catch a cancel request CTRL+C
# except KeyboardInterrupt:
# sys.exit("Cancel requested...")
# # Catch any errors which are raised because a show could not be found in Trakt
# except trakt.errors.NotFoundException:
# print(
# f"({rowsCount}/{rowsTotal}) WARNING: {tvShowName} does not exist (search) in Trakt!")
# break
2021-04-09 09:13:06 -05:00
def start ( ) :
2021-04-09 16:05:46 -05:00
# Create the initial authentication with Trakt, before starting the process
2021-04-09 09:13:06 -05:00
if initTraktAuth ( ) :
2021-04-09 18:39:10 -05:00
# Display a menu selection
print ( f " >> What do you want to do? " )
print ( f " 1) Import Watch History from TV Time " )
while True :
try :
menuSelection = int ( input ( f " Enter your menu selection: " ) )
break
except ValueError :
print ( " Invalid input. Please enter a numerical number. " )
# Start the process which is required
if menuSelection == 1 :
# Invoke the method which will import episodes which have been watched
# from TV Time into Trakt
processWatchedShows ( )
else :
print ( " Sorry - that ' s an unknown menu selection " )
2021-04-09 09:13:06 -05:00
else :
2021-04-09 16:05:46 -05:00
print ( " ERROR: Unable to complete authentication to Trakt - please try again. " )
2021-04-09 09:13:06 -05:00
if __name__ == " __main__ " :
2021-04-09 16:05:46 -05:00
# Check that the user has created the config file
if os . path . exists ( " config.json " ) :
# Check that the user has provided the GDPR path
if os . path . isdir ( config . GDPR_WORKSPACE_PATH ) :
start ( )
else :
print ( " Oops! The TV Time GDPR folder ' " + config . GDPR_WORKSPACE_PATH +
" ' does not exist on the local system. Please check it, and try again. " )
2021-04-09 09:13:06 -05:00
else :
2021-04-09 16:05:46 -05:00
print ( f " ERROR: The ' config.json ' file cannot be found - have you created it yet? " )