liberate.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. #!/usr/bin/env python3
  2. # -*- coding: utf8 -*-
  3. # liberate - Download and/or convert multiple files to Ogg Vorbis
  4. # Copyright © 2017 Nichlas Severinsen
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see http://www.gnu.org/licenses/.
  18. import os
  19. import sys
  20. import argparse
  21. import colorama
  22. import youtube_dl
  23. _ffmpeg_opts = """
  24. ffmpeg \
  25. -i "%s" \
  26. -v panic \
  27. -af dynaudnorm \
  28. -af silenceremove=1:0:-50dB \
  29. -vn \
  30. -c:a libvorbis \
  31. -q:a 5 \
  32. -f ogg \
  33. "%s".ogg
  34. """
  35. def info(msg, filename=''):
  36. """Print a simple info message.
  37. Args:
  38. msg (str): message to print
  39. filename (str): optional filename to specify
  40. """
  41. y = colorama.Fore.WHITE
  42. r = colorama.Style.RESET_ALL
  43. print(y + "INFO: %s '%s'" % (msg, filename) + r)
  44. def warning(msg, filename=''):
  45. """Print a simple warning message.
  46. Args:
  47. msg (str): message to print
  48. filename (str): optional filename to specify
  49. """
  50. y = colorama.Fore.YELLOW
  51. r = colorama.Style.RESET_ALL
  52. print(y + "WARNING: %s '%s'" % (msg, filename) + r)
  53. def file_to_ogg(input_file, remove=False, ffmpeg_opts=_ffmpeg_opts):
  54. """Convert a single file to ogg using ffmpeg_opts with os.system
  55. Args:
  56. input_file (str): file or path to file to convert
  57. remove (bool): whether or not to remove the original file. False by default
  58. ffmpeg_opts (str): ffmpeg command and opts
  59. """
  60. if not os.path.exists(input_file):
  61. for filename in os.listdir():
  62. if input_file.split('-')[-1] in filename:
  63. input_file = filename
  64. break
  65. output_file, file_extension = os.path.splitext(input_file)
  66. if file_extension == '.ogg':
  67. warning('conversion ignored, input_file is already .ogg;', input_file)
  68. return
  69. os.system(ffmpeg_opts % (input_file.replace('"','\"'), output_file.replace('"','\"')))
  70. if remove:
  71. os.remove(input_file)
  72. def try_yt_dl(yt_dl, item, remove_flag):
  73. """Try to convert item downloaded by yt_dl
  74. Args:
  75. item (str): file or path to file to convert
  76. """
  77. try:
  78. if 'http' not in item['url']:
  79. item = yt_dl.extract_info('https://www.youtube.com/watch?v=' + item['url'], download=False )
  80. itemname = '%s-%s' % (item['title'].replace('/','_').replace('*',''), item['id'])
  81. filename = '%s.%s' % (itemname, item['ext'])
  82. outfile = '%s.ogg' % (itemname)
  83. if outfile in os.listdir():
  84. info('file has already been downloaded and converted', outfile)
  85. return
  86. yt_dl.download([item['webpage_url']])
  87. file_to_ogg(filename, remove_flag)
  88. except (KeyError, youtube_dl.utils.DownloadError):
  89. warning('failed to download and/or convert', item['title'])
  90. if __name__ == '__main__':
  91. colorama.init()
  92. yt_dl_opts = {
  93. 'format': 'bestaudio',
  94. 'ignore-errors': True,
  95. 'quiet': True
  96. }
  97. yt_dl = youtube_dl.YoutubeDL(yt_dl_opts)
  98. parser = argparse.ArgumentParser()
  99. parser.add_argument('-r', '--remove', dest='remove_flag', help='Remove files after conversion.', action='store_true')
  100. args = parser.parse_known_args()
  101. for argument in args[1]:
  102. if os.path.isfile(argument): # Single existing file
  103. file_to_ogg(argument, args[0].remove_flag)
  104. continue
  105. if os.path.isdir(argument): # Single existing directory (not recursive!)
  106. for input_file in os.listdir(argument):
  107. file_to_ogg(argument+input_file, args[0].remove_flag)
  108. continue
  109. yt_dl.params['extract_flat'] = True
  110. # If not a file and not a directory, try to get info with yt_dl
  111. try:
  112. yt_dl_result = yt_dl.extract_info(argument, download=False)
  113. if yt_dl_result['extractor'] == 'youtube:channel':
  114. yt_dl_result = yt_dl.extract_info(yt_dl_result['url'], download=False)
  115. except (KeyError, youtube_dl.utils.DownloadError):
  116. warning('youtube-dl failed to download', argument)
  117. continue
  118. if 'entries' in yt_dl_result: # If yt_dl found a playlist or several items
  119. for item in yt_dl_result['entries']:
  120. try_yt_dl(yt_dl, item, args[0].remove_flag)
  121. else: # yt_dl found one item
  122. try_yt_dl(yt_dl, yt_dl_result, args[0].remove_flag)