sponskrub.py 3.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import os
  2. import shlex
  3. import subprocess
  4. from .common import PostProcessor
  5. from ..utils import (
  6. Popen,
  7. PostProcessingError,
  8. check_executable,
  9. cli_option,
  10. encodeArgument,
  11. prepend_extension,
  12. shell_quote,
  13. str_or_none,
  14. )
  15. # Deprecated in favor of the native implementation
  16. class SponSkrubPP(PostProcessor):
  17. _temp_ext = 'spons'
  18. _exe_name = 'sponskrub'
  19. def __init__(self, downloader, path='', args=None, ignoreerror=False, cut=False, force=False, _from_cli=False):
  20. PostProcessor.__init__(self, downloader)
  21. self.force = force
  22. self.cutout = cut
  23. self.args = str_or_none(args) or '' # For backward compatibility
  24. self.path = self.get_exe(path)
  25. if not _from_cli:
  26. self.deprecation_warning(
  27. 'yt_dlp.postprocessor.SponSkrubPP support is deprecated and may be removed in a future version. '
  28. 'Use yt_dlp.postprocessor.SponsorBlock and yt_dlp.postprocessor.ModifyChaptersPP instead')
  29. if not ignoreerror and self.path is None:
  30. if path:
  31. raise PostProcessingError(f'sponskrub not found in "{path}"')
  32. else:
  33. raise PostProcessingError('sponskrub not found. Please install or provide the path using --sponskrub-path')
  34. def get_exe(self, path=''):
  35. if not path or not check_executable(path, ['-h']):
  36. path = os.path.join(path, self._exe_name)
  37. if not check_executable(path, ['-h']):
  38. return None
  39. return path
  40. @PostProcessor._restrict_to(images=False)
  41. def run(self, information):
  42. if self.path is None:
  43. return [], information
  44. filename = information['filepath']
  45. if not os.path.exists(filename): # no download
  46. return [], information
  47. if information['extractor_key'].lower() != 'youtube':
  48. self.to_screen('Skipping sponskrub since it is not a YouTube video')
  49. return [], information
  50. if self.cutout and not self.force and not information.get('__real_download', False):
  51. self.report_warning(
  52. 'Skipping sponskrub since the video was already downloaded. '
  53. 'Use --sponskrub-force to run sponskrub anyway')
  54. return [], information
  55. self.to_screen('Trying to %s sponsor sections' % ('remove' if self.cutout else 'mark'))
  56. if self.cutout:
  57. self.report_warning('Cutting out sponsor segments will cause the subtitles to go out of sync.')
  58. if not information.get('__real_download', False):
  59. self.report_warning('If sponskrub is run multiple times, unintended parts of the video could be cut out.')
  60. temp_filename = prepend_extension(filename, self._temp_ext)
  61. if os.path.exists(temp_filename):
  62. os.remove(temp_filename)
  63. cmd = [self.path]
  64. if not self.cutout:
  65. cmd += ['-chapter']
  66. cmd += cli_option(self._downloader.params, '-proxy', 'proxy')
  67. cmd += shlex.split(self.args) # For backward compatibility
  68. cmd += self._configuration_args(self._exe_name, use_compat=False)
  69. cmd += ['--', information['id'], filename, temp_filename]
  70. cmd = [encodeArgument(i) for i in cmd]
  71. self.write_debug(f'sponskrub command line: {shell_quote(cmd)}')
  72. stdout, _, returncode = Popen.run(cmd, text=True, stdout=None if self.get_param('verbose') else subprocess.PIPE)
  73. if not returncode:
  74. os.replace(temp_filename, filename)
  75. self.to_screen('Sponsor sections have been %s' % ('removed' if self.cutout else 'marked'))
  76. elif returncode == 3:
  77. self.to_screen('No segments in the SponsorBlock database')
  78. else:
  79. raise PostProcessingError(
  80. stdout.strip().splitlines()[0 if stdout.strip().lower().startswith('unrecognised') else -1]
  81. or f'sponskrub failed with error code {returncode}')
  82. return [], information