我有一个 播客 ,我在其中与红帽同事和各种行业专家讨论从云到 DevOps 到容器到 IoT 再到开源等主题。随着时间的推移,我已经相当简化了录音和编辑过程。然而,当涉及到实际将播客放到网上的机制时,有很多繁琐的小步骤需要精确地遵循。我相信任何阅读本文的系统管理员都已经说“你需要一个脚本!”他们完全正确。
在本文中,我将带您了解一个 Python 脚本,我编写该脚本主要是为了在播客编辑完成后自动发布播客。该脚本并非万能。我仍然需要输入特定于剧集的信息才能应用该脚本,并且我手动撰写博客文章。(我曾经使用该脚本为我的博客创建一个存根,但是操作的那部分需要足够的手动步骤,以至于它对我没有任何帮助。)尽管如此,该脚本仍然处理了很多繁琐的小步骤,否则这些步骤既耗时又容易出错。
我先警告您,这是一个相当简陋的程序,是我在几年前为我的特定工作流程编写的。您需要根据自己的需求进行定制。此外,尽管我为了本文的目的清理了一些代码,但它不包含大量输入或错误检查,并且其用户界面非常基本。
此脚本执行六件事。它
- 提供一个界面,供用户输入剧集标题、副标题和摘要;
- 从 MP3 文件获取信息(例如持续时间);
- 更新 XML 播客订阅文件;
- 将原始编辑的 MP3 文件与开头和结尾片段连接起来;
- 创建 OGG 文件版本;
- 并将 XML、MP3 和 OGG 文件上传到 Amazon S3 并使其公开。
podcast-python 脚本
podcast-python 脚本在 GitHub 上可用,如果您想下载整个脚本以便在阅读本文时参考。
在深入探讨之前,先做一些准备工作。我们将使用 boto 作为 Amazon Web Services S3 接口,我们将在其中存储使播客公开可用的文件。我们将使用 mpeg1audio 从 MP3 文件中检索元数据。最后,我们将使用 pydub 作为操作音频文件的接口,这需要您的系统上安装 ffmpeg。
您现在需要创建一个文本文件,其中包含有关您的播客整体的信息。当您添加剧集时,这不会改变。下面的示例来自我的 Cloudy Chat 播客。
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
<channel>
<title>Cloudy Chat</title>
<link>http://www.bitmasons.com</link>
<language>en-us</language>
<copyright>℗ & © 2017, Gordon Haff</copyright>
<itunes:subtitle>Industry experts talk cloud computing</itunes:subtitle>
<itunes:author>Gordon Haff</itunes:author>
<itunes:summary>Information technology today is at the explosive intersection of major trends that are fundamentally changing how we do computing and ultimately interact with the world. Longtime industry expert, pundit, and now Red Hat cloud evangelist Gordon Haff examines these changes through conversations with leading technologists and visionaries.</itunes:summary>
<description>Industry experts talk cloud computing, DevOps, IoT, containers, and more.</description>
<itunes:owner>
<itunes:name>Gordon Haff</itunes:name>
<itunes:email>REDACTED@gmail.com</itunes:email>
</itunes:owner>
<itunes:image href="http://s3.amazonaws.com/grhpodcasts/cloudychat300.jpg" />
<itunes:category text="Technology" />
<itunes:explicit>no</itunes:explicit>
然后,您需要第二个文本文件,其中包含每个现有项目(即剧集)的 XML 以及一些额外的行。如果您没有任何现有剧集,该文件将如下所示。
</channel>
</rss>
此脚本通过将标头文本与新剧集的 XML 连接起来,然后附加第二个文本文件来构建您的播客订阅文件。然后,它还将新项目添加到第二个文本文件中,以便在您添加另一个新剧集时它在那里。
该程序使用 TkInter,它是 Tcl/Tk 之上的一个薄的面向对象层,作为其 GUI。您将在其中输入您的播客标题、副标题和摘要,以及选择您将上传的 MP3 文件。它作为主程序循环运行,看起来像下面的屏幕截图
这是使用以下代码构建的。(您可能应该使用较新的 TkInter 主题小部件,但我从未更新到更漂亮的界面。)
root = Tk()
Label(root,text="Podcast Title:").grid(row=1, sticky=W)
<Some interface building code omitted>
Button(root, text='Select file...',command=open_file_dialog).grid(row=9, column=0, sticky=W)
v = StringVar()
Label(root, textvariable=v,justify=LEFT,fg="blue").grid(row=10,sticky=W)
TimestampEntry = Entry(root,width=50,borderwidth=1)TimestampEntry.grid(row=11,sticky=W)
TimestampEntry.insert(END,"Time/date (default filled in automatically from file)")
FilelengthStr = StringVar()FilelengthStr.set("Filelength (bytes):")
FilelengthLabel = Label(root,textvariable=FilelengthStr)
FilelengthLabel.grid(row=12,sticky=W)
DurationLabelStr = StringVar()
DurationLabelStr.set("Duration: ");DurationLabel = Label(root,textvariable=DurationLabelStr)DurationLabel.grid(row=13,sticky=W)
Button(root, text='Go!',command=do_stuff).grid(row=14, sticky=W)
StatusText = StringVar()StatusText.set("Status: Nothing to report")
StatusLabel=Label(root,textvariable=StatusText)StatusLabel.grid(row=15, sticky=W)
root.mainloop()
当我们选择 MP3 文件时,open_file_dialog 函数运行。此函数执行所有音频文件操作,然后通过全局变量将有关文件大小、长度和日期戳的所需信息返回到界面中的标签小部件。首先执行操作更直接,因为我们想要获取适用于我们将要上传的最终文件的元数据。此操作可能需要一分钟左右的时间,具体取决于文件大小。
然后,Go! 按钮执行发布播客所需的剩余功能,并在过程似乎成功完成时返回状态。
在完成这些初步工作之后,让我们看一下脚本执行的一些特定任务。我将主要跳过与设置目录路径和类似内容相关的内务处理细节,而专注于实际的自动化。
添加开头和结尾。节省时间:每集 5 分钟。
我们做的第一件事是备份原始文件。这是良好的做法,以防出现问题。它还为我提供了一个基本文件的副本,用于发送出去进行转录,就像我经常做的那样。
renameOriginal = FileBase + "_original" + FileExtension
shutil.copy2(filename,renameOriginal)
然后,我将 MP3 文件与开头和结尾音频连接起来。 AudioSegment 是一个 pydub 函数。
baseSegment = AudioSegment.from_mp3(filename)
introSegment = AudioSegment.from_mp3(leadIn)
outroSegment = AudioSegment.from_mp3(leadOut)
completeSegment = introSegment + baseSegment + outroSegment
completeSegment.export(filename,"mp3")
开头和结尾是我用来引导和结束播客的标准音频片段。它们由一个简短的人声片段和几秒钟的音乐组成。手动添加这些至少需要几分钟,并且可能会出现添加错误剪辑的情况。我还创建了一个 OGG 版本 的播客,我将其与 MP3 文件一起从我的博客链接到。
获取文件元数据。节省时间:每集 3 分钟。
我们现在获取文件大小、时间、日期和长度,并将所有内容转换为播客订阅源所需的格式。大小和时间戳来自标准函数。mpeg1audio 提供 MP3 文件的持续时间。
Filelength = path.getsize(filename)
FilelengthStr.set("Filelength (bytes): " + str(Filelength))
timestruc = time.gmtime(path.getmtime(filename))
TimestampEntry.delete(0,END)
TimestampEntry.insert(0,time.strftime("%a, %d %b %G %T",timestruc) + " GMT")
mp3 = mpeg1audio.MPEGAudio(filename)
DurationStr = str(mp3.duration)
DurationLabelStr.set("Duration: " + DurationStr)
构建播客订阅源 XML 文件。节省时间:每集 8 分钟。
这真的是最大的胜利。甚至不仅仅是启动文本编辑器并编辑 XML 文件所花费的时间。而是我经常在第一次尝试时就弄错了。而且,因为我经常在第一次尝试时就弄错,所以我感觉有必要在手动编辑时在上传之前通过 XML 验证器 运行该文件。
现在,为了充分披露,我应该注意到,编写的脚本没有对字符(例如 & 符号)进行任何处理,如果它们出现在订阅源中,则必须对其进行转义。由于不同的原因,如果您将诸如弯引号之类的字符剪切并粘贴到摘要编辑框中,您也可能会遇到问题。但是,总的来说,我可以放心地在 GUI 中键入请求的信息,并确信订阅源将是干净的。
# create an XML file containing contents for new </item> for iTunes
FileBase, FileExtension = path.splitext(filename)
XMLfilename = FileBase + '.xml'
MP3url = "http://s3.amazonaws.com/"+bucket_name+"/"+path.basename(filename)
inp = file(XMLfilename, 'w')
inp.write("<item>\n")
inp.write("<title>"+PodcastTitleEntry.get()+"</title>\n")
inp.write("<itunes:subtitle>"+PodcastSubtitleEntry.get()+"</itunes:subtitle>\n")
inp.write("<itunes:summary>"+PodcastSummaryText.get(1.0,END)+"</itunes:summary>\n")
inp.write("<enclosure url=\""+MP3url+"\" length=\""+str(Filelength)+"\" type=\"audio/mpeg\" />\n")
inp.write("<guid>"+MP3url+"</guid>\n")
inp.write("<pubDate>"+TimestampEntry.get()+"</pubDate>\n")
inp.write("<itunes:duration>"+DurationStr+"</itunes:duration>\n")
inp.write("<itunes:keywords>cloud</itunes:keywords>\n")
inp.write("<itunes:explicit>no</itunes:explicit>\n")
inp.write("</item>")
inp.write("")
inp.close()
#Now concatenate to make a new itunesxml.xml file
#create backup of existing iTunes XML file in case something goes kaka
iTunesBackup = path.join(theDirname,"itunesxmlbackup.xml")
shutil.copy2(iTunesFile,iTunesBackup)
#create temporary iTunes item list (to overwrite the old one later on)
outfile = file("iTunestemp.xml", 'w')
# create a new items file
with open(XMLfilename) as f:
for line in f:
outfile.write(line)
with open(iTunesItems) as f:
for line in f:
outfile.write(line)
outfile.close()
#replace the old items file with the new one
shutil.copy2("iTunestemp.xml",iTunesItems)
#now we're ready to create the new iTunes File
outfile = file(iTunesFile, 'w')
# create a new items file
with open(iTunesHeader) as f:
for line in f:
outfile.write(line)
with open(iTunesItems) as f:
for line in f:
outfile.write(line)
outfile.close()
上传到 AWS S3。节省时间:每集 5 分钟。
我们有修改后的音频文件,并且我们有订阅源文件——是时候将它们放到世界可以收听的地方了。我使用 boto 连接到 AWS S3 并上传文件。
这非常简单。您建立与 S3 的连接。在此脚本中,假定 AWS 凭证存储在您的环境中。当前版本的 boto boto3 提供了多种处理凭证的替代方法。然后上传文件并使其公开。
如果您尝试使用现有播客进行自动化,您最好为您的订阅源文件命名一个与您现有订阅源不冲突的名称,并将您的文件作为私有文件上传。这使您有机会在上线之前手动检查一切是否正常。我一开始就是这样做的。随着时间的推移,我调整了一些东西,并获得了信心,我可以一劳永逸地(大部分情况下)完成工作。
我通常仍然会快速浏览一下以确认没有问题,但是,老实说,现在出现问题的几率很小。而且,如果我要听取自己的建议,我会花时间修复我所知道的几个剩余的潜在故障——特别是验证和清理输入。
# Upload files to Amazon S3
# Change 'public-read' to 'private' if you want to manually set ACLs
conn = boto.connect_s3()
bucket = conn.get_bucket(bucket_name)
k = Key(bucket)
k.key = path.basename(filename)
k.set_contents_from_filename(filename)
k.set_canned_acl('public-read')
k.key = path.basename(iTunesFile)
k.set_contents_from_filename(iTunesFile)
k.set_canned_acl('public-read')
节省时间
那么这给我们带来了什么? 如果我合计我的估计节省时间,我每集节省 21 分钟。当然,仍然需要我几分钟,但这大部分是在文本中描述剧集,无论如何都需要这样做。即使我们为每集分配较少的 15 分钟节省时间,那也是一个很好的 1,500 分钟——25 小时——我通过花费大约一天的时间编写脚本,在我大约 100 个播客中节省的时间。
但是,老实说,我不确定即使是那个时间数字也能反映现实。繁琐、重复的任务会打乱一天的时间并消耗精力。自动化一切没有意义。但是,通常,如果您冒险自动化您经常做的事情,您不会后悔的。
1 条评论