Модуль Python, предназначенный для загрузки файлов из определенных RSS-каналов, особенно предназначенных для подкастов. Он не использует какую-либо базу данных, но требует файла конфигурации.
Сценарий предназначен для периодического запуска. При запуске он анализирует каталог, в котором ранее хранились загруженные файлы. Затем он сравнивает эти файлы с файлами, перечисленными в RSS-канале, выявляя недостающие и загружая их.
По умолчанию поиск осуществляется в mp3
.
Результатом использования приведенного ниже примера для пустых каталогов будет:
dplocki@ghost-wheel:~$ python -m podcast_downloader
[2024-04-08 21:19:10] Loading configuration (from file: "~/.podcast_downloader_config.json")
[2024-04-08 21:19:15] Checking "The Skeptic Guide"
[2024-04-08 21:19:15] Last downloaded file ""
[2024-04-08 21:19:15] The Skeptic Guide: Downloading file: "https://traffic.libsyn.com/secure/skepticsguide/skepticast2024-04-06.mp3" saved as "skepticast2024-04-06.mp3"
[2024-04-08 21:19:41] Checking "The Real Python Podcast"
[2024-04-08 21:19:41] Last downloaded file ""
[2024-04-08 21:19:41] The Real Python Podcast: Downloading file: "https://chtbl.com/track/92DB94/files.realpython.com/podcasts/RPP_E199_03_Calvin.eef1db4d6679.mp3" saved as "[20240405] rpp_e199_03_calvin.eef1db4d6679.mp3"
[2024-04-08 21:20:04] Finished
Результат:
dplocki@ghost-wheel:~$ tree podcasts/
podcasts/
├── RealPython
│ └── [20240405] rpp_e199_03_calvin.eef1db4d6679.mp3
└── SGTTU
└── skepticast2024-04-06.mp3
2 directories, 2 files
Установка из PyPI:
pip install podcast_downloader
Для работы скрипта требуется файл конфигурации. После установки скрипт можно запустить как любой модуль Python:
python -m podcast_downloader
Также возможно запустить скрипт с заданным файлом конфигурации:
python -m podcast_downloader --config my_config.json
Пример файла конфигурации
{
"if_directory_empty" : " download_from_4_days " ,
"podcasts" : [
{
"name" : " The Skeptic Guide " ,
"rss_link" : " https://feed.theskepticsguide.org/feed/rss.aspx " ,
"path" : " ~/podcasts/SGTTU "
},
{
"rss_link" : " https://realpython.com/podcasts/rpp/feed " ,
"path" : " ~/podcasts/RealPython " ,
"file_name_template" : " [%publish_date%] %file_name%.%file_extension% "
}
]
}
По умолчанию файл конфигурации размещается в домашнем каталоге. Имя этого файла: .podcast_downloader_config.json
.
Конфигурационный файл имеет формат JSON. Ожидаемая кодировка — utf-8.
Путь к файлу конфигурации может быть указан аргументом скрипта.
Скрипт заменяет значения по умолчанию на значения, считанные из файла конфигурации. Они будут перегружены значениями, заданными из командной строки.
command line parameters > configuration file > default values
Свойство | Тип | Необходимый? | По умолчанию | Примечание |
---|---|---|---|---|
downloads_limit | число | нет | бесконечность | |
if_directory_empty | нить | нет | download_last | См. В случае пустого каталога |
podcast_extensions | ключ-значение | нет | {".mp3": "audio/mpeg"} | См. Фильтр типов файлов. |
podcasts | подраздел | да | [] | См. подкатегорию «Подкасты». |
http_headers | ключ-значение | нет | {"User-Agent": "podcast-downloader"} | См. заголовки HTTP-запросов. |
fill_up_gaps | логическое значение | нет | ЛОЖЬ | См. раздел Загрузка файлов из пробелов. |
download_delay | число | нет | 0 | См. Задержку загрузки. |
Сегмент podcasts
— это часть файла конфигурации, в которой вы предоставляете массив объектов со следующим содержимым:
Свойство | Тип | Необходимый | По умолчанию | Примечание |
---|---|---|---|---|
name | нить | да | - | Имя канала (используйте в логгере) |
rss_link | нить | да | - | URL-адрес RSS-канала |
path | нить | да | - | Путь к каталогу, в котором хранятся подкасты, будет загружен. |
file_name_template | нить | нет | %file_name%.%file_extension% | Шаблон для загружаемых файлов, см. Шаблон имени файла. |
disable | логическое значение | нет | false | Этот подкаст будет проигнорирован |
podcast_extensions | ключ-значение | нет | {".mp3": "audio/mpeg"} | Файловый фильтр |
if_directory_empty | нить | нет | download_last | См. В случае пустого каталога |
require_date | логическое значение | нет | false | Устарело. Дата подкаста должна быть добавлена в имя файла. Используйте шаблон file_name_template : [%publish_date%] %file_name%.%file_extension%" |
http_headers | ключ-значение | нет | {"User-Agent": "podcast-downloader"} | См. заголовки HTTP-запросов. |
fill_up_gaps | логическое значение | нет | ЛОЖЬ | См. раздел Загрузка файлов из пробелов. |
Некоторым серверам может не понравиться то, как им представляется urllib (заголовок HTTP User-Agent). Это может привести к таким проблемам, как: urllib.error.HTTPError: HTTP Error 403: Forbidden
. Поэтому существует возможность скрипта выдавать себя за что-то другое: указывая HTTP-заголовки при загрузке файлов.
Используйте параметр http_headers
в файле конфигурации. Значение должно быть объектом словаря, где каждый заголовок представлен как пара ключ-значение. Ключ — это заголовок заголовка, а значение — это значение заголовка.
По умолчанию это значение: {"User-Agent": "podcast-downloader"}
. Предоставление чего-либо еще для http_headers
приведет к переопределению всех значений по умолчанию (они не объединяются).
С другой стороны, в подконфигурации подкаста http_headers
будут объединены с глобальными http_headers
. В случае конфликта (то же самое имя ключа) значение из подконфигурации подкаста переопределит глобальное значение.
Пример:
{
"http_headers" : {
"User-Agent" : " podcast-downloader "
},
"podcasts" : [
{
"name" : " Unua Podcast " ,
"rss_link" : " http://www.unuapodcast.org/feed.rss " ,
"path" : " ~/podcasts/unua_podcast " ,
"https_headers" : {
"User-Agent" : " Mozilla/5.0 "
}
},
{
"name" : " Dua Podcast " ,
"rss_link" : " http://www.duapodcast.org/feed.rss " ,
"path" : " ~/podcasts/dua_podcast " ,
"https_headers" : {
"Authorization" : " Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== "
}
}
]
}
В этом примере подкаст Unua будет загружен только с заголовком: User-Agent: Mozilla/5.0
, а подкаст Dua — с: User-Agent: podcast-downloader
и Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
.
Если вам нужно загрузить много файлов с одного сервера, возможно, лучше установить небольшую задержку между загрузками, чтобы сервер не распознал вас как злоумышленника. В скрипте есть опция download_delay
, которая определяет количество секунд, в течение которых скрипт будет ждать между загрузками.
Значение по умолчанию — 0
.
Примечания:
Скрипт принимает следующие аргументы командной строки:
Краткая версия | Длинное имя | Параметр | По умолчанию | Примечание |
---|---|---|---|---|
--config | нить | ~/.podcast_downloader_config.json | Размещение файла конфигурации | |
--downloads_limit | число | бесконечность | Максимальное количество скачиваемых mp3 файлов | |
--if_directory_empty | нить | download_last | Общий подход к пустому каталогу | |
--download_delay | число | 0 | Время ожидания (в секундах) между загрузками |
Используйте для изменения имени файла после загрузки.
Значение по умолчанию ( %file_name%.%file_extension%
) просто сохранит файл в том виде, в котором он был загружен первоначальным создателем. Имя файла и его расширение основаны на ссылке на файл подкаста.
Значения шаблона:
Имя | Примечания |
---|---|
%file_name% | Имя файла по ссылке, без расширения |
%file_extension% | Расширение файла по ссылке |
%publish_date% | Дата публикации записи RSS |
%title% | Название записи RSS |
По умолчанию %publish_date%
дает результат в формате YEARMMDD
. Чтобы изменить его, вы можете указать новый после двоеточия ( :
:). Сценарий соответствует кодам стандарта C 1989 года, но знак процента ( %
) необходимо заменить знаком доллара ( $
). Это из-за моего неудачного решения использовать символ процента в качестве маркера кода.
Стандартный код | Код сценария | Примечания |
---|---|---|
%Y%m%d | $Y$m$d | Значение по умолчанию %publish_date% |
%A | $A | Добавляет день недели (локальные языковые настройки) |
%x | $x | Местная дата представляет. Внимание : в некоторых настройках здесь используется / , поэтому это может вызвать проблемы с именем файла. |
[%publish_date%] %file_name%.%file_extension%
[%publish_date%] %title%.%file_extension%
Подкасты в основном сохраняются в виде файлов *.mp3
. По умолчанию Podcast Downloader ищет только их, игнорируя все остальные типы.
Если ваш подкаст поддерживает другие типы медиафайлов, вы можете указать фильтры файлов. Укажите расширение файла (например, .mp3
) и тип ссылки в самом RSS-канале (для mp3
это audio/mpeg
).
Если вы не знаете тип файла, вы можете поискать его в RSS-файле. Найдите теги enclosure
, это должно выглядеть так:
< enclosure url = " https://www.vidocast.url/podcast/episode23.m4a " length = " 14527149 " type = " audio/x-m4a " />
Примечание : точка в расширении файла обязательна.
"podcast_extensions" : {
".mp3" : " audio/mpeg " ,
".m4a" : " audio/x-m4a "
}
Если каталог подкаста пуст, сценарий должен знать, что делать. Из-за отсутствия базы данных вы можете:
Поведение по умолчанию: download_last
Скрипт загрузит все серии из ленты.
Устанавливается download_all_from_feed
.
Скрипт загрузит из ленты только последнюю серию. Это также подход сценария по умолчанию.
Устанавливается download_last
.
Скрипт загрузит из ленты ровно заданное количество серий.
Устанавливается download_last_n_episodes
. Вместо n необходимо указать количество серий, которые вы хотели скачать. Например: download_last_5_episodes
означает, что будут загружены пять последних эпизодов.
Скрипт загрузит все серии, вышедшие за последние n дней. Его можно использовать при регулярной загрузке. Число n указывается в значении настройки: download_from_n_days
. Например: download_from_3_days
означает загрузку всех выпусков за последние 3 дня.
Скрипт загрузит все серии, которые появятся после дня выхода последней серии.
Число n — это день обычного эпизода. Вы можете указать здесь дни недели в виде слова (размер букв игнорируется)
Полный рабочий день | Сократить имя |
---|---|
Понедельник | Пн. |
Вторник | вторник |
Среда | СР |
Четверг | четверг |
Пятница | Пт |
Суббота | Суббота |
Воскресенье | Солнце |
Вы можете указать число, оно будет означать день месяца. Скрипт принимает только числа от 1 до 28.
Устанавливается download_from_
.
Примеры:
Пример значения | Значение |
---|---|
download_from_monday | Новые серии появятся в понедельник. Скрипт загрузит все серии с прошлого вторника (включая ее) |
download_from_Fri | Новые серии появятся в пятницу. Скрипт скачает все серии с прошлой субботы (включая ее) |
download_from_12 | Новые серии выходят каждое 12 числа месяца. Скрипт загрузит все серии за 13 месяцев до этого. |
Как только вы создадите тотемный файл, сценарий сможет использовать его для хранения даты своего последнего запуска. Затем, исходя из этой даты, скрипт будет скачивать все новые серии, появившиеся с тех пор.
Устанавливается download_since_last_run
. Требуется установить файл хранилища по last_run_mark_file_path
.
{
"last_run_mark_file_path" : " ~/.totem.json " ,
"podcasts" : [
{
"name" : " The Skeptic Guide " ,
"rss_link" : " https://feed.theskepticsguide.org/feed/rss.aspx " ,
"path" : " ~/podcasts/SGTTU "
}
]
}
Скрипт считывает дату последней модификации файла. Дата изменения файла обновляется сценарием.
Скрипт распознает поток загружаемых файлов (на основе данных фида). По умолчанию начало скачивания отмечает последний загруженный файл (по ленте). В случае пробелов, ситуаций, когда отсутствуют файлы перед последним загруженным, скрипт по умолчанию их игнорирует. Однако есть возможность изменить это поведение, чтобы загружать все недостающие файлы между уже загруженными. Чтобы включить это, вам нужно установить для параметра fill_up_gaps
значение true . Важно отметить, что скрипт не будет скачивать файлы раньше первого (по ленте), самого раннего эпизода.
Значение по умолчанию: false
.
Скрипт просматривает все узлы items
в RSS-файле. Узел item
может содержать узел enclosure
. Эти узлы используются для передачи файлов. Согласно соглашению, один item
должен содержать только один enclosure
, но скрипт (как и используемая в нем библиотека) может обрабатывать несколько файлов, прикрепленных к item
подкаста.
Файлы OPML можно преобразовать в конфигурацию. Выходной файл необходимо скорректировать (отсутствует path
).
import json
import sys
import xml . etree . ElementTree as ET
def build_podcast ( node_rss ):
return {
"name" : node_rss . attrib [ "title" ],
"rss_link" : node_rss . attrib [ "xmlUrl" ],
"path" : "" ,
}
tree = ET . parse ( sys . argv [ 1 ])
podcasts = list ( map ( build_podcast , tree . findall ( "body/outline[@type='rss']" )))
result = json . dumps ({ "podcasts" : podcasts }, sort_keys = True , indent = 4 )
print ( result )
Пример использования (после сохранения как opml_converter.py
):
python opml_converter.py example.opml > podcast_downloader_config.json