# get args # make torrent # read mediainfo # upload torrent # move torrent to watch dir # Standard library packages from subprocess import check_output import re import os import sys import argparse from urllib.parse import urlparse import json # Third-party packages from bs4 import BeautifulSoup from torf import Torrent from pathlib import Path # JPS-AU files import smpy from pymediainfo import MediaInfo def asciiart (): print(""" ███████╗███╗ ███╗ █████╗ ██╗ ██╗ ████████╗██╗ ██╗ ██╔════╝████╗ ████║ ██╔══██╗██║ ██║ ╚══██╔══╝██║ ██║ ███████╗██╔████╔██║█████╗███████║██║ ██║█████╗██║ ██║ ██║ ╚════██║██║╚██╔╝██║╚════╝██╔══██║██║ ██║╚════╝██║ ╚██╗ ██╔╝ ███████║██║ ╚═╝ ██║ ██║ ██║╚██████╔╝ ██║ ╚████╔╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ """) def getargs(): """ Get arguments using argparse """ parser = argparse.ArgumentParser() parser.add_argument('-i', '--input', help='Initiate upload on input file', nargs='?', required=True) parser.add_argument('-d', '--debug', help='Enable debug mode.', action='store_true') parser.add_argument("-dry", "--dryrun", help="Dryrun will carry out all actions other than the actual upload to SM.", action="store_true") parser.add_argument("-rt", "--releasetype", help='Set the release type. (PV, Music Performance, TV Music, TV Variety, TV-Drama)', nargs='?') parser.add_argument("-a", "--artists", help='Set the artists. (Romaji\English). Split multiple with ","', nargs='?') parser.add_argument("-oa", "--originalartist", help='Set the artist. (Original Language)', nargs='?') parser.add_argument("-ca", "--contributingartists", help='Set the contributing artists. (Romaji\English). Split multiple with ","', nargs='?') parser.add_argument("-ti", "--title", help='Set the title. (Romaji\English)', nargs='?') parser.add_argument("-oti", "--originaltitle", help='Set the title. (Original Language)', nargs='?') parser.add_argument("-t", "--tags", help="Add additional tags to the upload. At least 2 tags are required", nargs='?') parser.add_argument("-y", "--year", help='Set the torrent year (YYYYMMDD or YYYY).', nargs='?') #parser.add_argument("-f", "--freeleech", help="Enables freeleech. ()", action="store_true") parser.add_argument("-eti", "--editiontitle", help='Set the edition title', nargs='?') parser.add_argument("-ey", "--editionyear", help='Set the torrent edition year (YYYYMMDD or YYYY).', nargs='?') parser.add_argument("-ms", "--mediasource", help='Set the media source. (HDTV, Web)', nargs='?') parser.add_argument("-s", "--sub", help='Set the subtitle type. (NoSubs,Softsubs,Hardsub)', nargs='?') parser.add_argument("-l", "--language", help='Set the language (Japanese, English, Korean, Chinese, Vietnamese, Other)', nargs='?') parser.add_argument("-im", "--imageURL", help='Set the torrent cover URL.', nargs='?') parser.add_argument("-ss", "--screenshots", help='Set the torrent screenshots', nargs='?') parser.add_argument("-tdes", "--torrentdescription", help='Add a torrent description', nargs='?') parser.add_argument("-tgdes", "--torrentgroupdescription", help='Add a torrent group description. This is a required argument.', nargs='?', required=True) return parser.parse_args() def gatherdata(): """ Retrieve data about the upload. Ask for user input if necessary. :return: releasedata: dict """ releasedata = {"submit": "true"} releasedata["album_desc"] = torrentgroupdescription if torrentdescription: releasedata['release_desc'] = torrentdescription list_of_types = ["PV", "Music Performance", "TV Music", "TV Variety", "TV-Drama"] if releasetype in list_of_types: if releasetype == "PV": releasedata["type"] = 5 elif releasetype == "Music Performance": releasedata["type"] = 6 elif releasetype == "TV Music": releasedata["type"] = 7 elif releasetype == "TV Variety": releasedata["type"] = 8 elif releasetype == "TV-Drama": releasedata["type"] = 9 else: while(True): input_lang = input("\n" + "_" * 100 + "\nEnter a number to choose the upload type. \n1=PV\n2=Music Performance\n3=TV Music\n4=TV Variety\n5=TV-Drama\n") if input_lang == "1": releasedata["type"] = 5 break elif input_lang == "2": releasedata["type"] = 6 break elif input_lang == "3": releasedata["type"] = 7 break elif input_lang == "4": releasedata["type"] = 8 break elif input_lang == "5": releasedata["type"] = 9 break print("Invalid choice.") if artists: releasedata['idols[]'] = artists else: input_english_artist = input("\n" + "_" * 100 + "\nEnter the romaji/english ARTIST name. Separate multiple with \",\". \n") input_english_artist = [x.strip() for x in input_english_artist.split(',')] releasedata['idols[]'] = input_english_artist if originalartist: releasedata['artist_jp'] = originalartist else: input_artist = input("\n" + "_" * 100 + "\nEnter the original ARTIST name. Press enter to skip if this torrent has multiple artists or artist name is already english. \n") releasedata['artist_jp'] = input_artist if contributingartists: input_english_contributing_artist = contributingartists else: input_english_contributing_artist = input("\n" + "_" * 100 + "\nEnter the romaji/english CONTRIBUTING ARTIST name. Separate with \",\". Press enter to skip.\n") if input_english_contributing_artist != "": input_english_contributing_artist = [x.strip() for x in input_english_contributing_artist.split(',')] releasedata['contrib_artists[]'] = input_english_contributing_artist if title: releasedata['title'] = title else: input_english_title = input("\n" + "_" * 100 + "\nEnter the romaji/english TITLE:\n") releasedata['title'] = input_english_title if originaltitle: releasedata['title_jp'] = originaltitle else: input_title = input("\n" + "_" * 100 + "\nEnter the original TITLE. Press enter to skip.\n") releasedata['title_jp'] = input_title if year: releasedata["year"] = year else: input_year = input("\n" + "_" * 100 + "\nEnter the year as YYYYMMDD or YYYY.\n") releasedata["year"] = input_year if editiontitle: releasedata['remastertitle'] = editiontitle else: input_editiontitle = input("\n" + "_" * 100 + "\nEnter the edition TITLE. Press enter to skip.\n") if input_editiontitle != "": if editionyear: releasedata["remasteryear"] = editionyear else: input_editionyear = input("\n" + "_" * 100 + "\nEnter the edition year as YYYYMMDD or YYYY.\n") releasedata["remasteryear"] = input_editionyear releasedata['remastertitle'] = input_editiontitle if sub: releasedata["sub"] = sub else: while(True): input_sub = input("\n" + "_" * 100 + "\nEnter a number to choose subtitle type. \n1=NoSubs\n2=Softsubs\n3=Hardsubs\n") if input_sub == "1": releasedata["sub"] = "NoSubs" break elif input_sub == "2": releasedata["sub"] = "Softsubs" break elif input_sub == "3": releasedata["sub"] = "Hardsubs" break print("Invalid choice.") if language: releasedata["lang"] = language else: while(True): input_lang = input("\n" + "_" * 100 + "\nEnter a number to choose subtitle type. \n1=Japanese\n2=English\n3=Korean\n4=Chinese\n5=Vietnamese\n6=Other\n") if input_lang == "1": releasedata["lang"] = "Japanese" break elif input_lang == "2": releasedata["lang"] = "English" break elif input_lang == "3": releasedata["lang"] = "Korean" break elif input_lang == "2": releasedata["lang"] = "Chinese" break elif input_lang == "3": releasedata["lang"] = "Vietnamese" break elif input_lang == "2": releasedata["lang"] = "Other" break print("Invalid choice.") if mediasource: releasedata['media'] = mediasource else: while(True): input_lang = input("\n" + "_" * 100 + "\nEnter a number to choose the media source. \n1=HDTV\n2=Web\n") if input_lang == "1": releasedata["media"] = "HDTV" break elif input_lang == "2": releasedata["media"] = "Web" break print("Invalid choice.") if tags: releasedata["tags"] = tags else: while(True): input_tags = input("\n" + "_" * 100 + "\nEnter the tags. Separate multiple with \",\". Minimum 1 tag required.\n") if len(input_tags.split(",")) != 0: releasedata["tags"] = input_tags break else: print("Please enter at least one tag.") if screenshots: input_screenshots = screenshots else: input_screenshots = input("\n" + "_" * 100 + "\nEnter the screenshot links. Separate multiple with \",\". Press enter to skip.\n") if input_screenshots != "": input_screenshots = input_screenshots.replace(",","\n") releasedata["screenshots"] = input_screenshots return releasedata def add_mediainfo_to_releasedata(filename, releasedata): """ Retrieve mediainfo and append it to the releasedata dictionary. :return: releasedata: dict """ mediainfosall = "" media_info = MediaInfo.parse(filename) mediainfosall += str(MediaInfo.parse(filename, text=True)) replacement = str(Path(filename).parent) mediainfosall = mediainfosall.replace(replacement, '') for track in media_info.tracks: if track.track_type == 'General': # releasedataout['language'] = track.audio_language_list # Will need to check if this is reliable if 'container' not in releasedata: # Not an ISO, only set container if we do not already know its an ISO releasedata['container'] = track.file_extension.upper() else: # We have ISO - get category data based Mediainfo if we have it if track.file_extension.upper() == 'VOB': releasedata['category'] = 'DVD' elif track.file_extension.upper() == 'M2TS': # Not used yet as we cannot handle Bluray / UDF releasedata['category'] = 'Bluray' if track.track_type == 'Video': validatecodec = { "MPEG Video": "MPEG-2", "AVC": "h264", "HEVC": "h265", "MPEG-4 Visual": "DivX", # MPEG-4 Part 2 / h263 , usually xvid / divx } for old, new in validatecodec.items(): if track.format == old: releasedata['codec'] = new standardresolutions = { "3840": "1920", "1920": "1080", "1440": "1080", "1280": "720", "720": "480", } for width, height in standardresolutions.items(): if str(track.width) == width and str(track.height) == height: releasedata['ressel'] = height if 'ressel' in releasedata.keys(): # Known resolution type, try to determine if interlaced if track.scan_type == "Interlaced" or track.scan_type == "MBAFF" or (track.width == 1440 and track.height == 1080): releasedata['ressel'] += "i" else: releasedata['ressel'] += "p" # Sometimes a Progressive encode has no field set else: # Custom resolution releasedata['ressel'] = 'Other' releasedata['resolution'] = str(track.width) + "x" + str(track.height) if track.track_type == 'Audio' or track.track_type == 'Audio #1': # Handle multiple audio streams, we just get data from the first for now if track.format in ["AAC", "DTS", "PCM", "AC3"]: releasedata['audioformat'] = track.format elif track.format == "AC-3": releasedata['audioformat'] = "AC3" elif track.format == "MPEG Audio" and track.format_profile == "Layer 3": releasedata['audioformat'] = "MP3" elif track.format == "MPEG Audio" and track.format_profile == "Layer 2": releasedata['audioformat'] = "MP2" releasedata["mediainfo"] = mediainfosall return releasedata # Creates torrent file using torf module. def createtorrent(authkey, filepath, releasedata): """ Creates a torrent. :param: authkey: authkey string :param: filepath: full path of the file for torrent creation :param: releasedata: dict :return: filename of created torrent """ t = Torrent(path=filepath, trackers=[authkey]) # Torf requires we store authkeys in a list object. This makes it easier to add multiple announce urls. # Set torrent to private as standard practice for private trackers t.private = True t.source = "SugoiMusic" t.generate() ## Format releasedata to bring a suitable torrent name. # The reason we don't just use the directory name is because of an error in POSTING. # POSTS do not seem to POST hangul/jp characters alongside files. # filename = f"{releasedata['idols[]']} - {releasedata['title']} [{releasedata['media']}-{releasedata['audioformat']}].torrent" filename = f"{releasedata['title']}.torrent" filename = filename.replace("/","") try: t.write(filename) print("_" * 100) print("Torrent creation:\n") print(f"{filename} has been created.") except: print("_" * 100) print("Torrent creation:\n") os.remove(filename) print(f"{filename} already exists, existing torrent will be replaced.") t.write(filename) print(f"{filename} has been created.") return filename def getauthkey(): """ Get SM session authkey for use by uploadtorrent() data dict. Uses SM login data :return: authkey """ smpage = sm.retrieveContent("https://sugoimusic.me/torrents.php?id=118") # Arbitrary page on JPS that has authkey soup = BeautifulSoup(smpage.text, 'html5lib') rel2 = str(soup.select('#content .thin .main_column .torrent_table tbody')) authkey = re.findall('authkey=(.*)&torrent_pass=', rel2) return authkey def uploadtorrent(torrent, imageURL, releasedata): """ Uploads a torrent. :param: torrent: torrent filename. :param: imageURL: url to a cover image :param: releasedata: dict """ # POST url. uploadurl = "https://sugoimusic.me/upload.php" # Dataset containing all of the information obtained from our FLAC files. data = releasedata data['image'] = imageURL if not dryrun: data['auth'] = authkey if debug: print('_' * 100) print('Release Data:\n') for field in data: print(field) print(data) try: postDataFiles = { 'file_input': open(torrent, 'rb') #'userfile': open(cover, 'rb') } except FileNotFoundError: print("_" * 100) print('File not found!\nPlease confirm file locations and names. Cover image or .torrent file could not be found') sys.exit() # If dryrun argument has not ben passed we will POST the results to JPopSuki. if dryrun != True: SMres = sm.retrieveContent(uploadurl, "post", data, postDataFiles) SMerrorTorrent = re.findall('red; text-align: center;">(.*)
', SMres.text) # SMerrorLogon = re.findall('Invalid (.*)
', SMres.text) if len(SMerrorTorrent)!=0: print("Upload failed. Torrent error") print(SMerrorTorrent) # if len(SMerrorTorrent)!=0: # print("Upload failed. Logon error") # print(SMerrorLogon) ## TODO Filter through JPSres.text and create error handling based on responses #print(JPSres.text) def localfileorganization(torrent, watch_folder): # Move torrent directory to downloads_folder # if cfg['local_prefs']['add_to_downloads_folder']: # try: # os.mkdir(os.path.join(downloads_folder, os.path.basename(directory))) # except FileExistsError: # pass # copytree(directory, os.path.join(downloads_folder, os.path.basename(directory))) # shutil.rmtree(directory) if cfg['local_prefs']['add_to_watch_folder']: os.rename(torrent, f"{watch_folder}/{torrent}") if __name__ == "__main__": asciiart() args = getargs() # TODO consider calling args[] directly, we will then not need this line dryrun = debug = imageURL = tags = inputfile = artists = contributingartists = title = None originalartist = originaltitle = torrentdescription = torrentgroupdescription = editiontitle = editionyear = sub = language = year = mediasource = releasetype = screenshots = None inputfile = args.input torrentgroupdescription = args.torrentgroupdescription torrentdescription = args.torrentdescription if args.dryrun: dryrun = True if args.debug: debug = True # if args.freeleech: # freeleech = True if args.imageURL: imageURL = args.imageURL if args.releasetype: releasetype = args.releasetype if args.title: title = args.title if args.artists: artists = args.artists if args.contributingartists: contributingartists = args.contributingartists if args.originalartist: originalartist = args.originalartist if args.originaltitle: originaltitle = args.originaltitle if args.editiontitle: editiontitle = args.editiontitle if args.language: language = args.language if args.year: year = args.year if args.editionyear: editionyear = args.editionyear if args.sub: sub = args.sub if args.mediasource: mediasource = args.mediasource if args.tags: tags = args.tags if args.screenshots: screenshots = args.screenshots releasedata = gatherdata() releasedata_and_mediainfo = add_mediainfo_to_releasedata(inputfile, releasedata) if debug: print("Release data and MediaInfo complete. Uploading torrent now.") with open(f'json_data/config.json') as f: cfg = json.load(f) loginData = {'username': cfg['credentials']['username'], 'password': cfg['credentials']['password']} loginUrl = "https://sugoimusic.me/login.php" loginTestUrl = "https://sugoimusic.me" successStr = "Enabled users" passkey = cfg['credentials']['passkey'] annouceurl = "https://tracker.sugoimusic.me:24601/"+passkey+"/announce" # j is an object which can be used to make requests with respect to the loginsession sm = smpy.MyLoginSession(loginUrl, loginData, loginTestUrl, successStr, debug=args.debug) # Acquire authkey authkey = getauthkey() torrentfile = createtorrent(annouceurl, inputfile, releasedata_and_mediainfo) uploadtorrent(torrentfile, imageURL, releasedata_and_mediainfo) # Setting variable for watch/download folders # ftp_watch_folder = cfg['ftp_prefs']['ftp_watch_folder'] # ftp_downloads_folder = cfg['ftp_prefs']['ftp_downloads_folder'] local_watch_folder = cfg['local_prefs']['local_watch_folder'] # local_downloads_folder = cfg['local_prefs']['local_downloads_folder'] if not dryrun: if cfg['local_prefs']['add_to_watch_folder']: os.rename(torrentfile, f"{local_watch_folder}/{torrentfile}")