def _findLineZero(self): lineNb = 0 # Flag to stop the search for 0:00 isLineAtTimeZero = False logPrint.printLog("Parsing the description field") for line in self.descriptionLinesList: if (not isLineAtTimeZero): # Not find yet: perform the search idxReturnedFromFind = line.find("0:00") if idxReturnedFromFind != -1: # "0:00" found. We store the line number and the character index if idxReturnedFromFind > 0: # Consolidate: check again if idxReturnedFromFind>0, i.e. if it is really 00:00 and not for example 10:00 isLineAtTimeZero = self._checkIfDetectedZeroIsReallyZero( line, idxReturnedFromFind) else: # 0:00 at index 0 is for sure a time zero isLineAtTimeZero = True if isLineAtTimeZero: # self.idxCharZeroZero = idxReturnedFromFind self.lineNbZeroZero = lineNb lineNb = lineNb+1 if not isLineAtTimeZero: logPrint.printError("0:00 not found.") return isLineAtTimeZero
def main(outputFilePath, argv): prefix = "" youtubeApiInst = youtubeApiWrapper(sys.argv[1]) descrFromYoutubeApi = youtubeApiInst.getDescriptionField() # descriptionParser accepts only list of str descrListFromYoutubeApi = descrFromYoutubeApi.splitlines() # First attempt of chapter parsing using what Youtube API returned descriptionParserInstFromYoutube = descriptionParser( descrListFromYoutubeApi) succesfullParsing = descriptionParserInstFromYoutube.parse() if (not succesfullParsing): logPrint.printInfo( "Another attempt can be given using pasted chapter list.") # Another parsing is attempted using stdin logPrint.printInfo( "Copy/paste the description list you want, then hit Ctrl+Z") # returns a list of str descrListFromCopyPaste = sys.stdin.readlines() # strip this line from "\n" strippedDescrListFromCopyPaste = [ "" for idx in range(len(descrListFromCopyPaste)) ] for idx in range(len(descrListFromCopyPaste)): strippedDescrListFromCopyPaste[idx] = descrListFromCopyPaste[ idx].strip(" \n") # Create a new descriptionParser with the pasted list stripped from the final \n descriptionParserInstFromCopyPaste = descriptionParser( strippedDescrListFromCopyPaste) succesfullParsing = descriptionParserInstFromCopyPaste.parse() if (not succesfullParsing): logPrint.printError("Second attempt failed. Exiting.") exit(-5) else: chaptersMatrix = descriptionParserInstFromCopyPaste.getChaptersMatrix( ) else: chaptersMatrix = descriptionParserInstFromYoutube.getChaptersMatrix() if len(sys.argv) == 3: prefix = sys.argv[2] logPrint.printLog( "The prefix \"" + prefix + "\" read from the program's arguments, will be added before each title." ) else: prefix = "" logPrint.printLog("No prefix read from the program's arguments.") # Instantiate musicDisplayer to launch a timer and print the correct music name musicDisplayerInst = musicDisplayer(outputFilePath, chaptersMatrix, prefix) # Launch the timer musicDisplayerInst.start()
def __init__(self): logPrint.printLog("Parsing the config file config.ini") self.config = configparser.ConfigParser() self.config.read('config.ini') self.outputFilePath = self.config['timeSnip']['outputFilePath'] readVerbosityLevel = self.config['timeSnip']['verbosityLevel'] # outputFilePath if self.outputFilePath == "": print("[ERR] In internalConfig.py, outputFilePath is not defined") exit(-7) outputFile = Path(self.outputFilePath) outputFileParent = outputFile.parent if not outputFileParent.is_dir(): print( "[ERR] In internalConfig.py, outputFilePath is bad defined: its parent directory does not exist" ) exit(-8) # verbosityLevel if readVerbosityLevel == "": print( "[ERR] In internalConfig.py, readVerbosityLevel is not defined" ) exit(-7) if readVerbosityLevel == "debug": self.verbosityLevel = 0 elif readVerbosityLevel == "log": self.verbosityLevel = 1 elif readVerbosityLevel == "error": self.verbosityLevel = 2 else: print( "[ERR] In internalConfig.py, verbosityLevel is not/bad defined" ) exit(-9)
def getDescriptionField(self): logPrint.printLog( "Calling Youtube API to get the video description list") # Disable OAuthlib's HTTPS verification when running locally. # *DO NOT* leave this option enabled in production. os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1" api_service_name = "youtube" api_version = "v3" # This file is secret client_secrets_file = os.getcwd( ) + "\\_credentials\\google-api-key.json" # Get credentials and create an API client flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file( client_secrets_file, self.scopes) # Calls OAUTH2 services on a Browser credentials = flow.run_local_server( success_message="Twitch: @LukkoLuigi") youtube = googleapiclient.discovery.build(api_service_name, api_version, credentials=credentials) request = youtube.videos().list(part="snippet", id=self.videoId) response = request.execute() #description = response.items[0].snippet.description items = response["items"] snippet = items[0]["snippet"] description = snippet["description"] # logPrint.printDebug("description: ") # logPrint.printDebug(description) return description
def __init__(self, youtubeUrl): logPrint.printLog("Initializing youtubeApiWrapper with " + youtubeUrl) logPrint.printDebug("youtubeUrl: " + youtubeUrl) self.videoId = self.extractIdFromUrl(youtubeUrl)
def _parseChapterList(self): idxSplitted = 0 idxOfZeroInSplittedLine = -1 line = self.descriptionLinesList[self.lineNbZeroZero] splittedLineList = re.split(r'([0-9:]+:[0-9:]+)', line) for splittedWord in splittedLineList: if re.search(r'[0-9:]+:[0-9:]+', splittedWord): idxOfZeroInSplittedLine = idxSplitted idxSplitted = idxSplitted+1 # Analyze pattern self.patternId = self._analyzeChaptersPattern( splittedLineList, idxOfZeroInSplittedLine) logPrint.printLog("Pattern detected: "+str(self.patternId)) idxMatrix = 0 # To avoid false end-of-chapters due to problems of formatting in the description, another attempt is given to each non-match numberOfRemainingAttemptsToParseEachLine = self.MAX_ATTEMPTS_NB_TO_PARSE_TIME_AND_TITLE logPrint.printLog("While parsing the chapters, a maximum of "+str( self.MAX_ATTEMPTS_NB_TO_PARSE_TIME_AND_TITLE)+" not matching line(s) is accepted.") offsetForIdxMatrixBecauseOfFalseErrors = 0 # Max length of the matrix while (idxMatrix < self.MAX_SIZE_OF_MATRIX): # Avoid overflow if((self.lineNbZeroZero + idxMatrix + offsetForIdxMatrixBecauseOfFalseErrors < len(self.descriptionLinesList)) and numberOfRemainingAttemptsToParseEachLine > 0): line = self.descriptionLinesList[self.lineNbZeroZero + idxMatrix + offsetForIdxMatrixBecauseOfFalseErrors] if re.search('[0-9:]+:[0-9:]+', line): # Reset for the next formatting error found numberOfRemainingAttemptsToParseEachLine = self.MAX_ATTEMPTS_NB_TO_PARSE_TIME_AND_TITLE # FIXME Does not work for "[2:20] the past inside the present" splittedLine = re.split(r'([0-9:]+:[0-9:]+)', line) # Too specific, commented and will be deleted after testing # offsetForSplittedLineReading = 0 # if splittedLine[0] == "": # # re.split created an empty cell at the beginning, ignore it # offsetForSplittedLineReading = 1 # Pattern 0 could include the case where time is not the first word, the shift to do is idxOfZeroInSplittedLine # We here take thy hypothesis that the shift of the 0:00 line is the same for every line if self.patternId == 0: time = splittedLine[0 + idxOfZeroInSplittedLine] titleAndDelim = splittedLine[1 + idxOfZeroInSplittedLine] elif self.patternId == 1: time = splittedLine[1] titleAndDelim = splittedLine[0] else: logPrint.printError( "Chapters pattern not found. Exiting.") exit(-4) logPrint.printLog("Matching line found, line "+str(self.lineNbZeroZero + idxMatrix + offsetForIdxMatrixBecauseOfFalseErrors)+", time "+time) # The title is stripped to not store useless characters in the beginning and in the end title = titleAndDelim.strip(" -_[]#:") # Strip remaining parenthesis around timestamp e.g. "(25:32) title" without cutting the included parenthesis of one title # e.g. "(25:32) title (featuring Pitbull)" title = title.lstrip(") ") title = title.rstrip("( ") self.chaptersMatrix[idxMatrix, 0] = time self.chaptersMatrix[idxMatrix, 1] = title # Shift the index only if we have found something to avoid holes because of formatting errors idxMatrix = idxMatrix+1 else: if numberOfRemainingAttemptsToParseEachLine > 0: # Second chance logPrint.printLog("Not matching line found, line "+str(self.lineNbZeroZero + idxMatrix + offsetForIdxMatrixBecauseOfFalseErrors)+", another chance is given.") numberOfRemainingAttemptsToParseEachLine = numberOfRemainingAttemptsToParseEachLine - 1 offsetForIdxMatrixBecauseOfFalseErrors = offsetForIdxMatrixBecauseOfFalseErrors + 1 else: # No more time is detected and the formatting error margin is consumed, the chapter list is over logPrint.printLog("No more matching line found, line "+str(self.lineNbZeroZero + idxMatrix + offsetForIdxMatrixBecauseOfFalseErrors)+", the chapter list parsing is over.") self.chaptersMatrixSize = idxMatrix # End the while loop idxMatrix = self.MAX_SIZE_OF_MATRIX logPrint.printDebug("chaptersMatrix, " + str(self.chaptersMatrixSize)+" lines: ") for idxMatrix in range(self.chaptersMatrixSize): logPrint.printDebug("|" + self.chaptersMatrix[idxMatrix, 0]+"|"+self.chaptersMatrix[idxMatrix, 1]+"|")