sponskrub.py 4.0 KB

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