SHARE
TWEET

In Sorte Diaboli / "In league with the devil"

DeaD_EyE Jul 20th, 2019 97 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/env python3.8
  2.  
  3. """
  4. Got my inspiration from this video: https://www.youtube.com/watch?v=El4zsUZjsDc
  5. The pinned comment contains a list in a format of:
  6. hh:mm:ss Title ...
  7.  
  8. You need pydub and ffmpeg installed.
  9. # pip install pydub --user # or in a venv
  10.  
  11. Thanks to this project: http://pydub.com/
  12. Just needed to read the doc for 20 seconds to understand how it works
  13. Very nice API
  14. I extended it with Path support.
  15.  
  16. Also I tried the new walrus operator
  17. Google for: Python Named Assignment Expression
  18. or: PEP 572
  19.  
  20. + A little bit decorator magic.
  21. And some other stuff you would never use in production.
  22. """
  23.  
  24. import sys
  25. import re
  26. from argparse import ArgumentParser
  27. from itertools import tee
  28. from pathlib import Path
  29. from shutil import which
  30.  
  31. from pydub import AudioSegment
  32.  
  33.  
  34. def pathlib_compat(method):
  35.     def inner(cls, file, format):
  36.         if isinstance(file, Path):
  37.             file = str(file)
  38.         if format and format.startswith('.'):
  39.             format = format[1:]
  40.         return method(cls, file, format)
  41.     return inner
  42.  
  43.  
  44. class AudioSegmentPath:
  45.  
  46.     def __init__(self, data):
  47.         self.data = data
  48.  
  49.     @classmethod
  50.     @pathlib_compat
  51.     def from_file(cls, file, format):
  52.         data = AudioSegment.from_file(file, format)
  53.         return cls(data)
  54.  
  55.     @pathlib_compat
  56.     def export(self, file, format):
  57.         self.data.export(file, format)
  58.  
  59.     def __getitem__(self, miliseconds):
  60.         return self.__class__(self.data[miliseconds])
  61.  
  62.  
  63. def parse_segment_file(file):
  64.     regex = re.compile(r'(\d{2}):(\d{2}):(\d{2}) (.*$)')
  65.     with file.open() as fd:
  66.         timestamps = []
  67.         titles = []
  68.         for line in fd:
  69.             if (match := regex.search(line)):
  70.                 *timestamp, title = match.groups()
  71.                 hh, mm, ss = map(int, timestamp)
  72.                 miliseconds = 1000 * (hh * 3600 + mm * 60 + ss)
  73.                 timestamps.append(miliseconds)
  74.                 titles.append(title.strip())
  75.     timestamps.append(None)
  76.     last, current = tee(timestamps)
  77.     next(current)
  78.     slices = [slice(start, stop) for start, stop in zip(last, current)]
  79.     return zip(titles, slices)
  80.  
  81.  
  82. def split_audio(file):
  83.     print(f'Opening {file}')
  84.     song = AudioSegmentPath.from_file(file, file.suffix)
  85.     print(f'File loaded, splitting now')
  86.     for index, (title, segment) in enumerate(parse_segment_file(file.with_suffix('.segment')), start=1):
  87.         song_segment = song[segment]
  88.         title = f'{index} {title}'
  89.         destination = file.with_name(title).with_suffix(file.suffix)
  90.         song_segment.export(destination, file.suffix)
  91.         print(f'Written {destination.name}')
  92.  
  93.  
  94. def main(file):
  95.     file = Path(file).absolute()
  96.     if not which('ffmpeg'):
  97.         print(f'ffmpeg is missing or not in path', file=sys.stderr)
  98.         return 3
  99.     if not file.exists():
  100.         print(f'File {file.name} does not exist', file=sys.stderr)
  101.         return 1
  102.     if not (segment_file := file.with_suffix('.segment')).exists():
  103.         print(f'Segmentfile {segment_file.name} is missing')
  104.         return 2
  105.     split_audio(file)
  106.     return 0
  107.  
  108.  
  109. if __name__ == '__main__':
  110.     parser = ArgumentParser(description='Split a audio')
  111.     parser.add_argument('file', help='Audio file to split')
  112.     args = parser.parse_args()
  113.     sys.exit(main(args.file))
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top