2021-06-03 15:20:39 +02:00
|
|
|
import json
|
2017-02-13 15:32:07 +01:00
|
|
|
import shutil
|
|
|
|
import tempfile
|
2017-03-11 21:01:52 +01:00
|
|
|
import traceback
|
2022-09-09 11:51:16 +02:00
|
|
|
from cgi import parse_header
|
2015-09-24 18:58:02 +02:00
|
|
|
from contextlib import closing
|
2022-09-09 11:51:16 +02:00
|
|
|
from pathlib import Path
|
2022-09-09 11:58:57 +02:00
|
|
|
from typing import Optional
|
2019-08-18 17:45:48 +02:00
|
|
|
from urllib.parse import urlsplit
|
|
|
|
|
2021-06-03 15:20:39 +02:00
|
|
|
import mutagen
|
|
|
|
import requests
|
|
|
|
from celery import Celery
|
|
|
|
from celery.utils.log import get_task_logger
|
2022-09-09 11:51:16 +02:00
|
|
|
from requests import Response
|
2015-06-10 21:04:49 +02:00
|
|
|
|
2022-07-26 14:18:41 +02:00
|
|
|
from .config import config
|
|
|
|
|
2022-02-22 20:03:31 +01:00
|
|
|
worker = Celery()
|
2015-06-10 21:04:49 +02:00
|
|
|
logger = get_task_logger(__name__)
|
|
|
|
|
|
|
|
|
2022-02-22 20:03:31 +01:00
|
|
|
@worker.task(name="podcast-download", acks_late=True)
|
2019-08-18 17:45:48 +02:00
|
|
|
def podcast_download(
|
2022-09-09 11:58:57 +02:00
|
|
|
id: int,
|
|
|
|
url: str,
|
|
|
|
podcast_name: str,
|
|
|
|
album_override: bool,
|
|
|
|
track_title: Optional[str],
|
2019-08-18 17:45:48 +02:00
|
|
|
):
|
2015-09-22 21:26:08 +02:00
|
|
|
"""
|
2022-09-09 11:58:57 +02:00
|
|
|
Download a podcast episode.
|
2015-09-22 21:26:08 +02:00
|
|
|
|
2022-09-09 11:58:57 +02:00
|
|
|
Args:
|
|
|
|
id: Episode ID.
|
|
|
|
url: Episode download url.
|
|
|
|
podcast_name: Podcast name to save to the metadata.
|
|
|
|
album_override: Whether to override the album metadata.
|
|
|
|
track_title: Episode title to override the title metadata.
|
2015-10-30 21:10:16 +01:00
|
|
|
|
2022-09-09 11:58:57 +02:00
|
|
|
Returns:
|
|
|
|
Status of the podcast download as JSON string.
|
2015-09-22 21:26:08 +02:00
|
|
|
"""
|
2015-10-21 01:03:34 +02:00
|
|
|
# Object to store file IDs, episode IDs, and download status
|
|
|
|
# (important if there's an error before the file is posted)
|
2019-08-18 17:45:48 +02:00
|
|
|
obj = {"episodeid": id}
|
2015-10-21 01:03:34 +02:00
|
|
|
try:
|
|
|
|
re = None
|
|
|
|
with closing(requests.get(url, stream=True)) as r:
|
2022-09-09 11:51:16 +02:00
|
|
|
filename = extract_filename(r)
|
2019-08-18 17:45:48 +02:00
|
|
|
with tempfile.NamedTemporaryFile(mode="wb+", delete=False) as audiofile:
|
2017-10-08 00:48:39 +02:00
|
|
|
r.raw.decode_content = True
|
2017-02-13 15:32:07 +01:00
|
|
|
shutil.copyfileobj(r.raw, audiofile)
|
2018-12-27 23:50:33 +01:00
|
|
|
# mutagen should be able to guess the write file type
|
2018-12-13 18:36:10 +01:00
|
|
|
metadata_audiofile = mutagen.File(audiofile.name, easy=True)
|
2018-12-27 23:50:33 +01:00
|
|
|
# if for some reason this should fail lets try it as a mp3 specific code
|
|
|
|
if metadata_audiofile == None:
|
2018-12-28 00:38:17 +01:00
|
|
|
# if this happens then mutagen couldn't guess what type of file it is
|
|
|
|
mp3suffix = ("mp3", "MP3", "Mp3", "mP3")
|
|
|
|
# so we treat it like a mp3 if it has a mp3 file extension and hope for the best
|
|
|
|
if filename.endswith(mp3suffix):
|
2019-08-18 17:45:48 +02:00
|
|
|
metadata_audiofile = mutagen.mp3.MP3(
|
|
|
|
audiofile.name, ID3=mutagen.easyid3.EasyID3
|
|
|
|
)
|
|
|
|
# replace track metadata as indicated by album_override setting
|
2017-03-17 02:10:04 +01:00
|
|
|
# replace album title as needed
|
2019-08-18 17:45:48 +02:00
|
|
|
metadata_audiofile = podcast_override_metadata(
|
|
|
|
metadata_audiofile, podcast_name, album_override, track_title
|
|
|
|
)
|
2018-12-13 18:36:10 +01:00
|
|
|
metadata_audiofile.save()
|
|
|
|
filetypeinfo = metadata_audiofile.pprint()
|
2019-08-18 17:45:48 +02:00
|
|
|
logger.info(
|
2022-01-25 23:45:00 +01:00
|
|
|
"filetypeinfo is {}".format(filetypeinfo.encode("ascii", "ignore"))
|
2019-08-18 17:45:48 +02:00
|
|
|
)
|
2022-07-26 14:18:41 +02:00
|
|
|
callback_url = f"{config.general.public_url}/rest/media"
|
|
|
|
callback_api_key = config.general.api_key
|
|
|
|
|
2019-08-18 17:45:48 +02:00
|
|
|
re = requests.post(
|
|
|
|
callback_url,
|
|
|
|
files={"file": (filename, open(audiofile.name, "rb"))},
|
2022-07-26 14:18:41 +02:00
|
|
|
auth=requests.auth.HTTPBasicAuth(callback_api_key, ""),
|
2019-08-18 17:45:48 +02:00
|
|
|
)
|
2015-10-21 01:03:34 +02:00
|
|
|
re.raise_for_status()
|
2020-05-04 13:24:57 +02:00
|
|
|
try:
|
|
|
|
response = re.content.decode()
|
|
|
|
except (UnicodeDecodeError, AttributeError):
|
|
|
|
response = re.content
|
2019-08-18 17:45:48 +02:00
|
|
|
f = json.loads(
|
2020-05-04 13:24:57 +02:00
|
|
|
response
|
2019-08-18 17:45:48 +02:00
|
|
|
) # Read the response from the media API to get the file id
|
|
|
|
obj["fileid"] = f["id"]
|
|
|
|
obj["status"] = 1
|
2015-10-21 01:03:34 +02:00
|
|
|
except Exception as e:
|
2019-08-18 17:45:48 +02:00
|
|
|
obj["error"] = e.message
|
2022-01-25 23:45:00 +01:00
|
|
|
logger.info(f"Error during file download: {e}")
|
2019-08-18 17:45:48 +02:00
|
|
|
logger.debug("Original Traceback: %s" % (traceback.format_exc(e)))
|
|
|
|
obj["status"] = 0
|
2015-10-21 01:03:34 +02:00
|
|
|
return json.dumps(obj)
|
2015-09-24 21:57:38 +02:00
|
|
|
|
2019-08-18 17:45:48 +02:00
|
|
|
|
2018-12-23 20:54:47 +01:00
|
|
|
def podcast_override_metadata(m, podcast_name, override, track_title):
|
2017-03-17 02:10:04 +01:00
|
|
|
"""
|
|
|
|
Override m['album'] if empty or forced with override arg
|
|
|
|
"""
|
|
|
|
# if the album override option is enabled replace the album id3 tag with the podcast name even if the album tag contains data
|
|
|
|
if override is True:
|
2019-08-18 17:45:48 +02:00
|
|
|
logger.debug(
|
2022-01-25 23:45:00 +01:00
|
|
|
"overriding album name to {} in podcast".format(
|
2019-08-18 17:45:48 +02:00
|
|
|
podcast_name.encode("ascii", "ignore")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
m["album"] = podcast_name
|
|
|
|
m["title"] = track_title
|
|
|
|
m["artist"] = podcast_name
|
2017-03-17 02:10:04 +01:00
|
|
|
else:
|
|
|
|
# replace the album id3 tag with the podcast name if the album tag is empty
|
|
|
|
try:
|
2019-08-18 17:45:48 +02:00
|
|
|
m["album"]
|
2017-03-17 02:10:04 +01:00
|
|
|
except KeyError:
|
2019-08-18 17:45:48 +02:00
|
|
|
logger.debug(
|
2022-01-25 23:45:00 +01:00
|
|
|
"setting new album name to {} in podcast".format(
|
2019-08-18 17:45:48 +02:00
|
|
|
podcast_name.encode("ascii", "ignore")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
m["album"] = podcast_name
|
2017-03-17 02:10:04 +01:00
|
|
|
return m
|
2015-09-24 21:57:38 +02:00
|
|
|
|
2019-08-18 17:45:48 +02:00
|
|
|
|
2022-09-09 11:51:16 +02:00
|
|
|
def extract_filename(response: Response) -> str:
|
2015-09-24 21:57:38 +02:00
|
|
|
"""
|
2022-09-09 11:51:16 +02:00
|
|
|
Extract the filename from a download request.
|
2015-09-24 21:57:38 +02:00
|
|
|
|
2022-09-09 11:51:16 +02:00
|
|
|
Args:
|
|
|
|
response: Download request response.
|
2015-10-30 21:10:16 +01:00
|
|
|
|
2022-09-09 11:51:16 +02:00
|
|
|
Returns:
|
|
|
|
Extracted filename.
|
2015-09-24 21:57:38 +02:00
|
|
|
"""
|
2022-09-09 11:51:16 +02:00
|
|
|
if "Content-Disposition" in response.headers:
|
|
|
|
_, params = parse_header(response.headers["Content-Disposition"])
|
|
|
|
if "filename" in params:
|
|
|
|
return params["filename"]
|
|
|
|
|
|
|
|
return Path(urlsplit(response.url).path).name
|