Advertisement
Guest User

Untitled

a guest
Aug 23rd, 2016
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 30.34 KB | None | 0 0
  1. """==================================================================================
  2. conv2mp4-py - https://github.com/Kameecoding/conv2mp4-py
  3.  
  4. Python 2.7 script that recursively searches through a defined file path and converts MKV, AVI, FLV,
  5. and MPEG files to MP4 using handbrake (with AAC audio). It then refreshes a Plex library, and deletes
  6. the source file upon success and moves the new files into the defines directory (Plex Library directories
  7. for example). Fails over to ffmpeg encode if conversion failure is detected. The purpose of this script
  8. is to reduce the number of transcodes performed by a Plex server, by processing all files into an MP4 format.
  9. =====================================================================================
  10.  
  11. This script requires FFMPEG and Handbrake. You can download them at the URLs below.
  12. FFMPEG : https://FFMPEG.org/download.html
  13. HANDBRAKEcli : https://HANDBRAKE.fr/downloads.php
  14.  
  15. -------------------------------------------------------------------------------------
  16. User-specific variables
  17. -------------------------------------------------------------------------------------
  18. There are several user-defined variables you will need to edit using notepad or a program like Notepad++.
  19.  
  20. NOTE: to use a mapped drive, you must run net use z: \server\share /persistent:yes as the user you're going to run the script as (generally Administrator) prior to running the script.
  21.  
  22. FFMPEG = path to ffmpeg.exe
  23. HANDBRAKE = path to HandBrakeCLI.exe
  24. FILEBOT = path to FileBot.exe
  25.  
  26. ENABLE_PLEX_UPDATE Set to True to enable the script to update your server
  27. NOTE: Requires PLEX_IP and PLEX_TOKEN to work.
  28. PLEX_IP = the IP address and port of your Plex server (for the purpose of refreshing its library)
  29. PLEX_TOKEN = your Plex server's token (for the purpose of refreshing its library).
  30. NOTE: See https://support.plex.tv/hc/en-us/articles/204059436-Finding-your-account-token-X-Plex-Token for instructions on retrieving your Plex server's token. Your Plex server's token is also easy to retrieve with Couchpotato or SickRage.
  31.  
  32. MAX_CONVERT_ITEMS = Maximum number of files to convert in one go
  33. LOCALE = Three digit code for your language for example for Hungarian it's 'hun'
  34.  
  35. Target Folders for converted files
  36. MOVIE_TARGET = Path where you want your movies
  37. TVSHOW_TARGET = Path where you want your TV SHows
  38. LANG_MOVIE_TARGET = Path where you want your foreign language movies
  39. LANG_TVSHOW_TARGET = Path where you want your foreign langue Tv Shows
  40.  
  41. FOREIGN = Set to true if you want the script to take foreign language into account
  42. PREFER_METADATA = The Script tries to match the description and the metadata of the subtitles in the file, you can try turning it on and off to see if the subtitles were matched better
  43. EXTRACT_SRT = Whether to extract the subtitles from files into external SRTs (External SRT files don't trigger transcoding, while embedded might)
  44.  
  45. MOVE_FILES = Whether to allow to move the files to new directories or keep them in the same
  46. CREATE_MOVIE_DIRS = Whether to create a directory for each movie file: e.g. it will put the movie Pacific Rim and all it's subtitles to the folder [MOVIE_TARGET or LANG_MOVIE_TARGET]\Pacific Rim (2013)\
  47. CREATE_TVSHOW_DIRS = Whether to create a directory for each TV Show: e.g. it will put the tv show Modern Family and all it's episodes and subtitles to the folder [TVSHOW_TARGET or LANG_TVSHOW_TARGET]\Modern Family\
  48. CREATE_SEASON_DIRS = Used together with CREATE_TVSHOW_DIRS, also adds a Season folder. e.g. first Season of Modern Family will get put into: [TVSHOW_TARGET or LANG_TVSHOW_TARGET]\Modern Family\Season 01\
  49. NOTE: Only Works with CREATE_TVSHOW_DIRS set to True
  50. REMOVE_OLD = Whether to remove the files that have been already converted and are no longer needed
  51. HARD_LINK = Whether to create hardlink between folders, for example if you have a movie with both English and Foreign audio and you want plex to detect it in both the English and Foreign Library the easiest way is to Hard link the directories
  52. NOTE: Only Works with FOREIGN and MOVE_FILES set to True"""
  53.  
  54. import os
  55. import sys
  56. import time
  57. import platform
  58. import subprocess
  59. import urllib2
  60. import logging
  61. import re
  62. import shutil
  63. import ntfsutils.junction
  64.  
  65. """----------------------------------------------------------------------------------
  66. Global Constants controlling the script
  67. ----------------------------------------------------------------------------------"""
  68.  
  69. IS_WINDOWS = any(platform.win32_ver())
  70.  
  71.  
  72. #CONTROL THE SCRIPT WITH THESE VARIAB:ES
  73. FOREIGN = False
  74. PREFER_METADATA = True
  75. CREATE_MOVIE_DIRS = False
  76. CREATE_TVSHOW_DIRS = False
  77. CREATE_SEASON_DIRS = False
  78. MOVE_FILES = False
  79. EXTRACT_SRT = False
  80. REMOVE_CONVERTED = False
  81. HARD_LINK = False
  82. DEV_NULL = open(os.devnull,'w')
  83.  
  84.  
  85. # File types to convert
  86. videos = ('.mkv', '.avi', '.flv', '.mpeg', '.mp4')
  87. subtitles = ('.srt','.ass')
  88.  
  89.  
  90. """ Edit the following values to your liking, pay special attention to the media_path, PLEX_IP and PLEX_TOKEN values """
  91. # Plex Server Token - See URL below inorder to obtain your Token
  92. # https://support.plex.tv/hc/en-us/articles/204059436-Finding-your-account-token-X-Plex-Token
  93. ENABLE_PLEX_UPDATE = True
  94. PLEX_IP = '192.168.1.200:32400' #Typically '127.0.0.1:32400'
  95. PLEX_TOKEN = [REDACTED]' # See https://support.plex.tv/hc/en-us/articles/204059436-Finding-your-account-token-X-Plex-Token
  96. # Max media files to convert
  97. MAX_CONVERT_ITEMS = 2
  98. LOCALE = 'eng' #Three digit code for your language for example for Hungarian it's 'hun'
  99.  
  100. # Paths to FFMPEG, HANDBRAKE-cli and your log file
  101. # If you need help finding your install points in Linux, try 'which FFMPEG' and 'which HANDBRAKE'
  102. # Make sure you install the following on your platform, FFMPEG, HANDBRAKE AND HANDBRAKE-cli
  103. FFMPEG = 'C:\\FFMPEG\\bin\\FFMPEG.exe' #'/usr/bin/FFMPEG'
  104. HANDBRAKE = "C:\Program Files\HandBrake\HandBrakeCLI.exe" #'/usr/bin/HandBrakeCLI'
  105. FILEBOT = 'C:\Program Files\FileBot\\FILEBOT.exe'
  106.  
  107. MOVIE_TARGET = '' #Example: "F:\Media\Movies"
  108. LANG_MOVIE_TARGET = '' # Example: "F:\Media\Filmek"
  109. TVSHOW_TARGET = '' #Example F:\Media\TV Shows'
  110. LANG_TVSHOW_TARGET = '' #Example "F:\Media\Sorozatok"
  111.  
  112. #Pattern Constants - DO NOT EDIT THESE
  113. TV_SHOW_PATTERNS = [".s([0-9]+).",".([0-9]+)x[0-9][0-9]."]
  114. SUB_PATTERN = 'Stream #[0-9]:([0-9])\(([a-z]{3})\): Subtitle: [a-z]{3,7}[ ]*[\(]*([a-z]*)[\) \(]*([a-z]*)[\)\W]*Metadata:[\W]*title[ ]*: ([a-z]*)'
  115. SUB_PATTERN2 = 'Stream #[0-9]:([0-9])\(([a-z]{3})\): Subtitle: [a-z]{3,7}[ ]*[\(]*([a-z]*)[\) \(]*([a-z]*)[\)\W]*'
  116. AUDIO_PATTERN = 'Stream #[0-9]:[0-9]\(([a-z]{3})\):[\W]*Audio'
  117. LANG_PATTERN = ".{pattern}.".format(pattern=LOCALE)
  118.  
  119. #COUNTRY TUPLE FOR translating matched METADATA patterns
  120. COUNTRY_TUPLE = {
  121. 'nagyar' : 'hun',
  122. 'hungarian' : 'hun',
  123. 'angol' : 'eng',
  124. 'english' : 'eng',
  125. 'forced' : 'forced',
  126. 'dutch' : 'dut',
  127. 'french' : 'fre',
  128. 'german' : 'ger',
  129. 'italian' : 'ita',
  130. }
  131.  
  132. # Put log file here
  133. if IS_WINDOWS:
  134. LOG_FILE = os.path.expanduser('~/Desktop/simple_convert2.log')
  135. else:
  136. LOG_FILE = os.path.expanduser('~/simple_convert.log')
  137.  
  138. """ Don't change the following unless you know what you are doing!! """
  139.  
  140. """ Set up the Logger """
  141. logging.basicConfig(filename=LOG_FILE, level=logging.INFO)
  142.  
  143. console = logging.StreamHandler()
  144. console.setLevel(logging.INFO)
  145. formatter = logging.Formatter('%(asctime)s: %(name)-12s - %(levelname)-8s %(message)s')
  146. console.setFormatter(formatter)
  147. logging.getLogger('').addHandler(console)
  148. Logger = logging.getLogger('simple_convert')
  149.  
  150.  
  151. """----------------------------------------------------------------------------------
  152. Rename Files using Filebot,
  153. looks for patterns in the name to determine if it's a TV Show or a Movie
  154. ----------------------------------------------------------------------------------"""
  155. def rename_files(media_path):
  156. #TODO keep suffixes
  157. file_list = find_media_files(media_path)
  158.  
  159. for file in file_list:
  160. #skip already renamed files
  161. if re.search('.[a-z\W]+[0-9]+x[0-9]{2} - [a-z\W]+.',os.path.basename(file),re.I):
  162. continue
  163.  
  164. the_db = 'TheMovieDB'
  165. #if it looks like a TV Show change lookup
  166. if is_tvshow(file):
  167. the_db = 'TheTVDB'
  168.  
  169. suffix_match = re.search('\.([a-z]{3})\.',os.path.basename(file),re.I)
  170. forced_match = re.search('.(forced).',file,re.I)
  171. if suffix_match:
  172. suffix = suffix_match.group(1)
  173. is_good_suffix = False
  174. if suffix in COUNTRY_TUPLE.values():
  175. is_good_suffix = True
  176.  
  177. if forced_match:
  178. forced = forced_match.group(1)
  179.  
  180. #rename the files
  181. proc = subprocess.Popen([
  182. FILEBOT,
  183. '-rename', file,
  184. '--db', the_db,
  185. '-non-strict'
  186. ],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
  187.  
  188. output = proc.stdout.read()
  189.  
  190. if output.find('already exists') != -1:
  191. Logger.warning("{filename} wasn't renamed because a file with same name already exists".format(filename=file))
  192. continue
  193. match = re.search('\[MOVE\] Rename \[(.*?)\] to \[(.*?)\]',output,re.I | re.M)
  194.  
  195. output_file = match.group(2)
  196. rename_output = output_file
  197. output_file_extention = output_file[-3:]
  198. output_file = output_file[:-3]
  199.  
  200. if 'suffix' in locals() and is_good_suffix:
  201. output_file += suffix + '.'
  202. if 'forced' in locals():
  203. output_file += forced + '.'
  204. output_file += output_file_extention
  205. shutil.move(rename_output,output_file)
  206. Logger.info("{filename} renamed".format(filename=file))
  207.  
  208. def is_tvshow(filename):
  209. for pattern in TV_SHOW_PATTERNS:
  210. match = re.search(pattern,filename,re.I)
  211. if match:
  212. return True
  213. return False
  214.  
  215. def combine_matches(matches1,matches2):
  216. offset = 132456789
  217. output = []
  218.  
  219. if len(matches1) > 0 and int(matches1[0][0]) < offset:
  220. offset = matches1[0][0]
  221. if len(matches2) > 0 and int(matches2[0][0]) < offset:
  222. offset = matches2[0][0]
  223. while len(matches1) > 0 or len(matches2) > 0:
  224. if len(matches1) == 0:
  225. output.append(matches2[0])
  226. del matches2[0]
  227. elif len(matches2) == 0:
  228. output.append(matches1[0])
  229. del matches1[0]
  230. elif matches1[0][0] < matches2[0][0]:
  231. output.append(matches1[0])
  232. del matches1[0]
  233. elif matches1[0][0] > matches2[0][0]:
  234. output.append(matches2[0])
  235. del matches2[0]
  236. elif len(matches1[0]) > len(matches2[0]):
  237. output.append(matches1[0])
  238. del matches1[0]
  239. del matches2[0]
  240. else:
  241. Logger.info("combine_matches: Unexpected result while parsing subtitles")
  242. #print output
  243. return (offset,output)
  244.  
  245.  
  246.  
  247. def check_path(target_dir):
  248. if not os.path.isdir(target_dir):
  249. os.makedirs(target_dir)
  250.  
  251. def get_locale(prefix,title=''):
  252. temp = ''
  253. if title == '':
  254. temp = ".{prefix}".format(prefix=prefix)
  255. else:
  256. if(PREFER_METADATA):
  257. temp= ".{prefix}".format(prefix=translate_title(prefix,title))
  258. else:
  259. temp = ".{prefix}".format(prefix=prefix)
  260. return temp
  261.  
  262. def translate_title(prefix,title):
  263. title=title.lower()
  264. if PREFER_METADATA:
  265. try:
  266. if prefix != COUNTRY_TUPLE[title]:
  267. if COUNTRY_TUPLE[title] == 'forced':
  268. return prefix + '.forced'
  269. else:
  270. return COUNTRY_TUPLE[title]
  271. except KeyError:
  272. Logger.info("Unknown Country Touple Key: {key}".format(key=title))
  273. return prefix
  274.  
  275.  
  276. """----------------------------------------------------------------------------------
  277. Update Plex Server
  278. ----------------------------------------------------------------------------------"""
  279. def update_plex():
  280. Logger.info("plex - sending request to update Plex")
  281. url = 'http://%s/library/sections/all/refresh?X-Plex-Token=%s' % (PLEX_IP, PLEX_TOKEN)
  282.  
  283. try:
  284. urllib2.urlopen(url).read()
  285. except urllib2.HTTPError, e:
  286. Logger.warning("plex - unable to make request to Plex - HTTP Error %s", str(e.code))
  287. except urllib2.URLError, e:
  288. Logger.warning("plex - unable to make request to Plex - URL Error %s", e.reason)
  289. else:
  290. Logger.info("plex - update successful")
  291.  
  292.  
  293. """ Build a array of files to convert """
  294. def find_media_files(media_path):
  295. unconverted = []
  296.  
  297. #print
  298. #print os.listdir(media_path)
  299.  
  300. #for directories in next(os.walk(media_path))[1]:
  301. # print "Directories:" + directories
  302.  
  303. for dirname, directories, files in os.walk(media_path):
  304. for file in files:
  305. #skip hidden files
  306. if file.startswith('.'):
  307. continue
  308.  
  309. if is_video(file) or is_subtitle(file):
  310. file = os.path.join(dirname, file)
  311. #Skip Sample files
  312. if re.search(".sample.",file,re.I):
  313. continue
  314.  
  315. unconverted.append(file)
  316.  
  317. sorted_unconvered = sorted(unconverted)
  318.  
  319. return sorted_unconvered[:MAX_CONVERT_ITEMS] if MAX_CONVERT_ITEMS else sorted_unconvered
  320.  
  321. def is_subtitle(filename):
  322. if filename.endswith(subtitles):
  323. return True
  324. return False
  325.  
  326. def is_video(filename):
  327. if filename.endswith(videos):
  328. return True
  329. return False
  330.  
  331.  
  332.  
  333. def get_length(file):
  334. #Get file info and Parse it
  335. proc =subprocess.Popen([
  336. FFMPEG,
  337. '-i', file,
  338. ],stdout=None,stderr=subprocess.PIPE)
  339. output = proc.stderr.read()
  340.  
  341. pattern = '.Duration: ([0-9]{2}):([0-9]{2}):([0-9]{2}).'
  342. match = re.search(pattern,output,re.I | re.M)
  343.  
  344. if not match:
  345. Logger.info("ERROR: Failed to assert duration")
  346. sys.exit(0)
  347. return [int(match.group(1)),int(match.group(2)),int(match.group(3))]
  348.  
  349.  
  350. def good_output(oldFile,new_file):
  351. oldSize = get_length(oldFile)
  352. newSize = get_length(new_file)
  353.  
  354. for i in range(0,2):
  355. if i == 2:
  356. if abs(oldSize[i]- newSize[i]) > 5:
  357. Logger.info("ERROR: File Duration difference bigger than 5 seconds, convert failed")
  358. return False
  359. else:
  360. if oldSize[i] != newSize [i]:
  361. Logger.info("ERROR: File Duration difference bigger than 5 seconds, convert failed")
  362. return False
  363. Logger.info("SUCCESS: File Duration difference less than 5 seconds, convert successful")
  364. return True
  365.  
  366.  
  367. """ Main Application """
  368. def main(argv):
  369.  
  370. if len(argv) == 1:
  371. path, binary = os.path.split(argv[0])
  372. print "Usage: {} [directory ...]".format(binary)
  373. sys.exit(0)
  374.  
  375. global media_path
  376. media_path = argv[1]
  377.  
  378. if not os.path.exists(media_path):
  379. Logger.error("Unable to find directory: {path}".format(path=media_path))
  380. sys.exit(0)
  381.  
  382.  
  383. #Rename Files Using Filebot
  384. rename_files(media_path)
  385.  
  386. #Find Media files to convert
  387. MediaFile.files = find_media_files(media_path)
  388. media_files = []
  389. #Create file objects
  390. while MediaFile.files:
  391. media_files.append(MediaFile())
  392.  
  393. Logger.info("%d total files to convert", len(media_files))
  394.  
  395. i = 1
  396. for file in media_files:
  397.  
  398. if i > MAX_CONVERT_ITEMS:
  399. break
  400.  
  401. Logger.info("converting %d of %d items", i, len(media_files) if len(media_files) < MAX_CONVERT_ITEMS else MAX_CONVERT_ITEMS)
  402.  
  403. try:
  404. file.execute()
  405. except KeyboardInterrupt:
  406. Logger.info("KeyBoardInterrupt Detected, Exiting")
  407. sys.exit(1)
  408.  
  409. # Update Plex
  410. if ENABLE_PLEX_UPDATE == True:
  411. update_plex()
  412. # Keep a counter of item processed
  413. i = i + 1
  414.  
  415.  
  416.  
  417. class MediaFile:
  418.  
  419. files = []
  420.  
  421. def __init__(self):
  422. #Initialize attributes
  423. self.input_video = ''
  424. self.external_subtitles = []
  425. self.hard_link = ''
  426. self.target_dir = ''
  427. self.error = 0
  428.  
  429. self.find_matches()
  430. self.is_show = is_tvshow(self.input_video)
  431. self.add_targets()
  432. self.append_folder()
  433. self.output_video = os.path.join(
  434. self.target_dir,
  435. os.path.basename(self.input_video)[:-3] + 'mp4')
  436.  
  437. """----------------------------------------------------------------------------------
  438. Execute Converting of the file
  439. ----------------------------------------------------------------------------------"""
  440. def execute(self):
  441. check_path(self.target_dir)
  442.  
  443. if EXTRACT_SRT:
  444. self.extract_srt()
  445. if MOVE_FILES:
  446. self.move_external_subs()
  447. self.create_hard_link()
  448. try:
  449. self.handbrake_convert()
  450. except KeyboardInterrupt:
  451. Logger.info("KeyBoardInterrupt Detected, Cleaning up and Exiting")
  452. self.remove_media_file(self.output_video)
  453. sys.exit(0)
  454. if REMOVE_CONVERTED:
  455. self.remove_media_file(self.input_video)
  456. self.remove_folder(os.path.dirname(self.input_video))
  457.  
  458. """----------------------------------------------------------------------------------
  459. Create hard links from english to foreign directories
  460. ----------------------------------------------------------------------------------"""
  461. def create_hard_link(self):
  462. if self.hard_link:
  463. if not os.path.isdir(self.hard_link):
  464. Logger.info("Hardlinking {source} and {target}".format(source=self.target_dir,target=self.target_dir))
  465. check_path(os.path.dirname(self.hard_link))
  466. if IS_WINDOWS:
  467. ntfsutils.junction.create(self.target_dir,self.hard_link)
  468. else:
  469. os.link(self.target_dir,self.hard_link)
  470. else:
  471. Logger.warning("Can't hardlink {source} and {target}, {target} already exists".format(source=self.target_dir,target=self.target_dir))
  472.  
  473.  
  474. """----------------------------------------------------------------------------------
  475. Find Video files and matching external subtitles
  476. ----------------------------------------------------------------------------------"""
  477. def find_matches(self):
  478. self.input_video = ''
  479. for file in self.files:
  480. if is_video(file):
  481. self.input_video = file
  482. break
  483. if not self.input_video:
  484. logging.error("Unable to find video files, exiting")
  485. sys.exit(1)
  486. self.files.remove(self.input_video)
  487.  
  488. self.external_subtitles = []
  489. for file in self.files:
  490. match = os.path.commonprefix([self.input_video,file])
  491. if len(match) == len(self.input_video[:-3]):
  492. self.external_subtitles.append(file)
  493. self.files.remove(file)
  494.  
  495. for file in self.files:
  496. compare = [os.path.basename(self.input_video),os.path.basename(file)]
  497.  
  498. if os.path.commonprefix(compare) == os.path.basename(self.input_video[:-3]):
  499. if is_subtitle(file):
  500. self.external_subtitles.append(file)
  501. self.files.remove(file)
  502.  
  503. """----------------------------------------------------------------------------------
  504. Add target directory and hardlink if enabled
  505. ----------------------------------------------------------------------------------"""
  506. def add_targets(self):
  507.  
  508. self.target_dir = os.path.dirname(self.input_video)
  509. self.hard_link = ''
  510.  
  511. if MOVE_FILES:
  512. #If we care about foreign languages execute this part
  513. if FOREIGN:
  514. audiostreams = self.get_audio_streams()
  515. #if we want to create hard links and there is both english and locale audio stream in the file or in the name
  516. if HARD_LINK and ((LOCALE in audiostreams and 'eng' in audiostreams) or (re.search('.{}.'.format(LOCALE),self.input_video,re.I) and re.search('.eng.',self.input_video,re.I))):
  517. self.target_dir = TVSHOW_TARGET if self.is_show else MOVIE_TARGET
  518. self.hard_link = LANG_TVSHOW_TARGET if self.is_show else LANG_MOVIE_TARGET
  519. else:
  520. #If the the input is matches LOCALE put it in the lang folders
  521. if re.search(LANG_PATTERN,self.input_video,re.I | re.M):
  522. self.target_dir = LANG_TVSHOW_TARGET if self.is_show else LANG_MOVIE_TARGET
  523. #Else put them in the main folder
  524. else:
  525. self.target_dir = TVSHOW_TARGET if self.is_show else MOVIE_TARGET
  526. #if we don't give a shit about multiple languages simply determine if tvshow or movie
  527. else:
  528. self.target_dir = TVSHOW_TARGET if self.is_show else MOVIE_TARGET
  529.  
  530.  
  531. """----------------------------------------------------------------------------------
  532. Get Audio streams from the file
  533. ----------------------------------------------------------------------------------"""
  534. def get_audio_streams(self):
  535. #Get file info and Parse it
  536. proc =subprocess.Popen([
  537. FFMPEG,
  538. '-i', self.input_video,
  539. ],stdout=None,stderr=subprocess.PIPE)
  540. output = proc.stderr.read()
  541. return re.findall(AUDIO_PATTERN,output,re.I | re.M)
  542.  
  543. """----------------------------------------------------------------------------------
  544. Add /Season XX or /MovieName (Year) to target dir
  545. ----------------------------------------------------------------------------------"""
  546. def append_folder(self):
  547.  
  548. if (CREATE_TVSHOW_DIRS and self.is_show):
  549. sub_folder=os.path.basename(self.input_video)[:new_file.find('-')-1]
  550. if CREATE_SEASON_DIRS:
  551. match = re.search(TV_SHOW_PATTERNS[0],self.input_video,re.I)
  552. if match:
  553. season = match.group(1)
  554. else:
  555. match = re.search(TV_SHOW_PATTERNS[1],self.input_video,re.I)
  556. if match:
  557. season = match.group(1)
  558. if 'season' in locals():
  559. if len(season) == 1:
  560. season = ' 0' + season
  561. else:
  562. season = ' ' + season
  563. else:
  564. Logger.info('Failed to match season pattern in {new}'.format(new=self.input_video))
  565. sys.exit(0)
  566. sub_folder = os.path.join(sub_folder,'Season' + season)
  567. elif (CREATE_MOVIE_DIRS and not self.is_show):
  568. sub_folder=os.path.basename(self.input_video)[:-4]
  569. if 'sub_folder' in locals():
  570. self.target_dir = os.path.join(self.target_dir,sub_folder)
  571.  
  572. """----------------------------------------------------------------------------------
  573. Convert files found to mp4 using HandBrakeCLI
  574. ----------------------------------------------------------------------------------"""
  575. def handbrake_convert(self):
  576. Logger.info("HANDBRAKECLI - converting %s to %s", self.input_video, self.output_video)
  577.  
  578. try:
  579. proc = subprocess.Popen([
  580. HANDBRAKE,
  581. '-i', self.input_video,
  582. '-o', self.output_video,
  583. '-e', 'x264',
  584. '-q', '20.0',
  585. '-a','1,2,3,4,5,6',
  586. '-E','faac,copy:aac',
  587. '-B', '160,160',
  588. '-6', 'dpl2,none' ,
  589. '-R', 'Auto,Auto',
  590. '-D', '0.0,0.0',
  591. '--optimize',
  592. '--audio-copy-mask','aac'
  593. '--audio-fallback','faac',
  594. '-f', 'mp4',
  595. '--decomb',
  596. '--loose-anamorphic',
  597. '--modulus', '2',
  598. '--cfr','-r','30',
  599. '--markers','-4',
  600. '--x264-preset', 'medium',
  601. '--h264-profile', 'high',
  602. '--h264-level', '4.0',
  603. ], stdout=None,stderr=subprocess.PIPE)
  604. output = proc.stderr.read()
  605. while "encoding" in output:
  606. print output
  607.  
  608. if output.find("Encode done!") == -1:
  609. self.error = 1
  610.  
  611. # If the return code is 1 that means HANDBRAKECLI failed
  612. if self.error == 1:
  613. Logger.warning("HANDBRAKECLI - failure converting %s", os.path.basename(self.input_video))
  614. self.remove_media_file(self.output_video)
  615. self.ffmpeg_convert()
  616.  
  617. except OSError as e:
  618. if e.errno == os.errno.ENOENT:
  619. Logger.warning("HANDBRAKECLI not found, install on your system to use this script")
  620. sys.exit(0)
  621. else:
  622. Logger.info("HANDBRAKECLI - {input} converted succesfully converted to: {output}".format(input=self.input_video,output=self.output_video))
  623.  
  624.  
  625. """----------------------------------------------------------------------------------
  626. Convert files found to mp4 using ffmeg
  627. ----------------------------------------------------------------------------------"""
  628. def ffmpeg_convert(self):
  629. Logger.info("FFMPEG - converting %s to %s", self.input_video, self.output_video)
  630.  
  631. self.error = 0
  632.  
  633. try:
  634. proc = subprocess.Popen([
  635. FFMPEG,
  636. '-n',
  637. '-fflags', '+genpts',
  638. '-f', 'h264',
  639. '-i', self.input_video,
  640. '-vcodec', 'copy',
  641. '-acodec', 'aac',
  642. '-strict', '-2',
  643. '-c:v', 'libx264',
  644. '-c:a', 'aac',
  645. '-strict','experimental',
  646. '-b:a', '192K',
  647. '-maxrate', '5200K',
  648. '-bufsize', '5200K',
  649. '-crf', '23',
  650. '-r','30',
  651. self.output_video
  652. ],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
  653.  
  654. output = proc.stderr.read()
  655.  
  656. if output.find("Error ") != -1 and not good_output(self.input_video,self.output_video):
  657. self.error = 1
  658.  
  659. # If the return code is 1 that means FFMPEG failed, use HANDBRAKE instead
  660. if self.error == 1:
  661. Logger.warning("FFMPEG - failure converting %s", os.path.basename(self.input_video))
  662.  
  663. except OSError as e:
  664. if e.errno == os.errno.ENOENT:
  665. Logger.warning("FFMPEG not found, install on your system to use this script")
  666. sys.exit(0)
  667. else:
  668. Logger.info("FFMPEG - converting successful: %s", os.path.basename(self.input_video))
  669.  
  670. """----------------------------------------------------------------------------------
  671. Extract SRT files if they are embedded
  672. ----------------------------------------------------------------------------------"""
  673. def extract_srt(self):
  674.  
  675. #Get file info and Parse it
  676. proc =subprocess.Popen([
  677. FFMPEG,
  678. '-i', self.input_video,
  679. ],stdout=None,stderr=subprocess.PIPE)
  680. output = proc.stderr.read()
  681.  
  682. #Try to match subtitles
  683. matches1 = re.findall(SUB_PATTERN,output,re.I | re.M)
  684. matches2 = re.findall(SUB_PATTERN2,output,re.I | re.M)
  685.  
  686. (offset, finalmatch) = combine_matches(matches1,matches2)
  687.  
  688. #Check if there is a forced option
  689. if not finalmatch:
  690. return
  691.  
  692. filename = os.path.basename(self.output_video)[:-4]
  693.  
  694. for match in finalmatch:
  695. forced=''
  696. if 'forced' in match:
  697. forced = '.forced'
  698. prefix=''
  699. if len(match) == 5:
  700. prefix = get_locale(match[1],match[4])
  701. else:
  702. prefix = get_locale(match[1])
  703.  
  704. if prefix.find('forced') != -1:
  705. srtFile = os.path.join(self.target_dir,filename + prefix + ".srt")
  706. else:
  707. srtFile = os.path.join(self.target_dir,filename + prefix + forced + ".srt")
  708.  
  709. Logger.info("creating subtitle: {name}".format(name=srtFile))
  710. proc =subprocess.call([
  711. FFMPEG,'-y',
  712. '-i', self.input_video,
  713. '-map', '0:s:{num}'.format(num=int(match[0])-int(offset)),
  714. srtFile
  715. ],stdout=DEV_NULL,stderr=DEV_NULL)
  716. """----------------------------------------------------------------------------------
  717. Move Subs to target_dir
  718. ----------------------------------------------------------------------------------"""
  719. def move_external_subs(self):
  720. for sub in self.external_subtitles:
  721. new_sub = os.path.join(self.target_dir,os.path.basename(sub))
  722. shutil.move(sub,new_sub)
  723. Logger.info("Subtitle: {old}\n moved to {new}".format(old=sub,new=new_sub))
  724.  
  725. """----------------------------------------------------------------------------------
  726. Remove files quietly if they don't exist
  727. ----------------------------------------------------------------------------------"""
  728. def remove_media_file(self,file):
  729. try:
  730. os.remove(file)
  731. except OSError as e:
  732. if e.errno != os.errno.ENOENT:
  733. raise
  734. else:
  735. Logger.info("system - deleted file %s",file)
  736.  
  737. """----------------------------------------------------------------------------------
  738. Recursively remove folders
  739. ----------------------------------------------------------------------------------"""
  740. def remove_folder(folder):
  741. print folder != media_path
  742. if not find_media_files(folder) and folder != media_path:
  743. try:
  744. shutil.rmtree(folder,True)
  745. Logger.info("Folder {folder} succesfully removed".format(folder=folder))
  746. remove_folder(os.path.dirname(folder))
  747. except OSError as reason:
  748. Logger.info(reason)
  749.  
  750.  
  751. if __name__ == '__main__':
  752. main(sys.argv)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement