播客网站使用动态页面

tech2023-09-19  104

播客网站使用动态页面

In this article, I’ll demonstrate how to use PHP to generate a podcast feed. We’ll create a simple administrative interface to configure the podcast metadata, add and list episodes, and then go through the generation of the podcast feed itself (which is simply an RSS document).

在本文中,我将演示如何使用PHP生成播客feed。 我们将创建一个简单的管理界面来配置播客元数据,添加和列出剧集,然后逐步完成播客feed本身的生成(这只是一个RSS文档)。

I’ll base the application on this skeleton Slim app, though many of the principles will remain the same whatever framework you choose. You can download the application itself from GitHub or work through the tutorial and build it as you go along.

我将基于此Slim框架应用程序创建应用程序 ,尽管无论选择哪种框架,许多原理都将保持不变。 您可以从GitHub 下载应用程序本身,也可以按照本教程进行操作并逐步构建它。

Ready? Let’s get started!

准备? 让我们开始吧!

设置应用 (Setting up the Application)

The Slim skeleton app contains the basic set up for a web application powered by Slim, and also pulls in NotORM for querying databases and Twig for working with templates. We’ll also use the getID3 library to work with the audio files’ metadata, so open composer.json file and add the following require:

Slim骨架应用程序包含针对由Slim支持的Web应用程序的基本设置,还引入了NotORM(用于查询数据库)和Twig(用于处理模板)。 我们还将使用getID3库处理音频文件的元数据,因此打开composer.json文件并添加以下要求:

"nass600/get-id3": "dev-master"

Run composer.phar install and the application’s dependencies will be downloaded into a vendor directory.

运行composer.phar install ,应用程序的依赖项将下载到vendor目录中。

Create the directories data and public/uploads and ensure they are both writeable by the web server. The data directory will store some additional application configuration details, and public/uploads will hold our podcast audio uploads.

创建目录data和public/uploads并确保它们都可被Web服务器写入。 data目录将存储一些其他应用程序配置详细信息,而public/uploads将保存我们的播客音频上载。

I’m going to use MySQL for storing the application’s data, but you can choose whichever RDBMS you feel comfortable with. Initially the database only needs to store episode information, so our schema will be rather simple:

我将使用MySQL来存储应用程序的数据,但是您可以选择自己喜欢的任何RDBMS。 最初,数据库仅需要存储情节信息,因此我们的模式将非常简单:

CREATE TABLE episodes ( id INTEGER NOT NULL AUTO_INCREMENT, title VARCHAR(255) NOT NULL, author VARCHAR(255) NOT NULL, summary TEXT NULL, description TEXT NULL, audio_file VARCHAR(255) NOT NULL, created INTEGER NOT NULL, PRIMARY KEY (id) );

Copy the file config/config.php.example to config/config.php and update it with your database connection credentials. Also, it’s a good idea to add the data and public/uploads directories as configuration entries so we can reference them elsewhere in our code without hardcoding them.

将文件config/config.php.example复制到config/config.php并使用数据库连接凭据对其进行更新。 另外,将data和public/uploads目录添加为配置条目也是一个好主意,这样我们就可以在代码中的其他地方引用它们,而无需对其进行硬编码。

配置页面 (The Configuration Page)

Now we can create a page that will be used to configure the podcast feed itself. A feed incorporates a bunch of metadata such as a title, category information, the owner, and a summary of what it’s about. I’ll keep things as simple as possible and the configuration interface will ultimately be just a form which saves a serialized representation of the configuration to a file.

现在,我们可以创建一个页面,该页面将用于配置播客feed本身。 提要包含一堆元数据,例如标题,类别信息,所有者和其摘要。 我将使事情尽可能简单,并且配置界面最终将仅仅是一种将配置的序列化表示保存到文件中的形式。

Cut and paste the following to data/configuration.txt and make sure that the file is writeable by the web server.

将以下内容剪切并粘贴到data/configuration.txt ,并确保该文件可被Web服务器写入。

a:12:{s:5:"title";s:16:"A Sample Podcast";s:8:"language";s:2:"en";s:9:"copyright";s:14:"SitePoint 2013";s:8:"subtitle";s:16:"Podcast subtitle";s:6:"author";s:9:"SitePoint";s:7:"summary";s:31:"Generating a podcast using PHP.";s:11:"description";s:58:"This is a demonstration of generating a Podcast using PHP.";s:10:"owner_name";s:9:"SitePoint";s:11:"owner_email";s:22:"no-reply@sitepoint.com";s:10:"categories";a:4:{i:0;s:30:"Education|Education Technology";i:1;s:18:"Education|Training";i:2;s:21:"Technology|Podcasting";i:3;s:26:"Technology|Software How-To";}s:8:"keywords";s:21:"PHP,podcasts,tutorial";s:8:"explicit";s:2:"no";}

Now we’ll create a very simple class to load and save the configuration. Save the following as lib/SimpleFileConfiguration.php:

现在,我们将创建一个非常简单的类来加载和保存配置。 将以下内容另存为lib/SimpleFileConfiguration.php :

<?php class SimpleFileConfiguration { const DATA_FILE = 'configuration.txt'; public $dataFile; public function __construct(Pimple $c) { $this->dataFile = $c['config']['path.data'] . $this::DATA_FILE; } public function load() { $contents = file_get_contents($this->dataFile); return unserialize($contents); } public function save($configuration) { $contents = serialize($configuration); file_put_contents($this->dataFile, $contents); } }

Add the instantiation of SimpleFileConfiguration to include/services.php: so it’ll be easily accessible throughout the application:

将SimpleFileConfiguration的实例添加到include/services.php :这样就可以在整个应用程序中轻松访问它:

<?php $c['PodcastConfig'] = $c->share(function ($c) { return new SimpleFileConfiguration($c); }

Create the file routes/configure.php with the /configure routes:

使用/configure路由创建文件routes/configure.php :

<?php $app->get('/configure', function () use ($app, $c) { $config = $c['PodcastConfig']->load(); $app->view()->setData(array( 'configuration' => $config )); $app->render('configure.html'); }); $app->post('/configure', function () use ($app, $c) { $data = $app->request()->post(); $c['PodcastConfig']->save($data); $app->flash('success', 'Configuration Saved'); $app->redirect('/configure'); });

And finally, create the template file templates/configure.html that will present the form to update the configuration values.

最后,创建模板文件templates/configure.html ,该模板文件将显示表单以更新配置值。

<form method="post" enctype="multipart/form-data" action="/configure"> <fieldset> <legend>Details</legend> <label>Title</label> <input name="title" type="text" placeholder="Please enter a title..." value="{{ configuration.title }}" class="input-xlarge"> <label>Language</label> <select name="language"> <option value="en">English</select> <!-- You can put more languages here... --> </select> <label>Copyright</label> <input name="copyright" type="text" placeholder="Please enter copyright information..." value="{{ configuration.copyright }}" class="input-xlarge"> <label>Subtitle</label> <input name="subtitle" type="text" placeholder="Optionally enter a subtitle..." value="{{ configuration.subtitle }}" class="input-xlarge"> <label>Author</label> <input name="author" type="text" placeholder="Please enter the Podcast's author..." value="{{ configuration.author }}" class="input-xlarge"> <label>Summary</label> <textarea name="summary" cols="50" rows="2" placeholder="Please enter a summary..." class="input-xlarge">{{ configuration.summary }}</textarea> <label>Description</label> <textarea name="description" cols="50" rows="5" placeholder="Please enter a description..." class="input-xlarge">{{ configuration.description }}</textarea> </fieldset> <fieldset> <legend>Owner Details</legend> <label>Name</label> <input name="owner_name" type="text" placeholder="Please enter a the podcast owner's name..." value="{{ configuration.owner_name }}" class="input-xlarge"> <label>E-mail</label> <input name="owner_email" type="text" placeholder="Please enter a the podcast owner's e-mail..." value="{{ configuration.owner_email }}" class="input-xlarge"> </fieldset> <fieldset> <legend>Categorization</legend> <label>Categories</label> <select name="categories[]" multiple="true" class="input-xlarge"> <optgroup label="Arts"> <option value="Arts|Design">Design</option> <option value="Arts|Fashion & Beauty">Fashion & Beauty</option> <option value="Arts|Food">Food</option> <option value="Arts|Literature">Literature</option> ... </optgroup> </select> <label>Keywords</label> <textarea name="keywords" cols="50" rows="2" placeholder="Optionally enter some keywords (comma-separated)..." class="input-xlarge">{{ configuration.keywords }}</textarea> <label>Explicit content?</label> <select name="explicit"> <option value="no" {% if configuration.explicit == 'no' %}selected="selected"{% endif %}>No</select> <option value="yes" {% if configuration.explicit == 'yes' %}selected="selected"{% endif %}>Yes</select> </select> </fieldset> <div class="form-actions"> <button type="submit" class="btn btn-primary">Save Configuration</button> </div> </form>

I’ve copied the list of available podcast categories from the list defined by Apple for submission to iTunes. Some of them, such as Comedy, are self-contained categories and others have child categories. For those with children, I’ve used the pipe character as a separator in the option values.

我已经从Apple定义的列表中复制了可用播客类别的列表,以提交给iTunes。 其中一些类别(例如“喜剧”)是独立类别,而其他类别具有子类别。 对于那些有孩子的人,我在选项值中使用了竖线字符作为分隔符。

添加剧集 (Adding an Episode)

Next up we’ll create the page where we can create a new podcast episode. Let’s define the routes in routes/podcast.php:

接下来,我们将创建一个页面,您可以在其中创建一个新的播客片段。 让我们在routes/podcast.php定义路由:

<?php $app->get('/episode', function () use ($app) { $app->render('episode-add.html'); }); $app->post('/episode', function () use ($app, $c) { $db = $c['db']; $data = $app->request()->post(); $dir = $c['config']['path.uploads']; $filepath = $dir . basename($_FILES['file']['name']); move_uploaded_file($_FILES['file']['tmp_name'], $filepath); $id = $db->episodes->insert(array( 'title' => $data['title'], 'author' => $data['author'], 'summary' => $data['summary'], 'description' => $data['description'], 'audio_file' => $filepath, 'created' => time() )); $app->flash('success', 'Episode Created'); $app->redirect('/podcast'); });

I’m keeping things simple here; there’s no validation and uploading the audio file is very basic, but you get the idea. I’m also not going to go over implementing edit or delete functionality here; it’s pretty straightforward stuff that you can implement yourself later.

我在这里让事情变得简单。 没有验证,上传音频文件是非常基本的,但是您知道了。 我也不打算在这里实现编辑或删除功能。 这是非常简单的东西,您以后可以实现。

Now create the template file templates/episode-add.html with the form to add a new podcast:

现在,使用以下表单创建模板文件templates/episode-add.html ,以添加新的播客:

<form method="post" enctype="multipart/form-data" action="/episode"> <fieldset> <legend>Details</legend> <label>Title</label> <input name="title" type="text" placeholder="Please enter a title..."> <label>Author</label> <input name="author" type="text" placeholder="Please enter the author..." value=""> <label>Summary</label> <textarea name="summary" cols="50" rows="2" placeholder="Please enter a summary..."></textarea> <label>Description</label> <textarea name="description" cols="50" rows="5" placeholder="Please enter a description..."></textarea> <label>Audio File</label> <input name="file" type="file" /> <div class="form-actions"> <button type="submit" class="btn btn-primary">Add Episode</button> </div> </fieldset> </form>

列出播客节目 (Listing Podcast Episodes)

To create an overview page which lists all of the episodes in the podcast, we can grab the list of episodes from the database using NotORM and pass the result directly to the view.

要创建一个概述页面,列出所有播客中的所有情节,我们可以使用NotORM从数据库中获取情节列表,并将结果直接传递到视图。

Add the following to routes/podcast.php:

将以下内容添加到routes/podcast.php :

$app->get('/podcast', function () use ($app, $c) { $db = $c['db']; $app->view()->setData(array( 'podcast' => $db->episodes()->order('created DESC') )); $app->render('podcast.html'); });

And then create templates/podcast.html:

然后创建templates/podcast.html :

<table class="table table-bordered table-striped"> <thead> <tr> <td>Title</td> <td>Summary</td> </tr> </thead> <tbody> {% for episode in podcast %} <tr> <td>{{ episode.title }}</td> <td>{{ episode.summary }}</td> </tr> {% endfor %} </tbody> </table> {% endblock %}

We need to publish a feed to make the podcast available, which means getting our hands dirty with some XML. For that I’ll define the route /podcast.xml and use DOMDocument.

我们需要发布提要以使播客可用,这意味着要动手使用一些XML。 为此,我将定义路由/podcast.xml并使用DOMDocument 。

<?php $app->get('/podcast.xml', function () use ($app, $c) { $db = $c['db']; $conf = $c['PodcastConfig']->load(); $xml = new DOMDocument(); $root = $xml->appendChild($xml->createElement('rss')); $root->setAttribute('xmlns:itunes', 'http://www.itunes.com/dtds/podcast-1.0.dtd'); $root->setAttribute('xmlns:media', 'http://search.yahoo.com/mrss/'); $root->setAttribute('xmlns:feedburner', 'http://rssnamespace.org/feedburner/ext/1.0'); $root->setAttribute('version', '2.0'); $link = sprintf( '%s://%s/podcast', $app->request()->getScheme(), $app->request()->getHost() ); $chan = $root->appendChild($xml->createElement('channel')); $chan->appendChild($xml->createElement('title', $conf['title'])); $chan->appendChild($xml->createElement('link', $link)); $chan->appendChild($xml->createElement('generator', 'SitePoint Podcast Tutorial')); $chan->appendChild($xml->createElement('language', $conf['language'])); ... foreach ($db->episodes()->order('created ASC') as $episode) { $audioURL = sprintf( '%s://%s/uploads/%s', $app->request()->getScheme(), $app->request()->getHost(), basename($episode['audio_file']) ); $item = $chan->appendChild($xml->createElement('item')); $item->appendChild($xml->createElement('title', $episode['title'])); $item->appendChild($xml->createElement('link', $audioURL)); $item->appendChild($xml->createElement('itunes:author', $episode['title'])); $item->appendChild($xml->createElement('itunes:summary', $episode['summary'])); $item->appendChild($xml->createElement('guid', $audioURL)); $finfo = finfo_open(FILEINFO_MIME_TYPE); $enclosure = $item->appendChild($xml->createElement('enclosure')); $enclosure->setAttribute('url', $episode['audio_file']); $enclosure->setAttribute('length', filesize($episode['audio_file'])); $enclosure->setAttribute('type', finfo_file($finfo, $episode['audio_file'])); $item->appendChild($xml->createElement('pubDate', date('D, d M Y H:i:s O', $episode['created']))); $getID3 = new getID3(); $fileinfo = $getID3->analyze($episode['audio_file']); $item->appendChild($xml->createElement('itunes:duration', $fileinfo['playtime_string'])); } $xml->formatOutput = true; $res= $app->response(); $res['Content-Type'] = 'application/json'; print $xml->saveXML(); });

The highlights from the feed generation are:

提要生成的亮点是:

Following the requirements on Apple’s website, we create the XML document with the root element rss and provide the necessary channel information. I hardcoded the generator tag here; really you can set it to whatever you like.

按照Apple网站上的要求 ,我们创建根元素为rss的XML文档,并提供必要的渠道信息。 我在这里硬编码了generator标签; 确实,您可以将其设置为任何您喜欢的。

We iterate through the episodes and create an item element for each one. If we had a unique page for each episode – something pretty straightforward to set up – we could use it for the Globally Unique Identifier (GUID), but for now we’re just using the URL of the audio file itself since obviously that will be unique.

我们遍历这些情节,并为每个情节创建一个item元素。 如果每个剧集都有一个唯一的页面-设置起来非常简单-我们可以将其用于全局唯一标识符(GUID),但现在我们仅使用音频文件本身的URL,因为显然独特。

To create the enclosure element which contains the URL, file size, and MIME type of the actual audio file, we use filesize() and the Fileinfo extension to get the MIME type.

为了创建包含实际音频文件的URL,文件大小和MIME类型的enclosure元素,我们使用filesize()和Fileinfo扩展名来获取MIME类型。

To include the duration of the audio track, we use the getID3 library.

为了包括音频轨道的持续时间,我们使用getID3库。 We finish everything off by setting the correct headers and outputting the XML.

我们通过设置正确的标头并输出XML来完成所有工作。

Navigate to /podcast.xml and you should see the XML for the podcast feed. Run it through a few feed validators (tools.forret.com/podcast/validator.php, castfeedvalidator.com and feedvalidator.org) for good measure, and then you’re ready to submit it to iTunes!

导航到/podcast.xml ,您应该看到播客feed的XML。 通过一些饲料验证(运行tools.forret.com/podcast/validator.php , castfeedvalidator.com和feedvalidator.org )良好的措施,然后你就可以将其提交到iTunes !

摘要 (Summary)

In this article, I’ve shown how you can build a simple application to create and publish your own podcasts. There are a number of things missing from this implementation, such as editing and deleting episodes, proper validation and security, etc. They fall outside the scope of this article but are simple to add. Feel free to from GitHub and code away!

在本文中,我展示了如何构建一个简单的应用程序来创建和发布自己的播客。 此实现中缺少许多内容,例如编辑和删除剧集,适当的验证和安全性等。它们不在本文的讨论范围之内,但添加起来很简单。 欢迎从GitHub进行编码!

Image via Fotolia

图片来自Fotolia

翻译自: https://www.sitepoint.com/create-a-podcast-feed-with-php/

播客网站使用动态页面

最新回复(0)