mirror of
				https://github.com/KevinMidboe/spotify-downloader.git
				synced 2025-10-29 18:00:15 +00:00 
			
		
		
		
	Improve directory tree
This commit is contained in:
		
							
								
								
									
										1
									
								
								spotdl/encode/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								spotdl/encode/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
from spotdl.encode.encode_base import EncoderBase
 | 
			
		||||
							
								
								
									
										56
									
								
								spotdl/encode/encode_base.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								spotdl/encode/encode_base.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
from abc import ABC
 | 
			
		||||
from abc import abstractmethod
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
  NOTE ON ENCODERS
 | 
			
		||||
  ================
 | 
			
		||||
 | 
			
		||||
* A comparision between FFmpeg, avconv, and libav:
 | 
			
		||||
  https://stackoverflow.com/questions/9477115
 | 
			
		||||
 | 
			
		||||
* FFmeg encoders sorted in descending order based
 | 
			
		||||
  on the quality of audio produced:
 | 
			
		||||
  libopus > libvorbis >= libfdk_aac > aac > libmp3lame
 | 
			
		||||
 | 
			
		||||
* libfdk_aac encoder, due to copyrights needs to be compiled
 | 
			
		||||
  by end user on MacOS brew install ffmpeg --with-fdk-aac
 | 
			
		||||
  will do just that. Other OS? See:
 | 
			
		||||
  https://trac.ffmpeg.org/wiki/Encode/AAC
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EncoderBase(ABC):
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def __init__(self, encoder_path, loglevel, additional_arguments):
 | 
			
		||||
        self.encoder_path = encoder_path
 | 
			
		||||
        self._loglevel = loglevel
 | 
			
		||||
        self._additional_arguments = additional_arguments
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def set_argument(self, argument):
 | 
			
		||||
        self._additional_arguments += argument.split()
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def get_encoding(self, filename):
 | 
			
		||||
        _, extension = os.path.splitext(filename)
 | 
			
		||||
        # Ignore the initial dot from file extension
 | 
			
		||||
        return extension[1:]
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def set_debuglog(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def _generate_encode_command(self, input_file, output_file):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def _generate_encoding_arguments(self, input_encoding, output_encoding):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abstractmethod
 | 
			
		||||
    def re_encode(self, input_encoding, output_encoding):
 | 
			
		||||
        pass
 | 
			
		||||
							
								
								
									
										4
									
								
								spotdl/encode/encoders/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								spotdl/encode/encoders/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
from spotdl.encode.encoders.ffmpeg import EncoderFFmpeg
 | 
			
		||||
from spotdl.encode.encoders.avconv import EncoderAvconv
 | 
			
		||||
 | 
			
		||||
EncodeClasses = (EncoderFFmpeg, EncoderAvconv)
 | 
			
		||||
							
								
								
									
										77
									
								
								spotdl/encode/encoders/avconv.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								spotdl/encode/encoders/avconv.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
			
		||||
import subprocess
 | 
			
		||||
import os
 | 
			
		||||
from logzero import logger as log
 | 
			
		||||
from spotdl.encode import EncoderBase
 | 
			
		||||
 | 
			
		||||
class EncoderAvconv(EncoderBase):
 | 
			
		||||
    def __init__(self, encoder_path="avconv"):
 | 
			
		||||
        print("Using avconv is deprecated and this will be removed in",
 | 
			
		||||
              "future versions. Use ffmpeg instead.")
 | 
			
		||||
        encoder_path = encoder_path
 | 
			
		||||
        _loglevel = "-loglevel 0"
 | 
			
		||||
        _additional_arguments = ["-ab", "192k"]
 | 
			
		||||
 | 
			
		||||
        super().__init__(encoder_path, _loglevel, _additional_arguments)
 | 
			
		||||
 | 
			
		||||
    def set_argument(self, argument):
 | 
			
		||||
        super().set_argument(argument)
 | 
			
		||||
 | 
			
		||||
    def get_encoding(self, filename):
 | 
			
		||||
        return super().get_encoding(filename)
 | 
			
		||||
 | 
			
		||||
    def _generate_encoding_arguments(self, input_encoding, output_encoding):
 | 
			
		||||
        initial_arguments = self._rules.get(input_encoding)
 | 
			
		||||
        if initial_arguments is None:
 | 
			
		||||
            raise TypeError(
 | 
			
		||||
                'The input format ("{}") is not supported.'.format(
 | 
			
		||||
                input_extension,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        arguments = initial_arguments.get(output_encoding)
 | 
			
		||||
        if arguments is None:
 | 
			
		||||
            raise TypeError(
 | 
			
		||||
                'The output format ("{}") is not supported.'.format(
 | 
			
		||||
                output_extension,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        return arguments
 | 
			
		||||
 | 
			
		||||
    def _generate_encoding_arguments(self, input_encoding, output_encoding):
 | 
			
		||||
        return ""
 | 
			
		||||
 | 
			
		||||
    def set_debuglog(self):
 | 
			
		||||
        self._loglevel = "-loglevel debug"
 | 
			
		||||
 | 
			
		||||
    def _generate_encode_command(self, input_file, output_file):
 | 
			
		||||
        input_encoding = self.get_encoding(input_file)
 | 
			
		||||
        output_encoding = self.get_encoding(output_file)
 | 
			
		||||
 | 
			
		||||
        arguments = self._generate_encoding_arguments(
 | 
			
		||||
            input_encoding,
 | 
			
		||||
            output_encoding
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        command = [self.encoder_path] \
 | 
			
		||||
            + ["-y"] \
 | 
			
		||||
            + self._loglevel.split() \
 | 
			
		||||
            + ["-i", input_file] \
 | 
			
		||||
            + self._additional_arguments \
 | 
			
		||||
            + [output_file]
 | 
			
		||||
 | 
			
		||||
        return command
 | 
			
		||||
 | 
			
		||||
    def re_encode(self, input_file, output_file, delete_original=False):
 | 
			
		||||
        encode_command = self._generate_encode_command(
 | 
			
		||||
            input_file,
 | 
			
		||||
            output_file
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        returncode = subprocess.call(encode_command)
 | 
			
		||||
        encode_successful = returncode == 0
 | 
			
		||||
 | 
			
		||||
        if encode_successful and delete_original:
 | 
			
		||||
            os.remove(input_file)
 | 
			
		||||
 | 
			
		||||
        return returncode
 | 
			
		||||
							
								
								
									
										92
									
								
								spotdl/encode/encoders/ffmpeg.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								spotdl/encode/encoders/ffmpeg.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
import subprocess
 | 
			
		||||
import os
 | 
			
		||||
from logzero import logger as log
 | 
			
		||||
from spotdl.encode import EncoderBase
 | 
			
		||||
 | 
			
		||||
RULES = {
 | 
			
		||||
    "m4a": {
 | 
			
		||||
        "mp3": "-codec:v copy -codec:a libmp3lame -ar 48000",
 | 
			
		||||
        "webm": "-codec:a libopus -vbr on ",
 | 
			
		||||
        "m4a": "-acodec copy ",
 | 
			
		||||
        "flac": "-codec:a flac -ar 48000",
 | 
			
		||||
    },
 | 
			
		||||
    "webm": {
 | 
			
		||||
        "mp3": "-codec:a libmp3lame -ar 48000",
 | 
			
		||||
        "m4a": "-cutoff 20000 -codec:a aac -ar 48000",
 | 
			
		||||
        "flac": "-codec:a flac -ar 48000",
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class EncoderFFmpeg(EncoderBase):
 | 
			
		||||
    def __init__(self, encoder_path="ffmpeg"):
 | 
			
		||||
        encoder_path = encoder_path
 | 
			
		||||
        _loglevel = "-hide_banner -nostats -v panic"
 | 
			
		||||
        _additional_arguments = ["-b:a", "192k", "-vn"]
 | 
			
		||||
 | 
			
		||||
        super().__init__(encoder_path, _loglevel, _additional_arguments)
 | 
			
		||||
 | 
			
		||||
        self._rules = RULES
 | 
			
		||||
 | 
			
		||||
    def set_argument(self, argument):
 | 
			
		||||
        super().set_argument(argument)
 | 
			
		||||
 | 
			
		||||
    def set_trim_silence(self):
 | 
			
		||||
        self.set_argument("-af silenceremove=start_periods=1")
 | 
			
		||||
 | 
			
		||||
    def get_encoding(self, filename):
 | 
			
		||||
        return super().get_encoding(filename)
 | 
			
		||||
 | 
			
		||||
    def _generate_encoding_arguments(self, input_encoding, output_encoding):
 | 
			
		||||
        initial_arguments = self._rules.get(input_encoding)
 | 
			
		||||
        if initial_arguments is None:
 | 
			
		||||
            raise TypeError(
 | 
			
		||||
                'The input format ("{}") is not supported.'.format(
 | 
			
		||||
                input_extension,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        arguments = initial_arguments.get(output_encoding)
 | 
			
		||||
        if arguments is None:
 | 
			
		||||
            raise TypeError(
 | 
			
		||||
                'The output format ("{}") is not supported.'.format(
 | 
			
		||||
                output_extension,
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        return arguments
 | 
			
		||||
 | 
			
		||||
    def set_debuglog(self):
 | 
			
		||||
        self._loglevel = "-loglevel debug"
 | 
			
		||||
 | 
			
		||||
    def _generate_encode_command(self, input_file, output_file):
 | 
			
		||||
        input_encoding = self.get_encoding(input_file)
 | 
			
		||||
        output_encoding = self.get_encoding(output_file)
 | 
			
		||||
 | 
			
		||||
        arguments = self._generate_encoding_arguments(
 | 
			
		||||
            input_encoding,
 | 
			
		||||
            output_encoding
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        command = [self.encoder_path] \
 | 
			
		||||
            + ["-y", "-nostdin"] \
 | 
			
		||||
            + self._loglevel.split() \
 | 
			
		||||
            + ["-i", input_file] \
 | 
			
		||||
            + arguments.split() \
 | 
			
		||||
            + self._additional_arguments \
 | 
			
		||||
            + [output_file]
 | 
			
		||||
 | 
			
		||||
        return command
 | 
			
		||||
 | 
			
		||||
    def re_encode(self, input_file, output_file, delete_original=False):
 | 
			
		||||
        encode_command = self._generate_encode_command(
 | 
			
		||||
            input_file,
 | 
			
		||||
            output_file
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        returncode = subprocess.call(encode_command)
 | 
			
		||||
        encode_successful = returncode == 0
 | 
			
		||||
 | 
			
		||||
        if encode_successful and delete_original:
 | 
			
		||||
            os.remove(input_file)
 | 
			
		||||
 | 
			
		||||
        return returncode
 | 
			
		||||
		Reference in New Issue
	
	Block a user