# Get entries in Anilist, not on your Tachiyomi library # imports import os import json from google import protobuf # Local import import func.main as fMain from func import tachiBackup_pb2 as tachiBackupProto fMain.logString("Imported func.getNotOnTachi", "") # Functions def logString(text): fMain.logString(text, "getNotOnTachi") def sort_byval(json): try: return str(json['format']) except KeyError: return "" # Open json tachiyomi backup file, and load all Anilist-tracked entries def parseLegacyBackup(inputMangaPath: str) -> list: listReturn = [] loadTachi = None logString("Loading legacy backup '" + os.path.basename(inputMangaPath) + "' into memory..") with open(inputMangaPath, "r+", encoding='utf-8') as F:
def logString(text): fMain.logString(text, "getNotOnTachi")
def trim_results(filepath, inputAnime, inputManga, isNsfw): # Declare filepaths if isNsfw: outputStats = os.path.join(filepath, "output", f'nsfw_animemanga_stats.txt') else: outputStats = os.path.join(filepath, "output", f'animemanga_stats.txt') outputAnime = f'{inputAnime[:-5]}_NotInMAL.json' outputManga = f'{inputManga[:-5]}_NotInMAL.json' # STATS variables statScoreTotal = 0 statScoreCount = 0 statInMAL = 0 # Count entries cTotal = 0 cCurrent = 0 cComplete = 0 cHold = 0 cDrop = 0 cPlan = 0 # Delete prev files fMain.deleteFile(outputStats) # Load JSON objects # Check if anime file Exists! if not (os.path.exists(inputAnime)): fMain.logString("Anime json file does not exists!", logSrc) jsonAnime = None else: fMain.logString("Loading " + os.path.basename(inputAnime) + " into memory..", logSrc) with open(inputAnime, "r+", encoding='utf-8') as F: jsonAnime = json.load(F) jsonAnime.sort(key=sort_byval, reverse=True) fMain.logString("Anime json file loaded!", logSrc) # Check if manga file Exists! if not (os.path.exists(inputManga)): fMain.logString("Manga json file does not exists!", logSrc) jsonManga = None else: fMain.logString("Loading " + os.path.basename(inputManga) + " into memory..", logSrc) with open(inputManga, "r+", encoding='utf-8') as F: jsonManga = json.load(F) jsonManga.sort(key=sort_byval, reverse=True) fMain.logString("Manga json file loaded!", logSrc) # json Objects jsonOutputAnime = [] jsonOutputManga = [] # Get entries from Anime, not in MAL if jsonAnime is not None: fMain.logString("Checking anime list..", logSrc) for entry in jsonAnime: # Get each entry if (entry["idMal"] < 1): # If not in MAL, ID = 0 statInMAL += 1 jsonData = {} jsonData["idAnilist"] = entry["idAnilist"] jsonData["titleEnglish"] = entry["titleEnglish"] jsonData["titleRomaji"] = entry["titleRomaji"] if str(entry["synonyms"]) == "[]": jsonData["synonyms"] = "" else: jsonData["synonyms"] = entry["synonyms"] jsonData["format"] = entry["format"] jsonData["source"] = entry["source"] jsonData["status"] = entry["status"] jsonData["startedAt"] = entry["startedAt"] jsonData["completedAt"] = entry["completedAt"] jsonData["progress"] = entry["progress"] jsonData["totalEpisodes"] = entry["totalEpisodes"] jsonData["score"] = entry["score"] jsonData["notes"] = entry["notes"] # Append to JSON object jsonOutputAnime.append(jsonData) # Stats checker statScore = int(entry["score"]) if (statScore > 0): statScoreTotal = statScoreTotal + statScore statScoreCount = statScoreCount + 1 # Count entries AnilistStatus = str(entry["status"]) if (AnilistStatus == "COMPLETED"): cComplete = cComplete + 1 elif (AnilistStatus == "PAUSED"): cHold = cHold + 1 elif (AnilistStatus == "CURRENT"): cCurrent = cCurrent + 1 elif (AnilistStatus == "DROPPED"): cDrop = cDrop + 1 elif (AnilistStatus == "PLANNING"): cPlan = cPlan + 1 elif (AnilistStatus == "REPEATING"): cCurrent = cCurrent + 1 # Write 'outputAnime' if jsonOutputAnime: fMain.createJsonFile(outputAnime, jsonOutputAnime, logSrc) # Write stats for Anime cTotal = cComplete + cCurrent + cHold + cPlan + cDrop fMain.logString("Appending to file (Average Score stats): " + os.path.basename(outputStats), logSrc) averageScore = "{:.2f}".format(statScoreTotal/statScoreCount * 10) fMain.write_append(outputStats, "Anime stats:\nAverage Score (out of 100): " + averageScore + "\n") fMain.write_append(outputStats, "Count:\nCompleted: " + str(cComplete) + "\nCurrently Watching: " + str(cCurrent) + "\nPaused: " + str(cHold) + "\nPlanning: " + str(cPlan) + "\nDropped: " + str(cDrop) + "\n") fMain.write_append(outputStats, "\nTotal: " + str(cTotal)) fMain.write_append(outputStats, "\nAnime Not in MAL: " + str(statInMAL) + "\n") # Add Line Break fMain.write_append(outputStats, "=========================================\n") # Reset vars statScoreTotal = 0 statScoreCount = 0 statInMAL = 0 # Reset count cTotal = 0 cCurrent = 0 cComplete = 0 cHold = 0 cDrop = 0 cPlan = 0 # For MANGA # Get entries from MANGA, not in MAL if jsonManga is not None: fMain.logString("Checking manga list..", logSrc) for entry in jsonManga: # Get each entry if (entry["idMal"] < 1): # If not in MAL, ID = 0 statInMAL += 1 jsonData = {} jsonData["idAnilist"] = entry["idAnilist"] jsonData["titleEnglish"] = entry["titleEnglish"] jsonData["titleRomaji"] = entry["titleRomaji"] if str(entry["synonyms"]) == "[]": jsonData["synonyms"] = "" else: jsonData["synonyms"] = entry["synonyms"] jsonData["format"] = entry["format"] jsonData["source"] = entry["source"] jsonData["status"] = entry["status"] jsonData["startedAt"] = entry["startedAt"] jsonData["completedAt"] = entry["completedAt"] jsonData["progress"] = entry["progress"] jsonData["progressVolumes"] = entry["progressVolumes"] jsonData["totalChapters"] = entry["totalChapters"] jsonData["totalVol"] = entry["totalVol"] jsonData["score"] = entry["score"] jsonData["notes"] = entry["notes"] # Append to JSON object jsonOutputManga.append(jsonData) # Stats checker statScore = int(entry["score"]) if (statScore > 0): statScoreTotal = statScoreTotal + statScore statScoreCount = statScoreCount + 1 # Count entries AnilistStatus = str(entry["status"]) if (AnilistStatus == "COMPLETED"): cComplete = cComplete + 1 elif (AnilistStatus == "PAUSED"): cHold = cHold + 1 elif (AnilistStatus == "CURRENT"): cCurrent = cCurrent + 1 elif (AnilistStatus == "DROPPED"): cDrop = cDrop + 1 elif (AnilistStatus == "PLANNING"): cPlan = cPlan + 1 elif (AnilistStatus == "REPEATING"): cCurrent = cCurrent + 1 # Write 'outputManga' if jsonOutputManga: fMain.createJsonFile(outputManga, jsonOutputManga, logSrc) # Write stats for Manga cTotal = cComplete + cCurrent + cHold + cPlan + cDrop fMain.logString("Appending to file (Average Score stats): " + os.path.basename(outputStats), logSrc) averageScore = "{:.2f}".format(statScoreTotal/statScoreCount * 10) fMain.write_append(outputStats, "Manga stats:\nAverage Score (out of 100): " + averageScore + "\n") fMain.write_append(outputStats, "Count:\nCompleted: " + str(cComplete) + "\nCurrently Reading: " + str(cCurrent) + "\nPaused: " + str(cHold) + "\nPlanning: " + str(cPlan) + "\nDropped: " + str(cDrop) + "\n") fMain.write_append(outputStats, "\nTotal: " + str(cTotal)) fMain.write_append(outputStats, "\nManga Not in MAL: " + str(statInMAL) + "\n")
# Remove Entries that have MAL ID # And additional code to get stats # imports import os import json # Local imports import func.main as fMain # Other vars logSrc = "trim_list" fMain.logString("Imported func.trim_list", "") # Functions def sort_byval(json): try: return str(json['format']) except KeyError: return "" def trim_results(filepath, inputAnime, inputManga, isNsfw): # Declare filepaths if isNsfw: outputStats = os.path.join(filepath, "output", f'nsfw_animemanga_stats.txt') else: outputStats = os.path.join(filepath, "output", f'animemanga_stats.txt') outputAnime = f'{inputAnime[:-5]}_NotInMAL.json' outputManga = f'{inputManga[:-5]}_NotInMAL.json' # STATS variables statScoreTotal = 0 statScoreCount = 0
def main(): # Declare variables fMain.logString("Define Global Vars..", mainsrc) # Paths for Files PROJECT_PATH = os.path.dirname(os.path.realpath(__file__)) #os.path.dirname(sys.executable) fMain.logString("Current path: " + PROJECT_PATH, mainsrc) anilistConfig = os.path.join(PROJECT_PATH, "anilistConfig.json") entryLog = os.path.join(PROJECT_PATH, "output", "entries.log") # Log entries # Vars for Authentication ANICLIENT = "" ANISECRET = "" useOAuth = False # User vars username = "" userID = 0 isSepNsfw = False # Separate nsfw entries on output # Output files dictionary outputAnime = [] outputManga = [] # Create 'output' directory if not os.path.exists('output'): os.makedirs('output') # Toggle when skipping Public mode, or Authenticated mode inputChoice = fMain.inputX("Use Authenticated mode? [y/n] (Default: 'Public List mode'): ", "n") if inputChoice.lower()[0] == "y": # Import config for Anilist OAuth fMain.logString("Importing Anilist config", mainsrc) useOAuth, ANICLIENT, ANISECRET, REDIRECT_URL = fReq.setup_config(anilistConfig) if not useOAuth: accessToken = "" if useOAuth: code = fReq.request_pubcode(ANICLIENT, REDIRECT_URL) accessToken = fReq.request_accesstkn(ANICLIENT, ANISECRET, REDIRECT_URL, code) if accessToken: useOAuth = True fMain.logString("Has access token!", mainsrc) else: useOAuth = False fMain.logString("Cannot Authenticate! Will use Public Username.", mainsrc) else: useOAuth = False # Check whether authenticated, or use public Username if not useOAuth: fMain.logString("'Public Username' Mode", mainsrc) accessToken = "" while (userID < 1): # Get Anilist Username anilistUser = fMain.inputX("Enter your Anilist Username: "******"") userID = fReq.anilist_getUserID(anilistUser) else: fMain.logString("Getting User ID, from Authenticated user..", mainsrc) userID = fReq.anilist_getUserID_auth(accessToken) if userID is not None: fMain.logString("User ID: " + str(userID), mainsrc) else: fMain.logString("User Id cannot be fetched!", mainsrc) # Confirm if separating nsfw entries on generating output files inputChoice = fMain.inputX("Separate NSFW entries? [y/n] (Default: n): ", "n") if inputChoice.lower()[0] == "y": isSepNsfw = True # Delete prev files fMain.deleteFile(entryLog) # Initiate parameter values paramvals = { 'root': PROJECT_PATH, 'log': entryLog, 'access_tkn': accessToken, 'user_id': userID, 'username': username, 'use_auth': useOAuth, 'sep_nsfw': isSepNsfw } # Request anime list outputAnime = getMediaEntries("ANIME", paramvals) # Request manga list outputManga = getMediaEntries("MANGA", paramvals) # Trim List tempTrim = fMain.inputX("Trim list (Create list of Entries not on MAL)? [y/n] (Default: n): ", "n") if tempTrim.lower()[0] == "y": fTrim.trim_results(PROJECT_PATH, outputAnime.get('main'), outputManga.get('main'), False) if isSepNsfw: fTrim.trim_results(PROJECT_PATH, outputAnime.get('nsfw'), outputManga.get('nsfw'), True) # Get Entries not on Tachi tempTachi = fMain.inputX("Tachiyomi library json file (legacy backup): ", None) if tempTachi: fNotOnTachi.getNotOnTachi(outputManga.get('main'), tempTachi, False) if isSepNsfw: fNotOnTachi.getNotOnTachi(outputManga.get('nsfw'), tempTachi, True) fMain.inputX("Press <Enter> to exit..", "")
def getMediaEntries(mediaType, paramvals): # Vars and Objects filepath = str(paramvals['root']) entryLog = str(paramvals['log']) accessToken = str(paramvals['access_tkn']) userID = int(paramvals['user_id']) username = str(paramvals['username']) useOAuth = bool(paramvals['use_auth']) isSepNsfw = bool(paramvals['sep_nsfw']) returnMedia = {} entryID = [] # List of IDs, to prevent duplicates jsonToDump = [] # List of Json dict object of results jsonToDumpNsfw = [] # List of Json dict object of results, 18+ entries isAdult = False # Bool for 'isAdult' flag from Anilist nsfwToggle = 0 # 0=main, 1=nsfw. For arrays toggle source = "anilist_get" + mediaType fMain.logString("All vars are initiated", source) # Declare filepaths if mediaType == "ANIME": outputMedia = os.path.join( filepath, "output", "anime_" + datetime.now().strftime("%Y-%m-%d") + ".json") xmlMedia = os.path.join( filepath, "output", "anime_" + datetime.now().strftime("%Y-%m-%d") + ".xml") outputMedia18 = os.path.join( filepath, "output", "nsfw_anime_" + datetime.now().strftime("%Y-%m-%d") + ".json") xmlMedia18 = os.path.join( filepath, "output", "nsfw_anime_" + datetime.now().strftime("%Y-%m-%d") + ".xml") else: outputMedia = os.path.join( filepath, "output", "manga_" + datetime.now().strftime("%Y-%m-%d") + ".json") xmlMedia = os.path.join( filepath, "output", "manga_" + datetime.now().strftime("%Y-%m-%d") + ".xml") outputMedia18 = os.path.join( filepath, "output", "nsfw_manga_" + datetime.now().strftime("%Y-%m-%d") + ".json") xmlMedia18 = os.path.join( filepath, "output", "nsfw_manga_" + datetime.now().strftime("%Y-%m-%d") + ".xml") # Check if not existing if not (os.path.exists(outputMedia)): # Get JSON object if useOAuth: jsonMedia = fReq.anilist_userlist(accessToken, userID, mediaType) else: jsonMedia = fReq.anilist_userlist_public(userID, mediaType) # Check if not null if jsonMedia is not None: listMedia = jsonMedia["data"]["MediaListCollection"]["lists"] # Create vars # Count Manga entries cTotal = arr.array('i', [0, 0]) cWatch = arr.array('i', [0, 0]) cComplete = arr.array('i', [0, 0]) cHold = arr.array('i', [0, 0]) cDrop = arr.array('i', [0, 0]) cPtw = arr.array('i', [0, 0]) # Start generating JSON and XML.. fMain.logString("Generating JSON and XML..", source) # Log duplicate entries fMain.logFile(entryLog, f'{mediaType} Entries') entryID.clear() # Clear list # Iterate over the MediaCollection List for anime in listMedia: # Get entries animeInfo = anime["entries"] # Iterate over the anime information, inside the entries for entry in animeInfo: # Get Anilist ID anilistID = entry["media"]["id"] # Get Anilist Status AnilistStatus = fMain.validateStr(entry["status"]) # Get isAdult flag isAdult = bool(entry["media"]["isAdult"]) # Check if already exists if anilistID in entryID: fMain.logFile( entryLog, f'Skipped: {str(anilistID)}, Duplicate {mediaType} entry.' ) continue else: entryID.append(anilistID) # Write to json file if isAdult and isSepNsfw: jsonToDumpNsfw.append( fMain.entry_json(entry, mediaType)) else: jsonToDump.append(fMain.entry_json(entry, mediaType)) # Write to MAL Xml File malID = fMain.validateInt(entry["media"]["idMal"]) if malID != '0': # Get XML strings xmltoWrite = fMain.entry_xmlstr( mediaType, malID, entry, str(AnilistStatus)) # Write to xml file if isAdult and isSepNsfw: nsfwToggle = 1 fMain.write_append(xmlMedia18, xmltoWrite) else: nsfwToggle = 0 fMain.write_append(xmlMedia, xmltoWrite) # Add count cTotal[nsfwToggle] += 1 if (AnilistStatus == "COMPLETED"): cComplete[nsfwToggle] += 1 elif (AnilistStatus == "PAUSED"): cHold[nsfwToggle] += 1 elif (AnilistStatus == "CURRENT"): cWatch[nsfwToggle] += 1 elif (AnilistStatus == "DROPPED"): cDrop[nsfwToggle] += 1 elif (AnilistStatus == "PLANNING"): cPtw[nsfwToggle] += 1 elif (AnilistStatus == "REPEATING"): cWatch[nsfwToggle] += 1 # Dump JSON to file.. if (fMain.dumpToJson(jsonToDump, outputMedia)): fMain.logString("Succesfully created json file!", source) else: fMain.logString("Error with creating json file!", source) fMain.logString(f"Done with {mediaType} JSON file..", source) # Dump JSON (nsfw) to file.. if isSepNsfw: if (fMain.dumpToJson(jsonToDumpNsfw, outputMedia18)): fMain.logString("Succesfully created json file!", source) else: fMain.logString("Error with creating json file!", source) fMain.logString(f"Done with {mediaType} JSON file..", source) # Write to MAL xml file fMain.logString(f"Finalizing {mediaType} XML file..", source) malprepend = "" malprepend18 = "" mediastring = "" mediaexport = '0' mediawatchread = "" if mediaType == "ANIME": mediastring = "anime" mediaexport = '2' mediawatchread = "watch" else: mediastring = "manga" mediaexport = '1' mediawatchread = "read" fMain.write_append(xmlMedia, f'</my{mediastring}list>') if isSepNsfw: fMain.write_append(xmlMedia18, f'</my{mediastring}list>') # Total counts fMain.logString(f"Prepend 'myinfo' to {mediaType} XML file..", source) malprepend = f'<?xml version="1.0" encoding="UTF-8" ?>\n<my{mediastring}list>\n' malprepend += '\t<myinfo>\n' malprepend += '\t\t' + fMain.toMalval('', 'user_id') + '\n' malprepend += '\t\t' + fMain.toMalval(username, 'user_name') + '\n' malprepend += '\t\t' + fMain.toMalval(mediaexport, 'user_export_type') + '\n' malprepend18 = malprepend # Same prepend values as 'main' # Count for 'main' malprepend += '\t\t' + fMain.toMalval(str( cTotal[0]), f'user_total_{mediastring}') + '\n' malprepend += '\t\t' + fMain.toMalval( str(cWatch[0]), f'user_total_{mediawatchread}ing') + '\n' malprepend += '\t\t' + fMain.toMalval(str( cComplete[0]), 'user_total_completed') + '\n' malprepend += '\t\t' + fMain.toMalval(str(cHold[0]), 'user_total_onhold') + '\n' malprepend += '\t\t' + fMain.toMalval(str(cDrop[0]), 'user_total_dropped') + '\n' malprepend += '\t\t' + fMain.toMalval( str(cPtw[0]), f'user_total_planto{mediawatchread}') + '\n' # Count for 'nsfw' if isSepNsfw: malprepend18 += '\t\t' + fMain.toMalval( str(cTotal[1]), f'user_total_{mediastring}') + '\n' malprepend18 += '\t\t' + fMain.toMalval( str(cWatch[1]), f'user_total_{mediawatchread}ing') + '\n' malprepend18 += '\t\t' + fMain.toMalval( str(cComplete[1]), 'user_total_completed') + '\n' malprepend18 += '\t\t' + fMain.toMalval( str(cHold[1]), 'user_total_onhold') + '\n' malprepend18 += '\t\t' + fMain.toMalval( str(cDrop[1]), 'user_total_dropped') + '\n' malprepend18 += '\t\t' + fMain.toMalval( str(cPtw[1]), f'user_total_planto{mediawatchread}') + '\n' malprepend += '\t</myinfo>\n' malprepend18 += '\t</myinfo>\n' fMain.line_prepender(xmlMedia, malprepend) if isSepNsfw: fMain.line_prepender(xmlMedia18, malprepend18) fMain.logString(f"Done with {mediaType} XML file..", source) # Done anime/manga fMain.logString("Done! File generated: " + outputMedia, source) fMain.logString("Done! File generated: " + xmlMedia, source) if isSepNsfw: fMain.logString("Done! File generated: " + outputMedia18, source) fMain.logString("Done! File generated: " + xmlMedia18, source) # Already existing! else: fMain.logString(f"{mediaType} file already exist!: " + outputMedia, source) returnMedia = {'main': outputMedia, 'nsfw': outputMedia18} return returnMedia
# Imports import os from datetime import datetime import array as arr # Local Imports import func.main as fMain import func.anilist_request as fReq fMain.logString("Imported func.anilist_getMedia", "") # Main Function def getMediaEntries(mediaType, paramvals): # Vars and Objects filepath = str(paramvals['root']) entryLog = str(paramvals['log']) accessToken = str(paramvals['access_tkn']) userID = int(paramvals['user_id']) username = str(paramvals['username']) useOAuth = bool(paramvals['use_auth']) isSepNsfw = bool(paramvals['sep_nsfw']) returnMedia = {} entryID = [] # List of IDs, to prevent duplicates jsonToDump = [] # List of Json dict object of results jsonToDumpNsfw = [] # List of Json dict object of results, 18+ entries isAdult = False # Bool for 'isAdult' flag from Anilist nsfwToggle = 0 # 0=main, 1=nsfw. For arrays toggle source = "anilist_get" + mediaType fMain.logString("All vars are initiated", source)