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 | 细绳 | 不 | 最后下载 | 请参阅如果目录为空 |
podcast_extensions | 键值对 | 不 | {".mp3": "audio/mpeg"} | 请参阅文件类型过滤器 |
podcasts | 小节 | 是的 | [] | 请参阅播客子类别 |
http_headers | 键值对 | 不 | {"User-Agent": "podcast-downloader"} | 请参阅 HTTP 请求标头 |
fill_up_gaps | 布尔值 | 不 | 错误的 | 请参阅从间隙下载文件 |
download_delay | 数字 | 不 | 0 | 请参阅下载延迟 |
podcasts
段是配置文件的一部分,您可以在其中提供具有以下内容的对象数组:
财产 | 类型 | 必需的 | 默认 | 笔记 |
---|---|---|---|---|
name | 细绳 | 是的 | - | 通道名称(在记录器中使用) |
rss_link | 细绳 | 是的 | - | RSS 源的 URL |
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 Podcast 将仅使用标题下载: User-Agent: Mozilla/5.0
,而 Dua Podcast 则使用: 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
。为了更改它,您可以在冒号( :
字符)后提供新的。该脚本遵循 1989 C 标准的代码,但百分号 ( %
) 必须替换为美元符号 ( $
)。这是因为我不幸决定使用百分比字符作为代码的标记。
标准代码 | 脚本代码 | 笔记 |
---|---|---|
%Y%m%d | $Y$m$d | %publish_date% 的默认值 |
%A | $A | 添加工作日(本地语言设置) |
%x | $x | 当地日期代表。警告:在某些设置中,此处使用了/ ,因此可能会导致文件名出现问题 |
[%publish_date%] %file_name%.%file_extension%
[%publish_date%] %title%.%file_extension%
播客大多存储为*.mp3
文件。默认情况下,播客下载器仅查找它们,忽略所有其他类型。
如果您的播客支持其他类型的媒体文件,您可以指定文件过滤器。提供文件的扩展名(如.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
。
该脚本查看 RSS 文件中的所有items
节点。 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