A few weeks ago, it dawned on me that I could not use the usual .htaccess method of checking the referrer to prevent hotlinking to my .WMV video files, of which I have a substantial number. The reason for this is that using the .htaccess method prevents Windows Media Player from getting the file should WMP be invoked by Internet Explorer when the link to the file is clicked. This occurs on my Windows XP system with IE6. Why? Because Windows Media Player does not send any referrer information, which is what the .htaccess method relies on.
(There is more information regarding how WMP/IE6 behaves in this thread:
http://www.lunarforums.com/forum/index.php?topic=22528.msg173975#msg173975 )
Since I couldn't use .htaccess to prevent hotlinking, and a quick search of the 'net didn't turn up anything, I decided to see if I could write something in PHP to take care of this little problem.
I have succeeded. A couple of people asked if would share how I did it. Not a problem, I like to share things. But, be warned, this is a kludge of the first order. It's not perfect, but it will do the job. I should also mention that I have been messing with PHP only since last June (2004), so there may be better ways to do some of the stuff I'm doing. However, I have been programming off and on since the mid '80's.
The Overview (or, what the heck am I doing, and why?)
I knew that PHP had functions to read in files and spit them out to a waiting browser. I also knew that PHP does not care what is in a .htaccess file. Armed with those two bits of information, I knew I could devise a routine that could read in the file and send it out, and that I could use a .htaccess file containing "deny from all" in the folder containing the video files to keep others out. What I also needed was a way to track who was doing what on my site. That's where MySQL comes in.
When a user hits my home page, a PHP routine checks to see if a non-persistant cookie containing a session ID has been set. If not, it generates a unique session ID and sticks it into a database record, along with the name of the page that is being sent, as well as the current time, twice, once as the start time, and once as the current time. At the same time, a non-persistant cookie is set containing the generated sessionID. A redirect is then done to a page that checks to see if the cookie exists. If it does, another redirect is done back to the home page. If not, an error page is displayed.
At the top of all my other pages is another PHP routine that grabs the session ID from the cookie and checks to see if it is already in the database. If it is not, then a redirection is done to the home page (Hey! I've got my own way to force people to enter through the front door!) If the session ID is known, then the current time is updated, as well as the name of the page being sent, and the page continues to load.
When a user clicks on a link to a video file on my video index page, the link goes to another PHP file that does the work. Passed along to the video viewing page is the video file number, not the video file name. The information regarding the video file is contained in a database table. This give me some flexibility and ease of coding, and also hides the video file name.
When the video viewing page runs, it first checks to see if the session ID is known, redirecting to the home page if not. If it is known, then it updates the users record with the current time and the name of the file requested. It then gets the information for the requested file from the database, checks to see if the file exists, and if so, sends a few headers followed by the file data itself. The trick here is that it uses PHP to read in the file, and send the data to the waiting browser.
Now for the interesting part. When IE6 sees that it is a WMV file arriving, it stops the transfer and passes the URL to Windows Media Player. Windows Media Player then does an HTTP GET for that URL, which is the video viewing file and video number, not the video file itself. The video viewing file does the same checks again, but because it does not care about the referrer information, the file will again be sent using PHP file read and send functions.
If someone bookmarks the URL that points directly to the video viewing file, along with the video number, when they eventually come back to it, the video viewing file will not find them in the database, and will redirect them to the home page. If a web site includes a link directly to the same URL, anyone clicking on that link will not be known either, and will be redirected to the home page.
A cron job runs another PHP file that cleans up the user tracking table. It looks for records that are older than 30 minutes (the aging time can be changed) and deletes them.
The Details (or, great. So, just how do I do this magic anyway?)
First up, the tables needed to do this. Two of them, in fact.
The first table stores the information about your video files. I've called this table "gallery_video" on my system.
gallery_video
VideoID int(2) Unsigned Primary Index
VideoActive char(3)
VideoTitle varchar(255)
VideoDate Varchar(255)
VideoFile varchar(255)
VideoTime varchar(5)
VideoSize varchar(6)
VideoViews int(5) unsigned
An explanation of each field:
VideoID: The ID number of each video.
VideoActive: If you want to remove a video from the server, but leave its information in the table, this can be set to "no". Set to "yes" otherwise.
VideoTitle: User friendly name for the video.
VideoDate: User friendly date for the video. Useful if the file date is not the date you wish users to see.
VideoFile: The actual video file name, without the extension.
VideoTime: User friendly video length in min:sec (00:00)
VideoSize: User friendly video size (I use 00.0mb)
VideoViews: Number of times video has been viewed. (A nice byproduct of this whole thing.)
The second table stores the information about each visitor. I've called this table "user_session"
user_session
SessionID varchar(255) Primary Index
UserIP varchar(15)
UserHost Varchar(255)
UserStart datetime
UserCurrent datetime
UserPage varchar(255)
An explanation of each field:
SessionID: A unique alphanumeric id code for each user.
UserIP: The users IP address.
UserHost: The users host name. Not used in this example, but could be useful.
UserStart: The date and time the user first entered the site.
UserCurrent: The date and time the user last requested a page or file.
UserPage: The page or file the user last requested.
Now for the folder and file layout. This layout is for this example. If you wish to use a different layout, by all means do so. Just remember to adjust the paths that are referenced within the files themselves.
File/Folder layout
/(root)
connect.php
/crons
sessionmaint.php
/public_html
index.php
entry.php
/pages
video.php
viewvideo.php
/video
/thumbs
WMV video files are stored in the /video folder. Thumbnails for the video files are stored in the /thumbs folder. In this example, they must be JPG files and have the same basename as the WMV file.
Continued next message...
(Edit: updated link to other thread.)