選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

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