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.

upload.py 24 KiB

2 vuotta sitten
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. # Standard library packages
  2. from pickle import FALSE
  3. import re
  4. import os
  5. import sys
  6. import shutil
  7. import string
  8. import argparse
  9. import html
  10. from urllib.parse import urlparse
  11. import json
  12. import ftplib
  13. # Third-party packages
  14. import requests
  15. from bs4 import BeautifulSoup
  16. from torf import Torrent
  17. from tqdm import tqdm
  18. from langdetect import detect
  19. from pymediainfo import MediaInfo
  20. from pathlib import Path
  21. # JPS-AU files
  22. import jpspy
  23. def asciiart ():
  24. print("""
  25. ██╗██████╗ ███████╗ █████╗ ██╗ ██╗ ████████╗██╗ ██╗
  26. ██║██╔══██╗██╔════╝ ██╔══██╗██║ ██║ ╚══██╔══╝██║ ██║
  27. ██║██████╔╝███████╗█████╗███████║██║ ██║█████╗██║ ██║ ██║
  28. ██ ██║██╔═══╝ ╚════██║╚════╝██╔══██║██║ ██║╚════╝██║ ╚██╗ ██╔╝
  29. ╚█████╔╝██║ ███████║ ██║ ██║╚██████╔╝ ██║ ╚████╔╝
  30. ╚════╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝
  31. """)
  32. # Get arguments using argparse
  33. def getargs():
  34. """
  35. Get arguments using argparse
  36. """
  37. parser = argparse.ArgumentParser()
  38. parser.add_argument('-i', '--input', help='Initiate upload on input file', nargs='?', required=True)
  39. parser.add_argument('-d', '--debug', help='Enable debug mode.', action='store_true')
  40. parser.add_argument("-dry", "--dryrun", help="Dryrun will carry out all actions other than the actual upload to SM.", action="store_true")
  41. parser.add_argument("-rt", "--releasetype", help='Set the release type. (PV, Music Performance, TV Music, TV Variety, TV-Drama)', nargs='?')
  42. parser.add_argument("-a", "--artist", help='Set the artist. (Romaji\English). Split multiple with ","', nargs='?')
  43. parser.add_argument("-oa", "--originalartist", help='Set the artist. (Original Language)', nargs='?')
  44. parser.add_argument("-ti", "--title", help='Set the title. (Romaji\English)', nargs='?')
  45. parser.add_argument("-oti", "--originaltitle", help='Set the title. (Original Language)', nargs='?')
  46. parser.add_argument("-t", "--tags", help="Add additional tags to the upload. At least 2 tags are required", nargs='?')
  47. parser.add_argument("-y", "--year", help='Set the torrent year (YYYYMMDD or YYYY).', nargs='?')
  48. parser.add_argument("-fl", "--freeleech", help="Enables freeleech.", action="store_true")
  49. parser.add_argument("-eti", "--editiontitle", help='Set the edition title', nargs='?')
  50. parser.add_argument("-ey", "--editionyear", help='Set the torrent edition year (YYYYMMDD or YYYY).', nargs='?')
  51. parser.add_argument("-ms", "--mediasource", help='Set the media source. (HDTV, Web)', nargs='?')
  52. parser.add_argument("-im", "--imagepath", help='Set the torrent cover', nargs='?')
  53. parser.add_argument("-tdes", "--torrentdescription", help='Add a torrent description', nargs='?')
  54. parser.add_argument("-tgdes", "--torrentgroupdescription", help='Add a torrent group description. This is a required argument.', nargs='?', required=True)
  55. parser.add_argument("-f", "--formattype", help='Set the media source. (MPEG, MPEG2, AVI, MKV, MP4, h264)', nargs='?')
  56. return parser.parse_args()
  57. # Acquire the authkey used for torrent files from upload.php
  58. def getauthkey():
  59. uploadpage = j.retrieveContent("https://jpopsuki.eu/upload.php")
  60. soup = BeautifulSoup(uploadpage.text, 'html5lib')
  61. rel2 = str(soup.select('#wrapper #content .thin'))
  62. # Regex returns multiple matches, could be optimized.
  63. authkey = re.findall("(?<=value=\")(.*)(?=\")", rel2)[0]
  64. return authkey
  65. def copytree(src, dst, symlinks=False, ignore=None):
  66. for item in os.listdir(src):
  67. s = os.path.join(src, item)
  68. d = os.path.join(dst, item)
  69. if os.path.isdir(s):
  70. shutil.copytree(s, d, symlinks, ignore)
  71. else:
  72. shutil.copy2(s, d)
  73. # Creates torrent file using torf module.
  74. def createtorrent(authkey, filepath, releasedata):
  75. t = Torrent(path=filepath,
  76. trackers=[authkey]) # Torf requires we store authkeys in a list object. This makes it easier to add multiple announce urls.
  77. # Set torrent to private as standard practice for private trackers
  78. t.private = True
  79. t.generate()
  80. ## Format releasedata to bring a suitable torrent name.
  81. # The reason we don't just use the directory name is because of an error in POSTING.
  82. # POSTS do not seem to POST hangul/jp characters alongside files.
  83. filename = f"{releasedata['title']} [{releasedata['media']}-{releasedata['format']}].torrent"
  84. filename = filename.replace("/","/").replace(":",":").replace("?","?").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. def add_to_hangul_dict(hangul , english , category):
  99. hangul = str(hangul)
  100. english = str(english)
  101. categories = ['version','general','artist','genres', 'label', 'distr']
  102. file = f"json_data/dictionary.json"
  103. json_file = open(file, 'r', encoding='utf-8', errors='ignore')
  104. dictionary = json.load(json_file)
  105. json_file.close()
  106. new = dict()
  107. for cats in dictionary:
  108. #== Create the categories in the new temp file
  109. new[cats] = dict()
  110. for key,value in dictionary[cats].items():
  111. #== List all the old items into the new dict
  112. new[cats][key] = value
  113. if hangul in new[category].keys():
  114. if new[category].get(hangul) is None:
  115. if english != 'None':
  116. new[category][hangul] = english
  117. else:
  118. #== Only update if English word has been supplied ==#
  119. if english != 'None':
  120. new[category][hangul] = english
  121. else:
  122. if english == 'None':
  123. new[category][hangul] = None
  124. else:
  125. new[category][hangul] = english
  126. json_write = open(file, 'w+', encoding='utf-8')
  127. json_write.write(json.dumps(new, indent=4, ensure_ascii=False))
  128. json_write.close()
  129. def translate(string, category, result=None, output=None):
  130. file = "json_data/dictionary.json"
  131. with open(file, encoding='utf-8', errors='ignore') as f:
  132. dictionary = json.load(f, strict=False)
  133. category = str(category)
  134. string = str(string)
  135. search = dictionary[category]
  136. string = string.strip()
  137. if string == 'Various Artists':
  138. output = ['Various Artists',None]
  139. else:
  140. #== NO NEED TO SEARCH - STRING HAS HANGUL+ENGLISH or HANGUL+HANGUL ==#
  141. if re.search("\((?P<inside>.*)\)", string):
  142. #== Complete translation, add to dictionary with both values ==#
  143. #== Contains parentheses, need to split
  144. parenthesis = string.split("(")
  145. pre_parenthesis = parenthesis[0].strip()
  146. in_parenthesis = parenthesis[1].replace(")","").strip()
  147. #== Check the order of the parentheses ==#
  148. if re.search("[^\u0000-\u007F]+",pre_parenthesis) and re.search("[^\u0000-\u007F]+",in_parenthesis):
  149. #== Both hangul
  150. first = 'kr'
  151. second = 'kr'
  152. else:
  153. if re.search("[^\u0000-\u007F]+",pre_parenthesis):
  154. first = 'kr'
  155. second = 'eng'
  156. else:
  157. first = 'eng'
  158. second = 'kr'
  159. if first == 'kr' and second == 'eng':
  160. #== Hangul first ==#
  161. hangul = pre_parenthesis
  162. english = in_parenthesis
  163. add_to_hangul_dict(hangul,english,category)
  164. elif first == 'eng' and second == 'kr':
  165. #== English first ==#
  166. hangul = in_parenthesis
  167. english = pre_parenthesis
  168. add_to_hangul_dict(hangul,english,category)
  169. elif first == 'kr' and second == 'kr':
  170. #== Both Hangul ==#
  171. hangul = pre_parenthesis
  172. english = None
  173. add_to_hangul_dict(pre_parenthesis,None,category)
  174. add_to_hangul_dict(hangul,None,category)
  175. else:
  176. #== Both English
  177. hangul = None
  178. english = pre_parenthesis
  179. output = [hangul,english]
  180. #== No parentheses - HANGUL
  181. else:
  182. #== If the input string is a full Hangul word - check dictionary and then add if necessary)
  183. if re.search("[^\u0000-\u007F]+", string):
  184. if string in search.keys():
  185. #== yes
  186. if search.get(string) is None:
  187. #== If the keyword does not have a translation, add it to the dictionary ==#
  188. output = [string,None]
  189. else:
  190. #== Translation already exists, output the result in a list ==#
  191. output = [string,search.get(string)]
  192. else:
  193. output = [string,None]
  194. add_to_hangul_dict(string, None, category)
  195. #== Full English name -- leave it
  196. else:
  197. for key,value in search.items():
  198. if key == string:
  199. output = [value,string]
  200. break
  201. else:
  202. output = [string,string]
  203. return output
  204. def gatherdata():
  205. """
  206. Retrieve data about the upload. Ask for user input if necessary.
  207. :return: releasedata: dict
  208. """
  209. releasedata = {"submit": "true"}
  210. releasedata["album_desc"] = torrentgroupdescription
  211. if torrentdescription:
  212. releasedata['release_desc'] = torrentdescription
  213. list_of_types = ["PV", "Music Performance", "TV Music", "TV Variety", "TV-Drama"]
  214. if releasetype in list_of_types:
  215. if releasetype == "PV":
  216. releasedata["type"] = "PV"
  217. elif releasetype == "TV Music":
  218. releasedata["type"] = "TV-Music"
  219. elif releasetype == "TV Variety":
  220. releasedata["type"] = "TV-Variety"
  221. elif releasetype == "TV-Drama":
  222. releasedata["type"] = "TV-Drama"
  223. else:
  224. while(True):
  225. input_lang = input("\n" + "_" * 100 + "\nEnter a number to choose the upload type. \n1=PV\n2=TV-Music\n3=TV-Variety\n4=TV-Drama\n")
  226. if input_lang == "1":
  227. releasedata["type"] = "PV"
  228. break
  229. elif input_lang == "2":
  230. releasedata["type"] = "TV-Music"
  231. break
  232. elif input_lang == "3":
  233. releasedata["type"] = "TV-Variety"
  234. break
  235. elif input_lang == "4":
  236. releasedata["type"] = "TV-Drama"
  237. break
  238. print("Invalid choice.")
  239. if artist:
  240. releasedata['artist'] = artist
  241. else:
  242. print(artist)
  243. input_english_artist = input("\n" + "_" * 100 + "\nEnter the romaji/english ARTIST name.\n")
  244. releasedata['artist'] = input_english_artist
  245. if originalartist:
  246. releasedata['artistjp'] = originalartist
  247. else:
  248. input_artist = input("\n" + "_" * 100 + "\nEnter the original ARTIST name. Press enter to skip if this torrent has the artist name already english or already has a artist page.\n")
  249. releasedata['artistjp'] = input_artist
  250. if title:
  251. releasedata['title'] = title
  252. else:
  253. input_english_title = input("\n" + "_" * 100 + "\nEnter the romaji/english TITLE:\n")
  254. releasedata['title'] = input_english_title
  255. if originaltitle:
  256. releasedata['titlejp'] = originaltitle
  257. else:
  258. input_title = input("\n" + "_" * 100 + "\nEnter the original TITLE. Press enter to skip.\n")
  259. releasedata['titlejp'] = input_title
  260. if year:
  261. releasedata["releasedate"] = year
  262. else:
  263. input_year = input("\n" + "_" * 100 + "\nEnter the year as YYYYMMDD or YYYY.\n")
  264. releasedata["releasedate"] = input_year
  265. if editiontitle:
  266. releasedata['remaster_title'] = editiontitle
  267. else:
  268. input_editiontitle = input("\n" + "_" * 100 + "\nEnter the edition TITLE. Press enter to skip.\n")
  269. if input_editiontitle != "":
  270. if editionyear:
  271. releasedata["remaster_year"] = editionyear
  272. else:
  273. input_editionyear = input("\n" + "_" * 100 + "\nEnter the edition year as YYYY.\n")
  274. releasedata["remaster_year"] = input_editionyear
  275. releasedata['remaster_title'] = input_editiontitle
  276. if formattype:
  277. releasedata['format'] = formattype
  278. else:
  279. while(True):
  280. input_format = input("\n" + "_" * 100 + "\nEnter a number to choose the format. \n1=MPEG\n2=MPEG2\n3=AVI\n4=MKV\n5=MP4\n6=h264\n")
  281. if input_format == "1":
  282. releasedata["format"] = "MPEG"
  283. break
  284. elif input_format == "2":
  285. releasedata["format"] = "MPEG2"
  286. break
  287. elif input_format == "3":
  288. releasedata["format"] = "AVI"
  289. break
  290. elif input_format == "4":
  291. releasedata["format"] = "MKV"
  292. break
  293. elif input_format == "5":
  294. releasedata["format"] = "MP4"
  295. break
  296. elif input_format == "6":
  297. releasedata["format"] = "h264"
  298. break
  299. print("Invalid choice.")
  300. if mediasource:
  301. releasedata['media'] = mediasource
  302. else:
  303. while(True):
  304. input_media = input("\n" + "_" * 100 + "\nEnter a number to choose the media source. \n1=HDTV\n2=Web\n")
  305. if input_media == "1":
  306. releasedata["media"] = "HDTV"
  307. break
  308. elif input_media == "2":
  309. releasedata["media"] = "Web"
  310. break
  311. print("Invalid choice.")
  312. if tags:
  313. releasedata["tags"] = tags
  314. else:
  315. while(True):
  316. input_tags = input("\n" + "_" * 100 + "\nEnter the tags. Separate multiple with \",\". Minimum 1 tag required.\n")
  317. if len(input_tags.split(",")) != 0:
  318. releasedata["tags"] = input_tags
  319. break
  320. else:
  321. print("Please enter at least one tag.")
  322. return releasedata
  323. # MediaInfo.parse doesnt work properly right now. it has duplicate lines
  324. def add_mediainfo_to_releasedata(filename, releasedata):
  325. """
  326. Retrieve mediainfo and append it to the releasedata dictionary.
  327. :return: releasedata: dict
  328. """
  329. mediainfosall = ""
  330. mediainfosall += str(MediaInfo.parse(filename, output="text"))
  331. replacement = str(Path(filename).parent)
  332. mediainfosall = mediainfosall.replace(replacement, '')
  333. #releasedata["release_desc"] += "\n\n" + mediainfosall
  334. print(mediainfosall)
  335. return releasedata
  336. # Simple function to split a string up into characters
  337. def split(word):
  338. return [char for char in word]
  339. def detectlanguage(string):
  340. ## Language Detect
  341. # This is a required check as we don't want to enter non-english/romaji characters into the title field.
  342. characters = split(string)
  343. language_list = []
  344. for c in characters:
  345. try:
  346. language = detect(c)
  347. language_list.append(language)
  348. except:
  349. langauge = "error"
  350. if 'ko' or 'ja' in language_list:
  351. en = False
  352. else:
  353. en = True
  354. return en
  355. def uploadtorrent(torrent, cover, releasedata):
  356. # POST url.
  357. uploadurl = "https://jpopsuki.eu/upload.php"
  358. # Dataset containing all of the information obtained from our FLAC files.
  359. data = releasedata
  360. if debug:
  361. print('_' * 100)
  362. print('Release Data:\n')
  363. print(releasedata)
  364. try:
  365. postDataFiles = {
  366. 'file_input': open(torrent, 'rb'),
  367. 'userfile': open(cover, 'rb')
  368. }
  369. except FileNotFoundError:
  370. print("_" * 100)
  371. print('File not found!\nPlease confirm file locations and names. Cover image or .torrent file could not be found')
  372. sys.exit()
  373. # If dryrun argument has not ben passed we will POST the results to JPopSuki.
  374. if dryrun != True:
  375. JPSres = j.retrieveContent(uploadurl, "post", data, postDataFiles)
  376. print('\nUpload POSTED')
  377. ## TODO Filter through JPSres.text and create error handling based on responses
  378. #print(JPSres.text)
  379. # Function for transferring the contents of the torrent as well as the torrent.
  380. def ftp_transfer(fileSource, fileDestination, directory, folder_name, watch_folder):
  381. # Create session
  382. session = ftplib.FTP(cfg['ftp_prefs']['ftp_server'],cfg['ftp_prefs']['ftp_username'],cfg['ftp_prefs']['ftp_password'])
  383. # Set session encoding to utf-8 so we can properly handle hangul/other special characters
  384. session.encoding='utf-8'
  385. # Successful FTP Login Print
  386. print("_" * 100)
  387. print("FTP Login Successful")
  388. print(f"Server Name: {cfg['ftp_prefs']['ftp_server']} : Username: {cfg['ftp_prefs']['ftp_username']}\n")
  389. if cfg['ftp_prefs']['add_to_downloads_folder']:
  390. # Create folder based on the directory name of the folder within the torrent.
  391. try:
  392. session.mkd(f"{fileDestination}/{folder_name}")
  393. print(f'Created directory {fileDestination}/{folder_name}')
  394. except ftplib.error_perm:
  395. pass
  396. # Notify user we are beginning the transfer.
  397. print(f"Beginning transfer...")
  398. # Set current folder to the users preferred destination
  399. session.cwd(f"{fileDestination}/{folder_name}")
  400. # Transfer each file in the chosen directory
  401. for file in os.listdir(directory):
  402. with open(f"{directory}/{file}",'rb') as f:
  403. filesize = os.path.getsize(f"{directory}/{file}")
  404. ## Transfer file
  405. # tqdm used for better user feedback.
  406. with tqdm(unit = 'blocks', unit_scale = True, leave = False, miniters = 1, desc = f'Uploading [{file}]', total = filesize) as tqdm_instance:
  407. session.storbinary('STOR ' + file, f, 2048, callback = lambda sent: tqdm_instance.update(len(sent)))
  408. print(f"{file} | Complete!")
  409. f.close()
  410. if cfg['ftp_prefs']['add_to_watch_folder']:
  411. with open(fileSource,'rb') as t:
  412. # Set current folder to watch directory
  413. session.cwd(watch_folder)
  414. ## Transfer file
  415. # We avoid tqdm here due to the filesize of torrent files.
  416. # Most connections will upload these within 1-3s, resulting in near useless progress bars.
  417. session.storbinary(f"STOR {torrentfile}", t)
  418. print(f"{torrentfile} | Sent to watch folder!")
  419. t.close()
  420. # Quit session when complete.
  421. session.quit()
  422. def localfileorganization(torrent, directory, watch_folder, downloads_folder):
  423. # Move torrent directory to downloads_folder
  424. if cfg['local_prefs']['add_to_downloads_folder']:
  425. try:
  426. os.mkdir(os.path.join(downloads_folder, os.path.basename(directory)))
  427. except FileExistsError:
  428. pass
  429. copytree(directory, os.path.join(downloads_folder, os.path.basename(directory)))
  430. shutil.rmtree(directory)
  431. if cfg['local_prefs']['add_to_watch_folder']:
  432. os.rename(torrent, f"{watch_folder}/{torrent}")
  433. if __name__ == "__main__":
  434. asciiart()
  435. args = getargs()
  436. # TODO consider calling args[] directly, we will then not need this line
  437. dryrun = debug = tags = artist = title = formattype = imagepath = freeleech = None
  438. originalartist = originaltitle = torrentdescription = torrentgroupdescription = editiontitle = editionyear = year = mediasource = releasetype = None
  439. inputfile = args.input
  440. torrentgroupdescription = args.torrentgroupdescription
  441. torrentdescription = args.torrentdescription
  442. if args.dryrun:
  443. dryrun = True
  444. if args.debug:
  445. debug = True
  446. if args.freeleech:
  447. freeleech = True
  448. if args.releasetype:
  449. releasetype = args.releasetype
  450. if args.title:
  451. title = args.title
  452. if args.artist:
  453. artist = args.artist
  454. if args.originalartist:
  455. originalartist = args.originalartist
  456. if args.originaltitle:
  457. originaltitle = args.originaltitle
  458. if args.editiontitle:
  459. editiontitle = args.editiontitle
  460. if args.year:
  461. year = args.year
  462. if args.editionyear:
  463. editionyear = args.editionyear
  464. if args.mediasource:
  465. mediasource = args.mediasource
  466. if args.formattype:
  467. format_type = args.formattype
  468. if args.tags:
  469. tags = args.tags
  470. if args.imagepath:
  471. imagepath = args.imagepath
  472. # Load login credentials from JSON and use them to create a login session.
  473. with open(f'json_data/config.json') as f:
  474. cfg = json.load(f)
  475. loginData = {'username': cfg['credentials']['username'], 'password': cfg['credentials']['password']}
  476. loginUrl = "https://jpopsuki.eu/login.php"
  477. loginTestUrl = "https://jpopsuki.eu"
  478. successStr = "Latest 5 Torrents"
  479. # j is an object which can be used to make requests with respect to the loginsession
  480. j = jpspy.MyLoginSession(loginUrl, loginData, loginTestUrl, successStr, debug=args.debug)
  481. # Acquire authkey
  482. authkey = getauthkey()
  483. # Gather data of the file
  484. releasedata = gatherdata()
  485. # releasedata_and_mediainfo = add_mediainfo_to_releasedata(inputfile, releasedata)
  486. # Folder_name equals the last folder in the path, this is used to rename .torrent files to something relevant.
  487. # folder_name = os.path.basename(os.path.normpath(directory))
  488. # Identifying cover.jpg path
  489. # cover_path = directory + "/" + cfg['local_prefs']['cover_name']
  490. # Create torrent file.
  491. torrentfile = createtorrent(authkey, inputfile, releasedata)
  492. # Upload torrent to JPopSuki
  493. uploadtorrent(torrentfile, imagepath, releasedata)
  494. # Setting variable for watch/download folders
  495. ftp_watch_folder = cfg['ftp_prefs']['ftp_watch_folder']
  496. ftp_downloads_folder = cfg['ftp_prefs']['ftp_downloads_folder']
  497. local_watch_folder = cfg['local_prefs']['local_watch_folder']
  498. local_downloads_folder = cfg['local_prefs']['local_downloads_folder']
  499. # if cfg['ftp_prefs']['enable_ftp']:
  500. # ftp_transfer(fileSource=torrentfile, fileDestination=ftp_downloads_folder, directory=directory, folder_name=folder_name, watch_folder=ftp_watch_folder)
  501. # if cfg['local_prefs']['add_to_watch_folder'] or cfg['local_prefs']['add_to_downloads_folder']:
  502. # localfileorganization(torrent=torrentfile, directory=directory, watch_folder=local_watch_folder, downloads_folder=local_downloads_folder)
  503. if not dryrun:
  504. if cfg['local_prefs']['add_to_watch_folder']:
  505. os.rename(torrentfile, f"{local_watch_folder}/{torrentfile}")