Based off JPS-AU
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

838 řádky
34 KiB

  1. # Standard library packages
  2. import re
  3. import os
  4. import sys
  5. import shutil
  6. import string
  7. import argparse
  8. import html
  9. from urllib.parse import urlparse
  10. import json
  11. import ftplib
  12. # Third-party packages
  13. import requests
  14. from bs4 import BeautifulSoup
  15. from mutagen.flac import FLAC
  16. from mutagen.mp3 import MP3
  17. from torf import Torrent
  18. from tqdm import tqdm
  19. from langdetect import detect
  20. # JPS-AU files
  21. import smpy
  22. def asciiart ():
  23. print("""
  24. ███████╗███╗ ███╗ █████╗ ██╗ ██╗
  25. ██╔════╝████╗ ████║ ██╔══██╗██║ ██║
  26. ███████╗██╔████╔██║█████╗███████║██║ ██║
  27. ╚════██║██║╚██╔╝██║╚════╝██╔══██║██║ ██║
  28. ███████║██║ ╚═╝ ██║ ██║ ██║╚██████╔╝
  29. ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝
  30. """)
  31. # Get arguments using argparse
  32. def getargs():
  33. parser = argparse.ArgumentParser()
  34. parser.add_argument('-dir', '--directory', help='Initiate upload on directory.', nargs='?', required=True)
  35. # parser.add_argument("-f", "--freeleech", help="Enables freeleech.", action="store_true")
  36. parser.add_argument("-t", "--tags", help="Add additional tags to the upload.", nargs='?')
  37. parser.add_argument('-n', '--debug', help='Enable debug mode.', action='store_true')
  38. parser.add_argument("-d", "--dryrun", help="Dryrun will carry out all actions other than the actual upload to JPS.", action="store_true")
  39. parser.add_argument("-im", "--imageURL", help='Set the torrent cover URL.', nargs='?')
  40. parser.add_argument("-a", "--artists", help='Set the artists. (Romaji\English)', nargs='?')
  41. parser.add_argument("-ca", "--contributingartists", help='Set the contributing artists. (Romaji\English)', nargs='?')
  42. parser.add_argument("-rt", "--releasetype", help='Set the release type. (Album, Single, EP)', nargs='?')
  43. parser.add_argument("-ti", "--title", help='Set the title. (Romaji\English)', nargs='?')
  44. parser.add_argument("-eti", "--editiontitle", help='Set the edition title', nargs='?')
  45. parser.add_argument("-ey", "--editionyear", help='Set the torrent edition year (YYYYMMDD or YYYY).', nargs='?')
  46. parser.add_argument("-ms", "--mediasource", help='Set the media source. (CD, Web, Vinyl)', nargs='?')
  47. parser.add_argument("-tdes", "--torrentdescription", help='Add a torrent description', nargs='?')
  48. return parser.parse_args()
  49. # Acquire the authkey used for torrent files from upload.php
  50. def getauthkey():
  51. """
  52. Get SM session authkey for use by uploadtorrent() data dict.
  53. Uses SM login data
  54. :return: authkey
  55. """
  56. smpage = sm.retrieveContent("https://sugoimusic.me/torrents.php?id=118") # Arbitrary page on JPS that has authkey
  57. soup = BeautifulSoup(smpage.text, 'html5lib')
  58. rel2 = str(soup.select('#content .thin .main_column .torrent_table tbody'))
  59. authkey = re.findall('authkey=(.*)&torrent_pass=', rel2)
  60. return authkey
  61. def copytree(src, dst, symlinks=False, ignore=None):
  62. for item in os.listdir(src):
  63. s = os.path.join(src, item)
  64. d = os.path.join(dst, item)
  65. if os.path.isdir(s):
  66. shutil.copytree(s, d, symlinks, ignore)
  67. else:
  68. shutil.copy2(s, d)
  69. # Creates torrent file using torf module.
  70. def createtorrent(authkey, directory, filename, releasedata):
  71. t = Torrent(path=directory,
  72. trackers=[authkey]) # Torf requires we store authkeys in a list object. This makes it easier to add multiple announce urls.
  73. # Set torrent to private as standard practice for private trackers
  74. t.private = True
  75. t.source = "SugoiMusic"
  76. t.generate()
  77. ## Format releasedata to bring a suitable torrent name.
  78. # The reason we don't just use the directory name is because of an error in POSTING.
  79. # POSTS do not seem to POST hangul/jp characters alongside files.
  80. # filename = f"{releasedata['idols[]']} - {releasedata['title']} [{releasedata['media']}-{releasedata['audioformat']}].torrent"
  81. filename = f"{releasedata['title']} [{releasedata['media']}-{releasedata['audioformat']}].torrent"
  82. filename = filename.replace("/","/")
  83. filename = filename.replace(":",":")
  84. filename = filename.replace("?","")
  85. try:
  86. t.write(filename)
  87. print("_" * 100)
  88. print("Torrent creation:\n")
  89. print(f"{filename} has been created.")
  90. except:
  91. print("_" * 100)
  92. print("Torrent creation:\n")
  93. os.remove(filename)
  94. print(f"{filename} already exists, existing torrent will be replaced.")
  95. t.write(filename)
  96. print(f"{filename} has been created.")
  97. return filename
  98. # Reads FLAC file and returns metadata.
  99. def readflac(filename):
  100. read = FLAC(filename)
  101. # get some audio info
  102. audio_info={
  103. "SAMPLE_RATE": read.info.sample_rate,
  104. "BIT_DEPTH": read.info.bits_per_sample
  105. }
  106. # Create dict containing all meta fields we'll be using.
  107. tags={
  108. "ALBUM": read.get('album'),
  109. "ALBUMARTIST": read.get('albumartist'),
  110. "ARTIST": read.get('artist'),
  111. "DATE": read.get('date')[0],
  112. "GENRE": "",#read.get('genre'),
  113. "TITLE": read.get('title'),
  114. "COMMENT": read.get('comment'),
  115. "TRACKNUMBER": read.get('tracknumber')[0].zfill(2),
  116. "DISCNUMBER": read.get('discnumber')}
  117. # Not further looked into this but some FLACs hold a grouping key of contentgroup instead of grouping.
  118. tags['GROUPING'] = read.get('grouping')
  119. ## If grouping returns None we check contentgroup.
  120. # If it still returns none we will ignore it and handle on final checks
  121. if tags['GROUPING'] == None:
  122. tags['GROUPING'] = read.get('contentgroup')
  123. required_tags = ['ALBUM', 'ALBUMARTIST','DATE','TRACKNUMBER']
  124. for k,v in tags.items():
  125. if v == None:
  126. if k in required_tags:
  127. print(f"{k} has returned {v}, this is a required tag")
  128. sys.exit()
  129. return tags, audio_info
  130. # Reads MP3 file and returns metadata.
  131. def readmp3(filename):
  132. read = MP3(filename)
  133. # Create dict containing all meta fields we'll be using.
  134. tags={
  135. "ALBUM": read.get('TALB'), # Album Title
  136. "ALBUMARTIST": read.get('TPE2'), # Album Artist
  137. "ARTIST": read.get('TPE1'), # Track Artist
  138. "DATE": str(read.get('TDRC')), # Date YYYYMMDD (Will need to add a try/except for other possible identifiers)
  139. "GENRE": read.get('TCON').text, # Genre
  140. "TITLE": read.get('TIT2'), # Track Title
  141. "COMMENT": read.get('COMM::eng'), # Track Comment
  142. "GROUPING": read.get('TIT1'), # Grouping
  143. "TRACKNUMBER": re.sub(r"\/.*", "", str(read.get('TRCK'))).zfill(2), # Tracknumber (Format #/Total) Re.sub removes /#
  144. "DISCNUMBER": re.sub(r"\/.*", "", str(read.get('TPOS')))} # Discnumber (Format #/Total) Re.sub removes /#
  145. required_tags = ['ALBUM', 'ALBUMARTIST','DATE','TRACKNUMBER']
  146. for k,v in tags.items():
  147. if v == None:
  148. if k in required_tags:
  149. print(f"{k} has returned {v}, this is a required tag")
  150. sys.exit()
  151. return tags
  152. # Generates new log file based on directory contents
  153. def generatelog(track_titles, log_filename, log_directory):
  154. # Seperate each tracklist entry in the list with a newline
  155. track_titles = '\n'.join([str(x) for x in track_titles])
  156. # Format tracklist layout
  157. log_contents = f"""[size=5][b]Tracklist[/b][/size]\n{track_titles}
  158. """
  159. # If we have chosen to save the tracklist then we write log_contents to a .log file within the log directory specified
  160. if cfg['local_prefs']['save_tracklist']:
  161. # Write to {album_name}.log
  162. with open(f"{log_directory}/{log_filename}.log", "w+") as f:
  163. f.write(log_contents)
  164. # Reset position to first line and read
  165. f.seek(0)
  166. log_contents = f.read()
  167. f.close()
  168. # If debug mode is enabled we will print the log contents.
  169. if debug:
  170. print("_" * 100)
  171. print(f"Log Contents/Tracklisting: {log_contents}")
  172. return log_contents
  173. def readlog(log_name, log_directory):
  174. with open(f"{log_directory}/{log_name}.log", "r+") as f:
  175. log_contents = f.read()
  176. f.close()
  177. return log_contents
  178. def add_to_hangul_dict(hangul , english , category):
  179. hangul = str(hangul)
  180. english = str(english)
  181. categories = ['version','general','artist','genres', 'label', 'distr']
  182. file = f"json_data/dictionary.json"
  183. json_file = open(file, 'r', encoding='utf-8', errors='ignore')
  184. dictionary = json.load(json_file)
  185. json_file.close()
  186. new = dict()
  187. for cats in dictionary:
  188. #== Create the categories in the new temp file
  189. new[cats] = dict()
  190. for key,value in dictionary[cats].items():
  191. #== List all the old items into the new dict
  192. new[cats][key] = value
  193. if hangul in new[category].keys():
  194. if new[category].get(hangul) is None:
  195. if english != 'None':
  196. new[category][hangul] = english
  197. else:
  198. #== Only update if English word has been supplied ==#
  199. if english != 'None':
  200. new[category][hangul] = english
  201. else:
  202. if english == 'None':
  203. new[category][hangul] = None
  204. else:
  205. new[category][hangul] = english
  206. json_write = open(file, 'w+', encoding='utf-8')
  207. json_write.write(json.dumps(new, indent=4, ensure_ascii=False))
  208. json_write.close()
  209. def translate(string, category, result=None, output=None):
  210. file = "json_data/dictionary.json"
  211. with open(file, encoding='utf-8', errors='ignore') as f:
  212. dictionary = json.load(f, strict=False)
  213. category = str(category)
  214. string = str(string)
  215. search = dictionary[category]
  216. string = string.strip()
  217. if string == 'Various Artists':
  218. output = ['Various Artists',None]
  219. else:
  220. #== NO NEED TO SEARCH - STRING HAS HANGUL+ENGLISH or HANGUL+HANGUL ==#
  221. if re.search("\((?P<inside>.*)\)", string):
  222. #== Complete translation, add to dictionary with both values ==#
  223. #== Contains parentheses, need to split
  224. parenthesis = string.split("(")
  225. pre_parenthesis = parenthesis[0].strip()
  226. in_parenthesis = parenthesis[1].replace(")","").strip()
  227. #== Check the order of the parentheses ==#
  228. if re.search("[^\u0000-\u007F]+",pre_parenthesis) and re.search("[^\u0000-\u007F]+",in_parenthesis):
  229. #== Both hangul
  230. first = 'kr'
  231. second = 'kr'
  232. else:
  233. if re.search("[^\u0000-\u007F]+",pre_parenthesis):
  234. first = 'kr'
  235. second = 'eng'
  236. else:
  237. first = 'eng'
  238. second = 'kr'
  239. if first == 'kr' and second == 'eng':
  240. #== Hangul first ==#
  241. hangul = pre_parenthesis
  242. english = in_parenthesis
  243. add_to_hangul_dict(hangul,english,category)
  244. elif first == 'eng' and second == 'kr':
  245. #== English first ==#
  246. hangul = in_parenthesis
  247. english = pre_parenthesis
  248. add_to_hangul_dict(hangul,english,category)
  249. elif first == 'kr' and second == 'kr':
  250. #== Both Hangul ==#
  251. hangul = pre_parenthesis
  252. english = None
  253. add_to_hangul_dict(pre_parenthesis,None,category)
  254. add_to_hangul_dict(hangul,None,category)
  255. else:
  256. #== Both English
  257. hangul = None
  258. english = pre_parenthesis
  259. output = [hangul,english]
  260. #== No parentheses - HANGUL
  261. else:
  262. #== If the input string is a full Hangul word - check dictionary and then add if necessary)
  263. if re.search("[^\u0000-\u007F]+", string):
  264. if string in search.keys():
  265. #== yes
  266. if search.get(string) is None:
  267. #== If the keyword does not have a translation, add it to the dictionary ==#
  268. output = [string,None]
  269. else:
  270. #== Translation already exists, output the result in a list ==#
  271. output = [string,search.get(string)]
  272. else:
  273. output = [string,None]
  274. add_to_hangul_dict(string, None, category)
  275. #== Full English name -- leave it
  276. else:
  277. for key,value in search.items():
  278. if key == string:
  279. output = [value,string]
  280. break
  281. else:
  282. output = [string,string]
  283. return output
  284. def determine_flac_bitdepth_and_samplerate(audio_info):
  285. if audio_info['BIT_DEPTH'] == 16:
  286. return "Lossless"
  287. elif audio_info['BIT_DEPTH'] == 24 and audio_info['SAMPLE_RATE'] == 96000:
  288. return "24bit Lossless 96kHz"
  289. elif audio_info['BIT_DEPTH'] == 24 and audio_info['SAMPLE_RATE'] == 48000:
  290. return "24bit Lossless 48kHz"
  291. else:
  292. return "24bit Lossless"
  293. def gatherdata(directory):
  294. # Lists for storing some
  295. list_album_artists = []
  296. list_track_artists = []
  297. list_album = []
  298. list_genre = []
  299. translated_genre = []
  300. translated_album_artists = []
  301. tracklist_entries = []
  302. # Creation of releasedata dict, this will store formatted meta used for the POST.
  303. releasedata = {}
  304. ## Set no log as default value.
  305. # This will be set to True is a .log file is found, in turn this will allow us to determine if WEB or CD.
  306. log_available = False
  307. flac_present = False
  308. mp3_present = False
  309. # Read directory contents, grab metadata of .FLAC files.
  310. for file in os.listdir(directory):
  311. file_location = os.path.join(directory, file)
  312. if file.endswith(".flac"):
  313. # Read FLAC file to grab meta
  314. tags, audio_info = readflac(file_location)
  315. flac_present = True
  316. # If Discnumber isn't present then we omit it from the tracklist entry
  317. if tags['DISCNUMBER'] == None:
  318. tracklist_entry = f"[b]{tags['TRACKNUMBER']}[/b]. {tags['TITLE'][0]}"
  319. else:
  320. tracklist_entry = f"[b]{tags['DISCNUMBER'][0]}-{tags['TRACKNUMBER']}[/b]. {tags['TITLE'][0]}"
  321. tracklist_entries.append(tracklist_entry)
  322. if debug:
  323. print ("_" * 100)
  324. print(f"Tags for {file}:\n{tags}")
  325. if file.endswith(".mp3"):
  326. # Read MP3 file to grab meta
  327. tags = readmp3(file_location)
  328. mp3_present = True
  329. # If Discnumber isn't present then we omit it from the tracklist entry
  330. if tags['DISCNUMBER'] == "None":
  331. tracklist_entry = f"[b]{tags['TRACKNUMBER']}[/b]. {tags['TITLE'][0]}"
  332. else:
  333. tracklist_entry = f"[b]{tags['DISCNUMBER']}-{tags['TRACKNUMBER']}[/b]. {tags['TITLE'][0]}"
  334. tracklist_entries.append(tracklist_entry)
  335. if debug:
  336. print ("_" * 100)
  337. print(f"Tags for {file}:\n{tags}")
  338. # If only one genre in list attempt to split as there's likely more.
  339. if len(tags['GENRE']) == 1:
  340. tags['GENRE'] = tags['GENRE'][0].split(";")
  341. for aa in tags['ALBUMARTIST']:
  342. list_album_artists.append(aa)
  343. for a in tags['ARTIST']:
  344. list_track_artists.append(a)
  345. list_album.append(tags['ALBUM'][0])
  346. # for g in tags['GENRE']:
  347. # list_genre.append(g)
  348. # Check files to make sure there's no multi-format.
  349. if flac_present:
  350. format = 'FLAC'
  351. bitrate = determine_flac_bitdepth_and_samplerate(audio_info)
  352. if mp3_present:
  353. format = 'MP3'
  354. bitrate = '320'
  355. if flac_present and mp3_present:
  356. print("Mutt detected, exiting.")
  357. sys.exit()
  358. if file.endswith(".log"):
  359. log_available = True
  360. if log_available == True:
  361. media = 'CD'
  362. else:
  363. media = 'Web'
  364. # Load Dict.json for translations
  365. file = "json_data/dictionary.json"
  366. with open(file, encoding='utf-8', errors='ignore') as f:
  367. dictionary = json.load(f, strict=False)
  368. # Split additional genre's at comma and append to existing genre tags
  369. if additional_tags != None:
  370. split_tags = additional_tags.split(",")
  371. for s in split_tags:
  372. list_genre.append(s)
  373. # Translate genre's using dict and append to translated_genre
  374. for g in set(list_genre):
  375. translation = translate(g, "genres")[0]
  376. translated_genre.append(translation)
  377. # Translate artist's using dict and append to translated_album_artists
  378. for a in set(list_album_artists):
  379. if tags['ALBUMARTIST'][0] == 'Various Artists':
  380. translated_artist_name = 'V.A.'
  381. translated_album_artists.append("V.A.")
  382. else:
  383. translated_artist_name = translate(string=tags['ALBUMARTIST'][0], category="artist")
  384. translated_album_artists.append(translated_artist_name[0])
  385. ## Identify unique values using sets.
  386. unique_album_artists = ','.join(set(translated_album_artists))
  387. unique_track_artists = ','.join(set(list_track_artists))
  388. unique_genre = ','.join(set(translated_genre))
  389. unique_album = set(list_album)
  390. ## Acquire contents of our log file to be used for album description
  391. # Comments store the album id which matches our log names, so we can use the comment tag to find our album descriptions.
  392. log_directory = cfg['local_prefs']['log_directory']
  393. # Album description taken from log file.
  394. if cfg['local_prefs']['generate_tracklist']:
  395. log_filename = f"{unique_album_artists} - {tags['ALBUM'][0]}"
  396. album_description = generatelog(tracklist_entries, log_filename, log_directory)
  397. else:
  398. log_filename = tags['COMMENT'][0]
  399. album_description = readlog(log_filename, log_directory)
  400. ## If release description is enabled we apply comments to the bugs album url
  401. # Note that this is dependant on the album being sourced from bugs so should be changed per user.
  402. if cfg['local_prefs']['enable_release_description']:
  403. try:
  404. release_description = f"Sourced from [url=https://music.bugs.co.kr/album/{tags['COMMENT'][0]}]Bugs[/url]"
  405. # If any exceptions occur we will return to no release description
  406. except:
  407. release_description = ""
  408. # If release description is not enabled we will use no release description
  409. else:
  410. release_description = ""
  411. ## Assign all our unique values into releasedata{}. We'll use this later down the line for POSTING.
  412. # POST values can be found by inspecting JPS HTML
  413. releasedata['submit'] = 'true'
  414. # # List of accepted upload types
  415. # accepted_types = ['Album', 'Single', 'EP']
  416. # # Get type from args if present
  417. # if releasetype:
  418. # releasedata['type'] = releasetype
  419. # else:
  420. # # If type errors then we ask for user input
  421. # try:
  422. # releasedata['type'] = translate(tags['GROUPING'][0], "release_types")[0]
  423. # except TypeError:
  424. # releasedata['type'] = input("\n" + "_" * 100 + "\nGrouping is empty or has received an error, please enter manually (Album/Single/EP)\n")
  425. # # If type is still not in accepted_types we ask for user input again and do not break loop until correct
  426. # if releasedata['type'] not in accepted_types:
  427. # while True:
  428. # releasedata['type'] = input("\n" + "_" * 100 + "\nGrouping tag or release type argument did not return an album type, please enter manually (Album/Single/EP)\n")
  429. # if releasedata['type'] not in accepted_types:
  430. # continue
  431. # else:
  432. # break
  433. # # SM uses numbers for it's types
  434. # if releasedata['type'] == "Album":
  435. # releasedata['type'] = 0
  436. # elif releasedata['type'] == "Single":
  437. # releasedata['type'] = 2
  438. # else: # EP type
  439. # releasedata['type'] = 1
  440. accepted_types = ['Album', 'Single', 'EP']
  441. if releasedata['type'] not in accepted_types:
  442. if releasetype:
  443. releasedata['type'] = releasetype
  444. else:
  445. while(True):
  446. input_releasetype = input("\n" + "_" * 100 + "\nEnter a number to choose the release type. \n1=Album\n2=Single\n3=EP\n")
  447. if input_releasetype == "1":
  448. releasedata["type"] = "Album"
  449. break
  450. elif input_releasetype == "2":
  451. releasedata["type"] = "Single"
  452. break
  453. elif input_releasetype == "3":
  454. releasedata["type"] = "EP"
  455. break
  456. print("Invalid choice.")
  457. else:
  458. releasedata['type'] = releasetype
  459. releasedata['title'] = tags['ALBUM'][0]
  460. releasedata['idols[]'] = unique_album_artists
  461. # If the value of album artist and artist is the same, we don't need to POST original artist.
  462. if unique_album_artists != unique_track_artists:
  463. releasedata['artist_jp'] = unique_track_artists
  464. #re.sub removes any date separators, jps doesn't accept them
  465. releasedata['year'] = re.sub(r"[^0-9]", "", tags['DATE'])
  466. releasedata['audioformat'] = format
  467. releasedata['bitrate'] = bitrate
  468. releasedata['media'] = media
  469. releasedata['album_desc'] = album_description
  470. releasedata['release_desc'] = release_description
  471. releasedata['tags'] = unique_genre
  472. # Enable freeleech if arg is passed
  473. if freeleech:
  474. releasedata['freeleech'] = "true"
  475. ## Language Checks
  476. # This is a required check as we don't want to enter non-english/romaji characters into the title/artist field.
  477. en = detectlanguage(releasedata['title'])
  478. if debug:
  479. print("_" * 100)
  480. print("Title/Artist Language:\n")
  481. print(f"{releasedata['title']} < English = {en}")
  482. if en == False:
  483. if title:
  484. input_english_title = title
  485. else:
  486. input_english_title = input("\n" + "_" * 100 + "\nPlease enter the romaji/english title:\n")
  487. # Create new key called title_jp and assign the old title to it
  488. releasedata['title_jp'] = releasedata['title']
  489. # Replace title with the user input.
  490. releasedata['title'] = input_english_title
  491. en = detectlanguage(releasedata['idols[]'])
  492. if debug:
  493. print(f"{releasedata['idols[]']} < English = {en}")
  494. if en == False:
  495. if artists:
  496. input_english_artist = artists
  497. else:
  498. input_english_artist = input("\n" + "_" * 100 + "\nPlease enter the romaji/english artist name. Separate multiple main artists with \",\".\n")
  499. input_english_artist = [x.strip() for x in input_english_artist.split(',')]
  500. releasedata['idols[]'] = input_english_artist
  501. if contributingartists:
  502. input_english_contributing_artist = contributingartists
  503. else:
  504. input_english_contributing_artist = input("\n" + "_" * 100 + "\nPlease enter the romaji/english artist name. Separate multiple contributing artists with \",\". Press enter to skip.\n")
  505. if input_english_contributing_artist != "":
  506. input_english_contributing_artist = [x.strip() for x in input_english_contributing_artist.split(',')]
  507. releasedata['contrib_artists[]'] = input_english_contributing_artist
  508. if mediasource:
  509. releasedata['media'] = mediasource
  510. else:
  511. while(True):
  512. input_mediasource = input("\n" + "_" * 100 + "\nEnter a number to choose the media source. \n1=CD\n2=Web\n3=Vinyl\n")
  513. if input_mediasource == "1":
  514. releasedata["media"] = "CD"
  515. break
  516. elif input_mediasource == "2":
  517. releasedata["media"] = "Web"
  518. break
  519. elif input_mediasource == "3":
  520. releasedata["media"] = "Vinyl"
  521. break
  522. print("Invalid choice.")
  523. if editiontitle:
  524. releasedata['remastertitle'] = editiontitle
  525. else:
  526. input_editiontitle = input("\n" + "_" * 100 + "\nEnter the edition TITLE. Press enter to skip.\n")
  527. print(input_editiontitle)
  528. if input_editiontitle != "":
  529. if editionyear:
  530. releasedata["remasteryear"] = editionyear
  531. else:
  532. input_editionyear = input("\n" + "_" * 100 + "\nEnter the edition year as YYYYMMDD or YYYY.\n")
  533. releasedata["remasteryear"] = input_editionyear
  534. releasedata['remastertitle'] = input_editiontitle
  535. return releasedata
  536. # Simple function to split a string up into characters
  537. def split(word):
  538. return [char for char in word]
  539. def detectlanguage(string):
  540. ## Language Detect
  541. # This is a required check as we don't want to enter non-english/romaji characters into the title field.
  542. characters = split(string)
  543. language_list = []
  544. for c in characters:
  545. try:
  546. language = detect(c)
  547. language_list.append(language)
  548. except:
  549. langauge = "error"
  550. if 'ko' or 'ja' in language_list:
  551. en = False
  552. else:
  553. en = True
  554. return en
  555. def uploadtorrent(torrent, imageURL, releasedata):
  556. # POST url.
  557. uploadurl = "https://sugoimusic.me/upload.php"
  558. # Dataset containing all of the information obtained from our FLAC files.
  559. data = releasedata
  560. data['image'] = imageURL
  561. if not dryrun:
  562. data['auth'] = authkey
  563. if debug:
  564. print('_' * 100)
  565. print('Release Data:\n')
  566. print(releasedata)
  567. try:
  568. postDataFiles = {
  569. 'file_input': open(torrent, 'rb')
  570. #'userfile': open(cover, 'rb')
  571. }
  572. except FileNotFoundError:
  573. print("_" * 100)
  574. print('File not found!\nPlease confirm file locations and names. Cover image or .torrent file could not be found')
  575. sys.exit()
  576. # If dryrun argument has not ben passed we will POST the results to JPopSuki.
  577. if dryrun != True:
  578. SMres = sm.retrieveContent(uploadurl, "post", data, postDataFiles)
  579. SMerrorTorrent = re.findall('red; text-align: center;">(.*)</p>', SMres.text)
  580. # SMerrorLogon = re.findall('<p>Invalid (.*)</p>', SMres.text)
  581. if len(SMerrorTorrent)!=0:
  582. print("Upload failed. Torrent error")
  583. print(SMerrorTorrent)
  584. # if len(SMerrorTorrent)!=0:
  585. # print("Upload failed. Logon error")
  586. # print(SMerrorLogon)
  587. if dryrun != True:
  588. print('\nUpload POSTED. It may take a moment for this upload to appear on SugoiMusic.')
  589. ## TODO Filter through JPSres.text and create error handling based on responses
  590. #print(JPSres.text)
  591. # Function for transferring the contents of the torrent as well as the torrent.
  592. def ftp_transfer(fileSource, fileDestination, directory, folder_name, watch_folder):
  593. # Create session
  594. session = ftplib.FTP(cfg['ftp_prefs']['ftp_server'],cfg['ftp_prefs']['ftp_username'],cfg['ftp_prefs']['ftp_password'])
  595. # Set session encoding to utf-8 so we can properly handle hangul/other special characters
  596. session.encoding='utf-8'
  597. # Successful FTP Login Print
  598. print("_" * 100)
  599. print("FTP Login Successful")
  600. print(f"Server Name: {cfg['ftp_prefs']['ftp_server']} : Username: {cfg['ftp_prefs']['ftp_username']}\n")
  601. if cfg['ftp_prefs']['add_to_downloads_folder']:
  602. # Create folder based on the directory name of the folder within the torrent.
  603. try:
  604. session.mkd(f"{fileDestination}/{folder_name}")
  605. print(f'Created directory {fileDestination}/{folder_name}')
  606. except ftplib.error_perm:
  607. pass
  608. # Notify user we are beginning the transfer.
  609. print(f"Beginning transfer...")
  610. # Set current folder to the users preferred destination
  611. session.cwd(f"{fileDestination}/{folder_name}")
  612. # Transfer each file in the chosen directory
  613. for file in os.listdir(directory):
  614. with open(f"{directory}/{file}",'rb') as f:
  615. filesize = os.path.getsize(f"{directory}/{file}")
  616. ## Transfer file
  617. # tqdm used for better user feedback.
  618. with tqdm(unit = 'blocks', unit_scale = True, leave = False, miniters = 1, desc = f'Uploading [{file}]', total = filesize) as tqdm_instance:
  619. session.storbinary('STOR ' + file, f, 2048, callback = lambda sent: tqdm_instance.update(len(sent)))
  620. print(f"{file} | Complete!")
  621. f.close()
  622. if cfg['ftp_prefs']['add_to_watch_folder']:
  623. with open(fileSource,'rb') as t:
  624. # Set current folder to watch directory
  625. session.cwd(watch_folder)
  626. ## Transfer file
  627. # We avoid tqdm here due to the filesize of torrent files.
  628. # Most connections will upload these within 1-3s, resulting in near useless progress bars.
  629. session.storbinary(f"STOR {torrentfile}", t)
  630. print(f"{torrentfile} | Sent to watch folder!")
  631. t.close()
  632. # Quit session when complete.
  633. session.quit()
  634. def localfileorganization(torrent, directory, watch_folder, downloads_folder):
  635. # Move torrent directory to downloads_folder
  636. if cfg['local_prefs']['add_to_downloads_folder']:
  637. try:
  638. os.mkdir(os.path.join(downloads_folder, os.path.basename(directory)))
  639. except FileExistsError:
  640. pass
  641. copytree(directory, os.path.join(downloads_folder, os.path.basename(directory)))
  642. shutil.rmtree(directory)
  643. if cfg['local_prefs']['add_to_watch_folder']:
  644. os.rename(torrent, f"{watch_folder}/{torrent}")
  645. if __name__ == "__main__":
  646. asciiart()
  647. args = getargs()
  648. with open(f'json_data/config.json') as f:
  649. cfg = json.load(f)
  650. # TODO consider calling args[] directly, we will then not need this line
  651. dryrun = torrentdescription = freeleech = tags = directory = debug = imageURL = artists = contributingartists = releasetype = title = editiontitle = editionyear = mediasource = audio_info = None
  652. directory = args.directory
  653. additional_tags = args.tags
  654. if args.dryrun:
  655. dryrun = True
  656. if args.debug:
  657. debug = True
  658. # if args.freeleech:
  659. # freeleech = True
  660. if torrentdescription:
  661. torrentdescription = args.torrentdescription
  662. if args.imageURL:
  663. imageURL = args.imageURL
  664. if args.releasetype:
  665. releasetype = args.releasetype
  666. if args.title:
  667. title = args.title
  668. if args.artists:
  669. artists = args.artists
  670. if args.contributingartists:
  671. contributingartists = args.contributingartists
  672. if args.editiontitle:
  673. editiontitle = args.editiontitle
  674. if args.editionyear:
  675. editionyear = args.editionyear
  676. if args.mediasource:
  677. mediatype = args.mediasource
  678. # Load login credentials from JSON and use them to create a login session.
  679. loginData = {'username': cfg['credentials']['username'], 'password': cfg['credentials']['password']}
  680. loginUrl = "https://sugoimusic.me/login.php"
  681. loginTestUrl = "https://sugoimusic.me"
  682. successStr = "Enabled users"
  683. passkey = cfg['credentials']['passkey']
  684. annouceurl = "https://tracker.sugoimusic.me:24601/"+passkey+"/announce"
  685. # j is an object which can be used to make requests with respect to the loginsession
  686. sm = smpy.MyLoginSession(loginUrl, loginData, loginTestUrl, successStr, debug=args.debug)
  687. # Acquire authkey
  688. authkey = getauthkey()
  689. # Gather data of FLAC file
  690. releasedata = gatherdata(directory)
  691. # Folder_name equals the last folder in the path, this is used to rename .torrent files to something relevant.
  692. folder_name = os.path.basename(os.path.normpath(directory))
  693. # Identifying cover.jpg path
  694. # cover_path = directory + "/" + cfg['local_prefs']['cover_name']
  695. # Create torrent file.
  696. #torrentfile = createtorrent(authkey, directory, folder_name, releasedata)
  697. torrentfile = createtorrent(annouceurl, directory, folder_name, releasedata)
  698. # Upload torrent to SugoiMusic
  699. uploadtorrent(torrentfile, imageURL, releasedata)
  700. # Setting variable for watch/download folders
  701. ftp_watch_folder = cfg['ftp_prefs']['ftp_watch_folder']
  702. ftp_downloads_folder = cfg['ftp_prefs']['ftp_downloads_folder']
  703. local_watch_folder = cfg['local_prefs']['local_watch_folder']
  704. local_downloads_folder = cfg['local_prefs']['local_downloads_folder']
  705. if not dryrun:
  706. if cfg['ftp_prefs']['enable_ftp']:
  707. ftp_transfer(fileSource=torrentfile, fileDestination=ftp_downloads_folder, directory=directory, folder_name=folder_name, watch_folder=ftp_watch_folder)
  708. if cfg['local_prefs']['add_to_watch_folder'] or cfg['local_prefs']['add_to_downloads_folder']:
  709. localfileorganization(torrent=torrentfile, directory=directory, watch_folder=local_watch_folder, downloads_folder=local_downloads_folder)