You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

498 line
20 KiB

  1. # get args
  2. # make torrent
  3. # read mediainfo
  4. # upload torrent
  5. # move torrent to watch dir
  6. # Standard library packages
  7. from subprocess import check_output
  8. import re
  9. import os
  10. import sys
  11. import argparse
  12. from urllib.parse import urlparse
  13. import json
  14. # Third-party packages
  15. from bs4 import BeautifulSoup
  16. from torf import Torrent
  17. from pathlib import Path
  18. # JPS-AU files
  19. import smpy
  20. from pymediainfo import MediaInfo
  21. def asciiart ():
  22. print("""
  23. ███████╗███╗ ███╗ █████╗ ██╗ ██╗ ████████╗██╗ ██╗
  24. ██╔════╝████╗ ████║ ██╔══██╗██║ ██║ ╚══██╔══╝██║ ██║
  25. ███████╗██╔████╔██║█████╗███████║██║ ██║█████╗██║ ██║ ██║
  26. ╚════██║██║╚██╔╝██║╚════╝██╔══██║██║ ██║╚════╝██║ ╚██╗ ██╔╝
  27. ███████║██║ ╚═╝ ██║ ██║ ██║╚██████╔╝ ██║ ╚████╔╝
  28. ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝
  29. """)
  30. def getargs():
  31. """
  32. Get arguments using argparse
  33. """
  34. parser = argparse.ArgumentParser()
  35. parser.add_argument('-i', '--input', help='Initiate upload on input file', nargs='?', required=True)
  36. parser.add_argument('-d', '--debug', help='Enable debug mode.', action='store_true')
  37. parser.add_argument("-dry", "--dryrun", help="Dryrun will carry out all actions other than the actual upload to SM.", action="store_true")
  38. parser.add_argument("-a", "--artists", help='Set the artists. (Romaji\English). Split multiple with ","', nargs='?')
  39. parser.add_argument("-oa", "--originalartist", help='Set the artist. (Original Language)', nargs='?')
  40. parser.add_argument("-ca", "--contributingartists", help='Set the contributing artists. (Romaji\English). Split multiple with ","', nargs='?')
  41. parser.add_argument("-ti", "--title", help='Set the title. (Romaji\English)', nargs='?')
  42. parser.add_argument("-oti", "--originaltitle", help='Set the title. (Original Language)', nargs='?')
  43. parser.add_argument("-des", "--description", help='Add a torrent description.', nargs='?', required=True)
  44. parser.add_argument("-t", "--tags", help="Add additional tags to the upload. At least 2 tags are required", nargs='?')
  45. parser.add_argument("-im", "--imageURL", help='Set the torrent cover URL.', nargs='?')
  46. parser.add_argument("-ms", "--mediasource", help='Set the media source.', nargs='?')
  47. parser.add_argument("-rt", "--releasetype", help='Set the release type.', nargs='?')
  48. parser.add_argument("-s", "--sub", help='Set the subtitle type.', nargs='?')
  49. parser.add_argument("-l", "--language", help='Set the language', nargs='?')
  50. parser.add_argument("-y", "--year", help='Set the torrent year (YYYYMMDD or YYYY).', nargs='?')
  51. parser.add_argument("-f", "--freeleech", help="Enables freeleech.", action="store_true")
  52. parser.add_argument("-eti", "--editiontitle", help='Set the edition title', nargs='?')
  53. parser.add_argument("-ey", "--editionyear", help='Set the torrent edition year (YYYYMMDD or YYYY).', nargs='?')
  54. parser.add_argument("-ss", "--screenshots", help='Set the torrent screen shots', nargs='?')
  55. return parser.parse_args()
  56. def gatherdata():
  57. """
  58. Retrieve data about the upload. Ask for user input if necessary.
  59. :return: releasedata: dict
  60. """
  61. releasedata = {"submit": "true"}
  62. releasedata["album_desc"] = description
  63. if artists:
  64. releasedata['idols[]'] = artists
  65. else:
  66. input_english_artist = input("\n" + "_" * 100 + "\nEnter the romaji/english ARTIST name. Separate multiple with \",\". \n")
  67. input_english_artist = [x.strip() for x in input_english_artist.split(',')]
  68. releasedata['idols[]'] = input_english_artist
  69. if originalartist:
  70. releasedata['artist_jp'] = originalartist
  71. else:
  72. 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")
  73. releasedata['artist_jp'] = input_artist
  74. if contributingartists:
  75. input_english_contributing_artist = contributingartists
  76. else:
  77. input_english_contributing_artist = input("\n" + "_" * 100 + "\nEnter the romaji/english CONTRIBUTING ARTIST name. Separate with \",\". Press enter to skip.\n")
  78. if input_english_contributing_artist != "":
  79. input_english_contributing_artist = [x.strip() for x in input_english_contributing_artist.split(',')]
  80. releasedata['contrib_artists[]'] = input_english_contributing_artist
  81. if title:
  82. releasedata['title'] = title
  83. else:
  84. input_english_title = input("\n" + "_" * 100 + "\nEnter the romaji/english TITLE:\n")
  85. releasedata['title'] = input_english_title
  86. if originaltitle:
  87. releasedata['title_jp'] = originaltitle
  88. else:
  89. input_title = input("\n" + "_" * 100 + "\nEnter the original TITLE. Press enter to skip.\n")
  90. releasedata['title_jp'] = input_title
  91. if year:
  92. releasedata["year"] = year
  93. else:
  94. input_year = input("\n" + "_" * 100 + "\nEnter the year as YYYYMMDD or YYYY.\n")
  95. releasedata["year"] = input_year
  96. if editiontitle:
  97. releasedata['remastertitle'] = editiontitle
  98. else:
  99. input_editiontitle = input("\n" + "_" * 100 + "\nEnter the edition TITLE. Press enter to skip.\n")
  100. if input_editiontitle != "":
  101. if editionyear:
  102. releasedata["remasteryear"] = editionyear
  103. else:
  104. input_editionyear = input("\n" + "_" * 100 + "\nEnter the edition year as YYYYMMDD or YYYY.\n")
  105. releasedata["remasteryear"] = input_editionyear
  106. releasedata['remastertitle'] = input_editiontitle
  107. if sub:
  108. releasedata["sub"] = sub
  109. else:
  110. while(True):
  111. input_sub = input("\n" + "_" * 100 + "\nEnter a number to choose subtitle type. \n1=NoSubs\n2=Softsubs\n3=Hardsubs\n")
  112. if input_sub == "1":
  113. releasedata["sub"] = "NoSubs"
  114. break
  115. elif input_sub == "2":
  116. releasedata["sub"] = "Softsubs"
  117. break
  118. elif input_sub == "3":
  119. releasedata["sub"] = "Hardsubs"
  120. break
  121. print("Invalid choice.")
  122. if language:
  123. releasedata["lang"] = language
  124. else:
  125. while(True):
  126. 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")
  127. if input_lang == "1":
  128. releasedata["lang"] = "Japanese"
  129. break
  130. elif input_lang == "2":
  131. releasedata["lang"] = "English"
  132. break
  133. elif input_lang == "3":
  134. releasedata["lang"] = "Korean"
  135. break
  136. elif input_lang == "2":
  137. releasedata["lang"] = "Chinese"
  138. break
  139. elif input_lang == "3":
  140. releasedata["lang"] = "Vietnamese"
  141. break
  142. elif input_lang == "2":
  143. releasedata["lang"] = "Other"
  144. break
  145. print("Invalid choice.")
  146. if mediasource:
  147. releasedata['media'] = mediasource
  148. else:
  149. while(True):
  150. input_lang = input("\n" + "_" * 100 + "\nEnter a number to choose the media source. \n1=HDTV\n2=Web\n")
  151. if input_lang == "1":
  152. releasedata["media"] = "HDTV"
  153. break
  154. elif input_lang == "2":
  155. releasedata["media"] = "Web"
  156. break
  157. print("Invalid choice.")
  158. if tags:
  159. input_tags = tags
  160. else:
  161. input_tags = input("\n" + "_" * 100 + "\nEnter the tags. Separate multiple with \",\". Minimum 2 tags required.\n")
  162. if input_tags != "":
  163. input_tags = [x.strip() for x in input_tags.split(',')]
  164. releasedata["tags"] = input_tags
  165. list_of_types = ["PV", "Music Performance", "TV Music", "TV Variety", "TV-Drama"]
  166. if releasetype in list_of_types:
  167. if releasetype == "PV":
  168. releasedata["type"] = 5
  169. elif releasetype == "Music Performance":
  170. releasedata["type"] = 6
  171. elif releasetype == "TV Music":
  172. releasedata["type"] = 7
  173. elif releasetype == "TV Variety":
  174. releasedata["type"] = 8
  175. elif releasetype == "TV-Drama":
  176. releasedata["type"] = 9
  177. else:
  178. while(True):
  179. 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")
  180. if input_lang == "1":
  181. releasedata["type"] = 5
  182. break
  183. elif input_lang == "2":
  184. releasedata["type"] = 6
  185. break
  186. elif input_lang == "3":
  187. releasedata["type"] = 7
  188. break
  189. elif input_lang == "4":
  190. releasedata["type"] = 8
  191. break
  192. elif input_lang == "5":
  193. releasedata["type"] = 9
  194. break
  195. print("Invalid choice.")
  196. if screenshots:
  197. input_screenshots = screenshots
  198. else:
  199. input_screenshots = input("\n" + "_" * 100 + "\nEnter the screenshot links. Separate multiple with \",\".\n")
  200. if input_screenshots != "":
  201. input_screenshots = input_screenshots.replace(",","\n")
  202. releasedata["screenshots"] = input_screenshots
  203. #https://ptpimg.me/z4w6c0.png\nhttps://ptpimg.me/8txr8v.png
  204. return releasedata
  205. def add_mediainfo_to_releasedata(filename, releasedata):
  206. """
  207. Retrieve mediainfo and append it to the releasedata dictionary.
  208. :return: releasedata: dict
  209. """
  210. mediainfosall = ""
  211. media_info = MediaInfo.parse(filename)
  212. mediainfosall += str(MediaInfo.parse(filename, text=True))
  213. replacement = str(Path(filename).parent)
  214. mediainfosall = mediainfosall.replace(replacement, '')
  215. for track in media_info.tracks:
  216. if track.track_type == 'General':
  217. # releasedataout['language'] = track.audio_language_list # Will need to check if this is reliable
  218. if 'container' not in releasedata: # Not an ISO, only set container if we do not already know its an ISO
  219. releasedata['container'] = track.file_extension.upper()
  220. else: # We have ISO - get category data based Mediainfo if we have it
  221. if track.file_extension.upper() == 'VOB':
  222. releasedata['category'] = 'DVD'
  223. elif track.file_extension.upper() == 'M2TS': # Not used yet as we cannot handle Bluray / UDF
  224. releasedata['category'] = 'Bluray'
  225. if track.track_type == 'Video':
  226. validatecodec = {
  227. "MPEG Video": "MPEG-2",
  228. "AVC": "h264",
  229. "HEVC": "h265",
  230. "MPEG-4 Visual": "DivX", # MPEG-4 Part 2 / h263 , usually xvid / divx
  231. }
  232. for old, new in validatecodec.items():
  233. if track.format == old:
  234. releasedata['codec'] = new
  235. standardresolutions = {
  236. "3840": "1920",
  237. "1920": "1080",
  238. "1280": "720",
  239. "720": "480",
  240. }
  241. for width, height in standardresolutions.items():
  242. if str(track.width) == width and str(track.height) == height:
  243. releasedata['ressel'] = height
  244. if 'ressel' in releasedata.keys(): # Known resolution type, try to determine if interlaced
  245. if track.scan_type == "Interlaced" or track.scan_type == "MBAFF":
  246. releasedata['ressel'] += "i"
  247. else:
  248. releasedata['ressel'] += "p" # Sometimes a Progressive encode has no field set
  249. else: # Custom resolution
  250. releasedata['ressel'] = 'Other'
  251. releasedata['resolution'] = str(track.width) + "x" + str(track.height)
  252. if track.track_type == 'Audio' or track.track_type == 'Audio #1': # Handle multiple audio streams, we just get data from the first for now
  253. if track.format in ["AAC", "DTS", "PCM", "AC3"]:
  254. releasedata['audioformat'] = track.format
  255. elif track.format == "AC-3":
  256. releasedata['audioformat'] = "AC3"
  257. elif track.format == "MPEG Audio" and track.format_profile == "Layer 3":
  258. releasedata['audioformat'] = "MP3"
  259. elif track.format == "MPEG Audio" and track.format_profile == "Layer 2":
  260. releasedata['audioformat'] = "MP2"
  261. releasedata["mediainfo"] = mediainfosall
  262. return releasedata
  263. # Creates torrent file using torf module.
  264. def createtorrent(authkey, filepath, releasedata):
  265. """
  266. Creates a torrent.
  267. :param: authkey: authkey string
  268. :param: filepath: full path of the file for torrent creation
  269. :param: releasedata: dict
  270. :return: filename of created torrent
  271. """
  272. t = Torrent(path=filepath,
  273. trackers=[authkey]) # Torf requires we store authkeys in a list object. This makes it easier to add multiple announce urls.
  274. # Set torrent to private as standard practice for private trackers
  275. t.private = True
  276. t.source = "SugoiMusic"
  277. t.generate()
  278. ## Format releasedata to bring a suitable torrent name.
  279. # The reason we don't just use the directory name is because of an error in POSTING.
  280. # POSTS do not seem to POST hangul/jp characters alongside files.
  281. # filename = f"{releasedata['idols[]']} - {releasedata['title']} [{releasedata['media']}-{releasedata['audioformat']}].torrent"
  282. filename = f"{releasedata['title']}.torrent"
  283. filename = filename.replace("/","")
  284. try:
  285. t.write(filename)
  286. print("_" * 100)
  287. print("Torrent creation:\n")
  288. print(f"{filename} has been created.")
  289. except:
  290. print("_" * 100)
  291. print("Torrent creation:\n")
  292. os.remove(filename)
  293. print(f"{filename} already exists, existing torrent will be replaced.")
  294. t.write(filename)
  295. print(f"{filename} has been created.")
  296. return filename
  297. def getauthkey():
  298. """
  299. Get SM session authkey for use by uploadtorrent() data dict.
  300. Uses SM login data
  301. :return: authkey
  302. """
  303. smpage = sm.retrieveContent("https://sugoimusic.me/torrents.php?id=118") # Arbitrary page on JPS that has authkey
  304. soup = BeautifulSoup(smpage.text, 'html5lib')
  305. rel2 = str(soup.select('#content .thin .main_column .torrent_table tbody'))
  306. authkey = re.findall('authkey=(.*)&torrent_pass=', rel2)
  307. return authkey
  308. def uploadtorrent(torrent, imageURL, releasedata):
  309. """
  310. Uploads a torrent.
  311. :param: torrent: torrent filename.
  312. :param: imageURL: url to a cover image
  313. :param: releasedata: dict
  314. """
  315. # POST url.
  316. uploadurl = "https://sugoimusic.me/upload.php"
  317. # Dataset containing all of the information obtained from our FLAC files.
  318. data = releasedata
  319. data['image'] = imageURL
  320. if not dryrun:
  321. data['auth'] = authkey
  322. if debug:
  323. print('_' * 100)
  324. print('Release Data:\n')
  325. for field in data:
  326. print(field)
  327. print(data)
  328. try:
  329. postDataFiles = {
  330. 'file_input': open(torrent, 'rb')
  331. #'userfile': open(cover, 'rb')
  332. }
  333. except FileNotFoundError:
  334. print("_" * 100)
  335. print('File not found!\nPlease confirm file locations and names. Cover image or .torrent file could not be found')
  336. sys.exit()
  337. # If dryrun argument has not ben passed we will POST the results to JPopSuki.
  338. if dryrun != True:
  339. SMres = sm.retrieveContent(uploadurl, "post", data, postDataFiles)
  340. SMerrorTorrent = re.findall('red; text-align: center;">(.*)</p>', SMres.text)
  341. # SMerrorLogon = re.findall('<p>Invalid (.*)</p>', SMres.text)
  342. if len(SMerrorTorrent)!=0:
  343. print("Upload failed. Torrent error")
  344. print(SMerrorTorrent)
  345. # if len(SMerrorTorrent)!=0:
  346. # print("Upload failed. Logon error")
  347. # print(SMerrorLogon)
  348. ## TODO Filter through JPSres.text and create error handling based on responses
  349. #print(JPSres.text)
  350. def localfileorganization(torrent, watch_folder):
  351. if cfg['local_prefs']['add_to_watch_folder']:
  352. os.rename(torrent, f"{watch_folder}/{torrent}")
  353. if __name__ == "__main__":
  354. asciiart()
  355. args = getargs()
  356. # TODO consider calling args[] directly, we will then not need this line
  357. dryrun = debug = freeleech = imageURL = tags = inputfile = artists = contributingartists = title = None
  358. originalartist = originaltitle = description = editiontitle = editionyear = sub = language = year = mediasource = releasetype = screenshots = None
  359. inputfile = args.input
  360. description = args.description
  361. if args.dryrun:
  362. dryrun = True
  363. if args.debug:
  364. debug = True
  365. if args.freeleech:
  366. freeleech = True
  367. if args.imageURL:
  368. imageURL = args.imageURL
  369. if args.releasetype:
  370. releasetype = args.releasetype
  371. if args.title:
  372. title = args.title
  373. if args.artists:
  374. artists = args.artists
  375. if args.contributingartists:
  376. contributingartists = args.contributingartists
  377. if args.originalartist:
  378. originalartist = args.originalartist
  379. if args.originaltitle:
  380. originaltitle = args.originaltitle
  381. if args.editiontitle:
  382. editiontitle = args.editiontitle
  383. if args.language:
  384. language = args.language
  385. if args.year:
  386. year = args.year
  387. if args.editionyear:
  388. editionyear = args.editionyear
  389. if args.sub:
  390. sub = args.sub
  391. if args.mediasource:
  392. mediasource = args.mediasource
  393. if args.tags:
  394. tags = args.tags
  395. if args.screenshots:
  396. screenshots = args.screenshots
  397. releasedata = gatherdata()
  398. releasedata_and_mediainfo = add_mediainfo_to_releasedata(inputfile, releasedata)
  399. if debug:
  400. print("Release data and MediaInfo complete. Uploading torrent now.")
  401. with open(f'json_data/config.json') as f:
  402. cfg = json.load(f)
  403. loginData = {'username': cfg['credentials']['username'], 'password': cfg['credentials']['password']}
  404. loginUrl = "https://sugoimusic.me/login.php"
  405. loginTestUrl = "https://sugoimusic.me"
  406. successStr = "Enabled users"
  407. passkey = cfg['credentials']['passkey']
  408. annouceurl = "https://tracker.sugoimusic.me:24601/"+passkey+"/announce"
  409. # j is an object which can be used to make requests with respect to the loginsession
  410. sm = smpy.MyLoginSession(loginUrl, loginData, loginTestUrl, successStr, debug=args.debug)
  411. # Acquire authkey
  412. authkey = getauthkey()
  413. torrentfile = createtorrent(annouceurl, inputfile, releasedata_and_mediainfo)
  414. uploadtorrent(torrentfile, imageURL, releasedata_and_mediainfo)
  415. # Setting variable for watch/download folders
  416. ftp_watch_folder = cfg['ftp_prefs']['ftp_watch_folder']
  417. ftp_downloads_folder = cfg['ftp_prefs']['ftp_downloads_folder']
  418. local_watch_folder = cfg['local_prefs']['local_watch_folder']
  419. local_downloads_folder = cfg['local_prefs']['local_downloads_folder']
  420. if not dryrun:
  421. if cfg['local_prefs']['add_to_watch_folder'] or cfg['local_prefs']['add_to_downloads_folder']:
  422. localfileorganization(torrent=torrentfile, watch_folder=local_watch_folder)