podcast direct downloading

This commit is contained in:
zotify 2023-03-25 14:35:36 +13:00
parent 2a96a12a2a
commit dbf05e3b50
4 changed files with 201 additions and 12 deletions

163
.gitignore vendored Normal file
View File

@ -0,0 +1,163 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
.vscode/*
!.vscode/settings.json

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"python.linting.flake8Enabled": true,
"python.linting.mypyEnabled": true,
"python.linting.enabled": true,
"python.formatting.provider": "black"
}

View File

@ -18,6 +18,7 @@ from zotify import Session
from zotify.config import Config
from zotify.file import TranscodingError
from zotify.loader import Loader
from zotify.playable import Track
from zotify.printer import Printer, PrintChannel
from zotify.utils import API_URL, AudioFormat, b62_to_hex
@ -302,7 +303,7 @@ class App:
output,
self.__config.chunk_size,
)
if self.__config.save_lyrics:
if self.__config.save_lyrics and isinstance(track, Track):
with Loader("Fetching lyrics..."):
try:
track.get_lyrics().save(output)

View File

@ -174,11 +174,7 @@ class Track(PlayableContentFeeder.LoadedStream, Playable):
}
def get_lyrics(self) -> Lyrics:
"""
Fetch lyrics from track if available
Returns:
Instance of track lyrics
"""
"""Returns track lyrics if available"""
if not self.track.has_lyrics:
raise FileNotFoundError(
f"No lyrics available for {self.track.artist[0].name} - {self.track.name}"
@ -226,10 +222,33 @@ class Episode(PlayableContentFeeder.LoadedStream, Playable):
def can_download_direct(self) -> bool:
"""Returns true if episode can be downloaded from its original external source"""
return bool(self.episode.is_externally_hosted)
return bool(self.external_url)
def download_direct(self) -> LocalFile:
"""Downloads episode from original source"""
if not self.can_download_directly():
raise RuntimeError("Podcast cannot be downloaded direct")
raise NotImplementedError()
def write_audio_stream(
self, output: Path, chunk_size: int = 128 * 1024
) -> LocalFile:
"""
Writes audio stream to file
Args:
output: File path of saved audio stream
chunk_size: maximum number of bytes to read at a time
Returns:
LocalFile object
"""
if not self.can_download_direct():
return super().write_audio_stream(output, chunk_size)
file = f"{output}.{self.external_url.rsplit('.', 1)[-1]}"
with get(self.external_url, stream=True) as r, open(
file, "wb"
) as f, Printer.progress(
desc=self.name,
total=self.input_stream.size,
unit="B",
unit_scale=True,
unit_divisor=1024,
position=0,
leave=False,
) as p_bar:
for chunk in r.iter_content(chunk_size=chunk_size):
p_bar.update(f.write(chunk))
return LocalFile(Path(file))