Beruflich Dokumente
Kultur Dokumente
14 May 2008
Setting up your own on-demand video site doesn't have to be complicated. Upload
some videos and put them up for people to watch. Easy enough. But if you're going
to be doing a lot of videos, you'll need a way to keep them organized. This three-part
"Setting up your own on-demand video site with PHP" tutorial series will take you
through what you need to know to create video optimized for the Web, as well as
creating a PHP application that will keep your videos organized and readily
accessible. Part 1 lays the groundwork by assembling and installing the necessary
components and gathering and converting the video. Part 2 builds the basic
application using CakePHP.
Basic structure
© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 1 of 23
developerWorks® ibm.com/developerWorks
This series was written with the developer in mind. You should be comfortable
working with PHP and Web applications. You don't need to be an expert to go
through this series, but not a lot of time will be spent explaining PHP syntax and
Web application concepts. If you're unfamiliar with either, but keen to learn, feel free
to dive right in. It wouldn't hurt to have some basic understanding of digital video, but
we'll explain most of what you need to know.
Here, you build a basic application to manage the uploaded files and the tagging.
You'll be using a PHP framework — CakePHP — to help jump-start this process.
The application won't be another video-sharing site. The focus will be on managing
your own files and getting them up there for people to see.
In Part 3, you add some advanced features and create a slick user interface. We'll
smooth out the UI and look at using APIs from popular video-sharing sites to
disseminate your videos. If Part 1 is about getting up to speed, and Part 2 is about
making it all work, then Part 3 is about making it awesome.
System requirements
To work with digital video in this series, you'll need to set up a few things; the
installation of basic components won't be covered here:
Basic structure
Page 2 of 23 © Copyright IBM Corporation 1994, 2008. All rights reserved.
ibm.com/developerWorks developerWorks®
software written for Windows XP. However, if you're adept at editing and
converting video on computers running other operating systems, you
certainly may do so.
• FFmpeg — An open source command-line video-conversion utility.
• Riva FLV Encoder V2.0 for Windows — A video-conversion utility for
Windows that is no-cost for the first 30 days of use.
• Wikipedia for .flv conversion programs for other platforms.
• Red5 — A Java™-based open source Flash server.
• JW FLV Media Player V3.15 — A small embeddable Flash media player.
For the Web application, you'll also need the following:
Basic structure
© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 3 of 23
developerWorks® ibm.com/developerWorks
In Part 1, you created some tables. Users, videos, and tags are the ones we care
about right now. For each of those tables, we'll need to set up a model in Cake.
Each of the tables is related in some way: The videos table, for example, has a
user_id field that tells us who uploaded the video. There is also a join table to
allow us to tag specific videos. To make the best use of Cake, we need to define
these relationships, called model associations, in the models.
The user model, app/models/user.php, is very simple. All you need to do is specify
the name of the model and define the hasMany relationship between users and
videos, as shown below.
Likewise, the video model, app/models/video.php, needs to specify its name and its
belongsTo relationship to Users. In addition to that, you need to specify the
hasAndBelongsToMany relationship with tags. Each video can have many tags,
and each individual tag can be applied to many videos.
Basic structure
Page 4 of 23 © Copyright IBM Corporation 1994, 2008. All rights reserved.
ibm.com/developerWorks developerWorks®
)
);
}
Finally, you need to do your tag model, app/models/tag.php, specifying the name
and the hasAndBelongsToMany relationship to videos.
Now that your models are in place, we can use the Bake code generator to get a leg
up.
Whenever you're running Bake, you need to be in your app directory. So if you
installed CakePHP into a directory called /cakephp, you need to cd into
/cakephp/app before starting. The following steps get a little easier if you add your
cake/console directory to your system PATH, but the instructions assume you
haven't done this.
Start out by baking your controllers. You can bake them without a lot of interactivity if
you tell Bake what you are doing at invocation. For example, from your app
directory, if you wanted to bake your users controller, you would run the following
command: ../cake/console/cake bake controller Users scaffold.
The Bake console will ask if you want to bake test files (say No), then you're done.
You can see the new users controller in app/controllers/users_controller.php —
basic CRUD and all. Then you can bake your videos and tags controllers.
Basic structure
© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 5 of 23
developerWorks® ibm.com/developerWorks
Before you go too far, though, let's make one small change. Right now, if you go to
http://[yourdomain], you get a page that tells you about how Cake is installed and
running. Let's change it so that when you load the domain, you get routed to the
videos controller, calling the index action, so you will default to seeing the list of
videos. Do this by editing the app/config/routes.php file.
Now that you've made this simple change, you're ready to dig deeper into setting up
the application.
Basic structure
Page 6 of 23 © Copyright IBM Corporation 1994, 2008. All rights reserved.
ibm.com/developerWorks developerWorks®
We've already baked an extremely simple page for adding new users, but any
publicly available application is going to require something a little more complex.
Particularly, it is bad practice to save passwords in clear text. Happily, PHP provides
a built-in md5 hashing function that, when used in conjunction with CakePHP's
Security.salt value (you set this in Part 1), will save a hashed string that can be
recalculated when the user logs in again with the correct password, but can't be
looked up directly.
Now we're ready to change the add function in the users controller. Open
app/controllers/users_controller.php and locate the add function.
Just before the user is created with $this->User->create();, insert a line that
changes the clear-text password value to the password hashed with our new
Security.salt: $this->data['User']['password'] =
md5($this->data['User']['password'] .
Configure::read('Security.salt'));.
The next step is adding a basic login function and a view to go with it. But first, we
need to find the registered user in the database.
function login() {
if ($this->data) {
$results = $this->User->findByUsername($this->data['User']['username']);
if ($results && $results['User']['password'] == md5($this->data['User']
['password'] . Configure::read('Security.salt'))) {
$this->Session->write('user', $results['User']);
$this->redirect(array('action' => 'index'), null, true);
} else {
$this->set('error', true);
}
}
}
The password check compares the hashed password in the database to a hash it
calculates from Security.salt and the value passed by the person trying to log in. If
they have the correct password, it will pass the comparison and log the user in, by
saving the user's data to a session variable.
Basic structure
© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 7 of 23
developerWorks® ibm.com/developerWorks
While we're at it, we may as well add the logout function as well.
function logout() {
$this->Session->delete('user');
$this->redirect(array('action' => 'login'), null, true);
}
The logout function always redirects to the login action, so it won't require a view
of its own. We will need to add a login view, so create app/views/users/login.ctp. All
we really need in this file for now is the code below.
<?php
if (isset($error)) {
echo('Invalid Login.');
}
?>
<p>Please log in.</p>
<?php echo $form->create('User', array('action' => 'login')); ?>
<?php
echo $form->input('username');
echo $form->input('password');
?>
<?php echo $form->end('Login');?>
<?php echo $html->link('Register', array('action' => 'add')); ?>
Before we test, it will be nice to add some visual confirmation that we've logged in
successfully to the index view we're going to be redirected to. Open the index view
(index.ctp) in the same directory and insert the following three lines directly under
the first div tag.
Now, the index view will have an exuberant personal welcome and the option to log
out ready to go for every user who successfully logs in — or rather it will, as soon as
we tell the controller what the view means by $session_user. So, it's back to the
users_controller.php to add some code to the index action. Open up the
app/controllers/users_controller.php file. Get used to bouncing endlessly between
the controllers and the views and add the next six lines to the index function.
Basic structure
Page 8 of 23 © Copyright IBM Corporation 1994, 2008. All rights reserved.
ibm.com/developerWorks developerWorks®
$user = $this->Session->read('user');
if (!$user['username']) {
$this->redirect(array('controller' => 'users', 'action'
=> 'login'), null, true);
} else {
$this->set('session_user', $user);
}
These six lines test for the user session variable. If the user's username is not set, it
deduces that you are not logged in and redirects you to the login page. If it is set, it
copies out the user session variable to a session_user variable the view can
use. Those six lines, in combination with the three from the view, can be used
anywhere you want to prevent Web surfers from browsing a page unless they are
logged in.
To make sure the work we've done so far is solid, we're going to register ourselves a
new user and log in. Point a browser to the users index in your application. It
should be http://[your domain]/users/index. Because you are not logged in, and we
now require Web surfers to be logged in to view the index page, you should have
been summarily bounced off to the login page. The screen should look something
like Figure 1.
We don't have a user yet, so click on the Register link at the bottom of the page.
This will take you to the old add action we've modified to use md5 and the
Security.salt.
Basic structure
© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 9 of 23
developerWorks® ibm.com/developerWorks
In the example, we have entered the password video for the user. When we
submit, we get tossed back to the login screen with a message that our user has
been saved. When we log in with our new user's username and password, we are
finally allowed access to the user index, where we see two things of note: Our
exuberant personal welcome is working as described, and our password looks like
hexadecimal gibberish in the database.
Basic structure
Page 10 of 23 © Copyright IBM Corporation 1994, 2008. All rights reserved.
ibm.com/developerWorks developerWorks®
go away entirely. E-mail should probably go with them (or be modified significantly),
and edit and delete actions should only show up for your own account. We
should also change the actions available at the bottom of the page. We will see how
to accomplish tasks like these editing the videos views and controller. We will restrict
the edit and delete functions to the video's owner only, and adding a My Videos
page.
Another issue that needs to be addressed is the edit user action. If left unaltered,
the password automatically stuffed in view's password control will be the hash we
created. A couple things have to happen before the edit-users page can be used.
First, we have to check to make sure that the user they are trying to edit is the
account they are logged in to, just like we did with the video delete action. Second,
we need to stop the hashed password from being sent to the control if the user is
coming in to the page without having made any changes. This is handled in Listing
10.
Listing 10. Stopping the hashed password from being sent to the control
if (empty($this->data)) {
$this->data = $this->User->read(null, $id);
unset($this->data['User']['password']);
}
The data is read from the user table, then we unset (or destroy) the password key.
Next, if user-submitted data exists, we have to deal with the hashed password
business. That is done here, just before the data is saved.
if (!empty($this->data['User']['password'])) {
$this->data['User']['password'] = md5($this->data['User']['password'] .
Configure::read('Security.salt'));
} else {
unset($this->data['User']['password']);
}
If the password isn't empty, the user has entered something into the box we blanked,
so we should probably hash it and save it. If the password is empty, we assume the
user meant not to change the current settings and unset the password variable from
our incoming data. That will prevent the existing password's value from being
changed when we save the new user data to the database.
Basic structure
© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 11 of 23
developerWorks® ibm.com/developerWorks
users can't edit and delete each other's videos. This means not just hiding delete
links but making sure the user owns the video being edited or deleted.
In the videos controller, the index function should look something like Listing 12.
function index() {
$this->Video->recursive = 0;
$this->set('videos', $this->paginate());
//add the next six lines, everywhere user data is required
$user = $this->Session->read('user');
if (!$user['username']) {
$this->redirect(array('controller' => 'users', 'action' =>
'login'), null, true);
} else {
$this->set('session_user', $user);
}
}
Look familiar? It should. It's largely the same as the index function in the users
controller. The only difference is that it's pulling out videos to make a table out of —
instead of — users. It is important to note in this case that the redirect for Web
surfers who are not logged in specifies the users controller. Otherwise, it would look
for a login action in the current controller, and there isn't one.
Now we need to disallow users from editing or deleting videos that aren't theirs. Of
course, we want to remove those action links from the videos index page for all
videos belonging to a different user, but that's not enough. We also need to test for
ownership at the action itself. Since we're already looking at the videos controller,
let's start with the delete action. Before we change anything, it looks like Listing 13.
This will allow anybody to delete any video if they supply a valid video ID. We need
to pull both the user's logged-in ID and the video owner's ID, and see if they match
before we allow any of this to happen. The user's ID is conveniently stashed away in
a session variable, and all we have to do to get the information on this particular
video out of the database, including the owner's user ID, is sift through the one line
of results we get from calling $this->Video->read with the $id parameter.
Basic structure
Page 12 of 23 © Copyright IBM Corporation 1994, 2008. All rights reserved.
ibm.com/developerWorks developerWorks®
Using the model's read method, we're telling it to find the video by the ID passed in
the delete function parameter, and it only took one line from us.
Now, with the retrieval of the user-session variable, an if statement, and a rather
off-putting cavemanesque error message, if it's not the right person asking, we've
restricted deletion only to the creators. Later, we will probably want to add
application administrators to the list. But that's for later. Now we should really take
out those edit and delete links on all the videos that aren't ours. That sort of thing
happens in the view. For this one, open up app/views/videos/index.ctp. The three
lines that make the View, Edit, and Delete links, look like Listing 15.
Should you happen to have the application open in a browser, notice where those
URLs would take you. For instance: The view function on video ID No. 3 would go
to: http://[yourdomain]/videos/view/3.
Seeing that, it wouldn't take a genius to deduce how the URLs work for edit and
delete. That's why it's so important to verify ownership in your controller before you
let the public rampage all over your application. But we digress.
We've found the three video-specific action lines in the index view. Now we just have
to take two of them out if you're not the owner of that particular line. We have
Basic structure
© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 13 of 23
developerWorks® ibm.com/developerWorks
already added the six lines of code that test for logged-in-ed-ness to the index
action, and one of those lines passes the user-session variable to the view as
$session_user. Once we add in our user identity test, the action links look like
Listing 16.
Listing 16. The action links after adding the user-identity test
Now you have disallowed your users from deleting each other's videos and have let
them know that it's something they shouldn't be trying anyway. Now would be an
excellent time to modify your video controller's edit action to behave the same way
that delete does. After all, taking away the links really isn't enough. We've seen the
links are easy to figure out. You need to verify in your controller that the right user is
requesting the action.
Now the actual uploading happens without much of a fuss — until we get to the
videos controller, that is. In the add action, we need to actually write our file
Basic structure
Page 14 of 23 © Copyright IBM Corporation 1994, 2008. All rights reserved.
ibm.com/developerWorks developerWorks®
somewhere, and make sure the related data we save to the database is still
something the database can handle and that it includes the location of our new file
so we can find it later.
$new_video = $this->data['Video']['location'];
//name, type, tmp_name, error, size
$this->data['Video']['location'] = 'files/' . $user['id'] .
'_' . $new_video['name'];
if (is_uploaded_file($new_video['tmp_name'])) {
if (move_uploaded_file($new_video['tmp_name'], WWW_ROOT .
$this->data['Video']['location'])){
//nothing went wrong! Your file has been saved.
// Save data to the database as usual
}
}
The $new_video variable now holds the information about the file we've just
uploaded, $this->data['Video']['location'] holds the path to our new file,
and if the new video really did upload without a hitch, we move it to its snug new
location right off the WWW_ROOT as described in
$this->data['Video']['location'].
We should probably also tell you that this uploading function should never be used
by anyone. Well, not as it is, anyway. Any time you allow people to put files on your
server, you should make sure they are clean, of the MIME type you expected (check
the type variable), and sporting a proper file extension. Additionally, your PHP
settings can easily get in the way of this working. Particularly, the
max_execution_time and upload_max_filesize settings can cause the script
to quietly stop running without a single informative complaint for your users. If you
can't get at your PHP settings to change them, it is possible to get around these
settings on the fly by using the ini_set function.
You've come a long way. You're able to upload your videos now, and they are being
Basic structure
© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 15 of 23
developerWorks® ibm.com/developerWorks
stored in a place that's Web-accessible. It's time to pull it all together by integrating
the OS FLV libraries and CakePHP. Once that's done, you can start playing your
videos.
AC_RunActiveContent.js
Firefox codegenerator.html
demo.php
flash/flash.php
flash/getid3.lib.php
flash/getid3.php
flash/module.audio-video.flv.php
flash/module.audio-video.swf.php
flash/module.tag.apetag.php
flash/module.tag.id3v1.php
flash/module.tag.id3v2.php
flash/module.tag.lyrics3.php
flash/write.apetag.php
flash/write.id3v1.php
flash/write.id3v2.php
player.fla
player.swf
player8.fla
player8.swf
rac.js
video.flv
You don't need all these files in order to use OS FLV. Some are for demo purposes,
others are tools. Start by putting the contents of the flash directory into the
app/vendors directory. This will allow you to load the needed libraries by using
CakePHP's vendors function. For example, to load the basic OS FLV libraries, you
would use App::import('Vendor', 'flash');.
You're going to need to add this to the view action for the videos controller, so you
can call the library from the View Video page. This will allow you to use OS FLV's
basic library to include the right JavaScript for the right browser. Unfortunately, we
can't use the simple flvheader() and flv() functions that are a part of OS FLV
because we have a number of things in the way — mod_rewrite for one. That's
OK, though. We can write those bits of code by hand.
But we're not there yet. For the player to work as expected, we need to move around
a couple files. Specifically, we need to put the AC_RunActiveContent.js, rac.js,
player8.swf and player.swf files into a location that's Web-accessible. Copy all four
Basic structure
Page 16 of 23 © Copyright IBM Corporation 1994, 2008. All rights reserved.
ibm.com/developerWorks developerWorks®
Now that you've got your files in place, you can edit the view file for View Video
(app/views/videos/view.ctp). You need to add some code to import the right script
file by browser. Normally for OS FLV, you would use the flvheader() function, but
with us moving files around, this isn't an option. Instead, we can use the same test
OS FLV uses to make sure we're using the right JavaScript.
<?php
if(strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE'))
echo "<script src='/js/AC_RunActiveContent.js'
language='javascript'></script>";
else
echo "<script charset='ISO-8859-1' src='/js/rac.js'
language='javascript'></script>";
?>
Next, you need to call the getflvsize() function on the video we'll be playing, so
we can show it at the right size: <?php $size = getflvsize(WWW_ROOT .
$video['Video']['location']); ?>.
Finally, you need to add the embed code to pull the player and the video file
together. Listing 20 is more or less what the flv() function would output, but it's
been parameterized to fit our own needs and set to point to the correct file locations.
<script language='javascript'>
var src = '/js/player';
if(!DetectFlashVer(9, 0, 0) && DetectFlashVer(8, 0, 0))
src = '/js/player8';
AC_FL_RunContent('codebase',
'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0',
'width', <?php echo $size[0] ?>, 'height', <?php echo $size[1] ?>, 'src',
src, 'pluginspage',
'http://www.macromedia.com/go/getflashplayer', 'id', 'flvPlayer', 'allowFullScreen',
'true', 'movie', src, 'FlashVars','movie=/<?php echo
$video['Video']['location']?>&autoload=off&volume=70');
</script>
<noscript>
<object width='<?php echo $size[0] ?>' height='<?php echo $size[1] ?>'
id='flvPlayer'>
<param name='allowFullScreen' value='true'>
<param name='movie' value='/js/player.swf?movie=/
<?php echo $video['Video']['location']?>&autoload=off&volume=70'>
<embed src='/js/player.swf?movie=/<?php echo
$video['Video']['location']?>&autoload=off&volume=70'
width='<?php echo $size[0] ?>' height='<?php echo $size[1] ?>'
allowFullScreen='true'
type='application/x-shockwave-flash'>
</object>
</noscript>
Basic structure
© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 17 of 23
developerWorks® ibm.com/developerWorks
If you've plugged everything together correctly, you should be able to view and play
any videos you uploaded earlier by looking at the list of videos and clicking View.
You should see something that looks like Figure 4.
Congratulations! You've gotten your videos uploaded, and you can watch and share
them at will.
Basic structure
Page 18 of 23 © Copyright IBM Corporation 1994, 2008. All rights reserved.
ibm.com/developerWorks developerWorks®
Section 5. Summary
You've gotten quite a bit done. Users can register, log in, and look at their videos.
You can upload, view, and play videos using the OS FLV libraries That's quite a bit
of work to get done all in one go. There's ample room for improvement, certainly. But
you should have a really good start for sharing your videos.
In Part 3, we take things one step further. There's lots of video-sharing sites out
there, and being able to push your video from this central application to other sites
will be a major help. We'll look at this in particular as it relates to the recently opened
up YouTube API.
Basic structure
© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 19 of 23
developerWorks® ibm.com/developerWorks
Downloads
Description Name Size Download method
Source code os-php-ondemvideo.part2.zip
13KB HTTP
Basic structure
Page 20 of 23 © Copyright IBM Corporation 1994, 2008. All rights reserved.
ibm.com/developerWorks developerWorks®
Resources
Learn
• Get the CakePHP Manual.
• Read the five-part series "Cook up Web sites fast with CakePHP."
• Wikipedia explains Flash video encoders, which convert ordinary video to Flash
Video (FLV) format.
• PHP.net is the central resource for PHP developers.
• Check out the "Recommended PHP reading list."
• Browse all the PHP content on developerWorks.
• Expand your PHP skills by checking out IBM developerWorks' PHP project
resources.
• To listen to interesting interviews and discussions for software developers,
check out developerWorks podcasts.
• Using a database with PHP? Check out the Zend Core for IBM, a seamless,
out-of-the-box, easy-to-install PHP development and production environment
that supports IBM DB2 V9.
• Stay current with developerWorks' Technical events and webcasts.
• Check out upcoming conferences, trade shows, webcasts, and other Events
around the world that are of interest to IBM open source developers.
• Visit the developerWorks Open source zone for extensive how-to information,
tools, and project updates to help you develop with open source technologies
and use them with IBM's products.
• Watch and learn about IBM and open source technologies and product
functions with the no-cost developerWorks On demand demos.
Get products and technologies
• This series and the sample application were written using MySQL V5.0.37,
which is available from MySQL.com.
• Download the latest version of FFmpeg, a command-line, open source
video-conversion utility.
• Download a trial version of Riva FLV Encoder V2.0 for Windows, a
video-conversion utility for Windows produced by Rothenberger Global Training
Solutions.
• Download Red5, a Java-based open source Flash server.
Basic structure
© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 21 of 23
developerWorks® ibm.com/developerWorks
Katie Horn
According to her mother, Katie Horn has spent far too much of her 28 years on the
computer. She has a degree in computer science from Chapman University, after
which she has mostly enjoyed the jobs with "systems" or "engineer" somewhere in
the title. Despite a degree of proficiency in the area, she would prefer never again to
Basic structure
Page 22 of 23 © Copyright IBM Corporation 1994, 2008. All rights reserved.
ibm.com/developerWorks developerWorks®
Will Robot
Will Robot is a semi-professional dabbler in too many things. He lives with between
four and five cats and thinks anything can be improved with the addition of a laser
and at least two blinking LEDs.
Basic structure
© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 23 of 23