Best way to deliver purchased music files?
Results 1 to 9 of 9

Thread: Best way to deliver purchased music files?

  1. #1
    Senior Member
    Join Date
    Apr 2003
    Location
    Silver Lake
    Posts
    4,886

    Best way to deliver purchased music files?

    I'm hoping to put together a PHP script which will deliver music files to an authenticated user in the most convenient way possible, regardless of which device they are using. I realize this may differ widely depending on a user's OS, applications installed, etc. E.g., if a Mac user who has iTunes visits, the tune would ideally be imported into their iTunes library. A visitor with an Android phone would have the song stored in their phone's multimedia library.

    I suspect there are myriad different possibilities here between devices, OSes, applications, etc. I also expect that success will have a lot to do with specifying the correct mime types and headers. I'm wondering if anyone has dealt with this problem before and what advice you might have. E.g., should a header specify that the file is to be downloaded (and then it must subsequently be imported manually)? Or is there some way to specify headers such that it goes directly into iTunes/DoubleTwist/whatever. Is there some way to sniff out whether iTunes is installed and respond accordingly?

    Also, what should be done when a user purchases many files (e.g., 10 or more)? Should we zip them up? Give them 10 different download dialogues? Can this be accomplished with a single PHP script or would the user need to run the script for each file downloaded?

    I'm guessing that because I want to limit access to these files to authenticated users that I will not be able to make use of a CDN -- if I'm wrong please let me know. I therefore expect I will store the music files somewhere outside my server's web root. The script might look something like this:
    PHP Code:
    // validate requested file input
    if (!isset($_GET["file"]) || !preg_match("/^[a-z0-9]+$/i"$_GET["file"])){
      throw new 
    Exception("Invalid file request");
    }

    $user User::getUserBlaBlah();
    if (
    $user->authenticate() && $user->hasPermission($_GET["file"])) {
      
    $full_file_path "/path/to/music/files/" $_GET["file"];
      if (!
    is_readable()) {
        throw new 
    Exception("File not found");
      }
      
    $file_size filesize($full_file_path); 

      
    // send headers
      
    header("Cache-Control: no-cache");
      
    header("Cache-Control: no-store");
      
    header("Content-Description: File Transfer");
      
    header("Content-Disposition: attachment; filename=$file");
      
    header("Content-Type: audio/mp3"); // or audio/aac
      
    header("Content-Transfer-Encoding: binary");
      
    header("Content-Length: " $file_size);
      
    ob_clean();
      
    flush();
      
    readfile($full_file_path);
        
    } else {
      die(
    "Sorry you are not authorized to access this file");

    IMPORTANT: STOP using the mysql extension. Use mysqli or pdo instead.
    World War One happened 100 years ago. Visit Old Grey Horror for the agony and irony.

  2. #2
    Senior Member
    Join Date
    Apr 2003
    Location
    Silver Lake
    Posts
    4,886
    I'm also wondering about user authentication and how many times a given visitor should be able to download a file. Should a user always be able to download files they have purchased? Should we cap it at 10 downloads per file or something? How can I be sure that a download has been completed?
    IMPORTANT: STOP using the mysql extension. Use mysqli or pdo instead.
    World War One happened 100 years ago. Visit Old Grey Horror for the agony and irony.

  3. #3
    Senior Member
    Join Date
    Aug 2008
    Location
    London, UK
    Posts
    753
    I would assume that applications which are installed and deal with music would offer themselves in a list of applicable apps that can deal with your media file when the browser downloads it, although you'd have to force the download as plenty of browsers these days have access to codecs which allow them to play back all sorts of media types. I'm not sure how you would detect if something like iTunes was installed on a computer, short of using some Javascript to sniff the plugins to see if their browser had a plugin you recognise, but that would only really work in a few cases when there was a perfect combination of browser/app/browser plugin.

    As for media type, you can't really go wrong with mp3 for audio and mp4 for video (assuming you're offering video files?). Most systems will be able to play these as standard.

    Offering up zip files is a good idea, but I'd also stuff in a text file with the zip to explain to the user that they might need to import the media or whatever. Always assume all users are idiots and you catch the few who really are!

    Lastly, not sure how you'd track whether a file was fully downloaded or not. You could loop through a media file reading a chunk and outputting it, but if the user aborted at the last second, they wouldn't get the full download but your script may have sent it all. You could use a Flash-based front end to report back to the server once a file has been transferred (in a similar way Internet speed tests work) and then notify the server about it, but beyond that, sounds a bit tricky.
    Ashley Sheridan
    www.ashleysheridan.co.uk

  4. #4
    Senior Member
    Join Date
    Apr 2003
    Location
    Silver Lake
    Posts
    4,886
    Quote Originally Posted by Ashley Sheridan View Post
    I would assume that applications which are installed and deal with music would offer themselves in a list of applicable apps that can deal with your media file when the browser downloads it, although you'd have to force the download as plenty of browsers these days have access to codecs which allow them to play back all sorts of media types.
    The behavior wherein the browser simply plays the file with some kind of plugin would NOT be preferable. I would imagine that declaring proper headers (Content-type, Content-disposition, etc.) would force a file download and the user could do what they like with an extra step or two. Something like:
    PHP Code:
    header("Cache-Control: no-cache, must-revalidate");
    header("Expires: 0");
    header('Content-disposition: attachment; filename='.$file);
    header("Content-type: " $mime_type);
    header('Content-Transfer-Encoding: binary'); 
    header("Content-Length: ".$filesize); 
    Quote Originally Posted by Ashley Sheridan View Post
    I'm not sure how you would detect if something like iTunes was installed on a computer, short of using some Javascript to sniff the plugins to see if their browser had a plugin you recognise, but that would only really work in a few cases when there was a perfect combination of browser/app/browser plugin.
    If you visit an itunes page, it apparently knows that you don't have itunes installed:
    https://itunes.apple.com/us/album/th...ng/id386264599
    Not sure what the mechanism is, but the browser seems to know that I don't have itunes installed on this machine. On my mac, which does have iTunes, the page lacks the big banner ad for itunes. If anyone already has this reverse-engineered, I'd love to hear how it's done.

    Quote Originally Posted by Ashley Sheridan View Post
    As for media type, you can't really go wrong with mp3 for audio and mp4 for video (assuming you're offering video files?). Most systems will be able to play these as standard.
    I was mostly just wondering if people offer mp3 versus m4a files or whatever. Would this be based on the same browser sniffing that detects itunes or presented as a user option? Also, which sounds better?

    Quote Originally Posted by Ashley Sheridan View Post
    Offering up zip files is a good idea, but I'd also stuff in a text file with the zip to explain to the user that they might need to import the media or whatever. Always assume all users are idiots and you catch the few who really are!
    Zip files sound like a chore unless you just zip up your albums together in one file initially. I was rather hoping for folks to select songs a la carte.

    Quote Originally Posted by Ashley Sheridan View Post
    Lastly, not sure how you'd track whether a file was fully downloaded or not. You could loop through a media file reading a chunk and outputting it, but if the user aborted at the last second, they wouldn't get the full download but your script may have sent it all. You could use a Flash-based front end to report back to the server once a file has been transferred (in a similar way Internet speed tests work) and then notify the server about it, but beyond that, sounds a bit tricky.
    It had occurred to me to use Flash before but then this would not work on most mobile phones (certainly not iPhones). Your suggestion of chunking is a good one, but I suspect it would require something client side. Based on what I've seen from NIN and Radiohead, I'm inclined not to worry about it.
    IMPORTANT: STOP using the mysql extension. Use mysqli or pdo instead.
    World War One happened 100 years ago. Visit Old Grey Horror for the agony and irony.

  5. #5
    Pna lbh ernq guvf¿
    Join Date
    Jul 2004
    Location
    Kansas City area
    Posts
    19,429
    Quote Originally Posted by sneakyimp View Post
    If you visit an itunes page, it apparently knows that you don't have itunes installed:
    https://itunes.apple.com/us/album/th...ng/id386264599
    Not sure what the mechanism is, but the browser seems to know that I don't have itunes installed on this machine. On my mac, which does have iTunes, the page lacks the big banner ad for itunes. If anyone already has this reverse-engineered, I'd love to hear how it's done.
    Simple search of all resources loaded when visiting that page (this takes a couple of keystrokes in any decent modern-day browser, e.g. Chrome) turned up this line of JS code:
    Code:
    its.detect.itunesDetected=function itsDetectItunesDetected(){return(its.cookies.get(its.detect.ITUNES_INSTALLED_COOKIE_NAME)||(navigator.userAgent.indexOf("Macintosh")!=-1)||(its.x.isIE()&&its.detect.iTunesActiveXComponentInstalled())||(its.x.isSafari()&&(window.location.href.indexOf("volume.itunes.apple.com")>-1))||(its.x.isSafari()&&(window.location.href.indexOf("mint.itunes.apple.com")>-1))||(its.x.isSafari()&&(window.location.href.indexOf("vpp.itunes.apple.com")>-1))||((its.x.isFirefox()||its.x.isSafari()||its.x.isChrome())&&its.detect.iTunesMozillaPluginDetected()))
    (taken from https://itunes.apple.com/htmlResourc...ont-preview.js line 2049)

    EDIT: Slightly more readable version:
    Code:
    its.detect.itunesDetected = function itsDetectItunesDetected() {
        return (
                   its.cookies.get(its.detect.ITUNES_INSTALLED_COOKIE_NAME)
                || ( navigator.userAgent.indexOf("Macintosh") != -1)
                || ( its.x.isIE() && its.detect.iTunesActiveXComponentInstalled())
                || ( its.x.isSafari() && (window.location.href.indexOf("volume.itunes.apple.com") > -1))
                || ( its.x.isSafari() && (window.location.href.indexOf("mint.itunes.apple.com") > -1))
                || ( its.x.isSafari() && (window.location.href.indexOf("vpp.itunes.apple.com") > -1))
                || ( ( its.x.isFirefox() || its.x.isSafari() || its.x.isChrome() )
                    && its.detect.iTunesMozillaPluginDetected()
                   )
               )
    };
    Last edited by bradgrafelman; 10-17-2012 at 09:45 PM.

  6. #6
    Senior Member
    Join Date
    Jul 2007
    Posts
    3,658
    If you want to have a look at a real world implementation dealing with nothing but music, check out http://www.beatport.com. It's been a while since I bought music there, so I don't remember all different things they handle (such as possibly zipping multiple files), but it does handle lots of other nice features such as samples of all songs, possibility to create lists of samples (from albums, artists, searches), downloadable content in various bitrates and even non-compressed formats.

    I don't remember how user-friendly the site was. If it isn't, it might be safe to assume that beatport's users are willing to spend time on learning how to use it to get both the particular music styles it provides as well as the sample previews. This may differ from "ordinary" use cases. But it's still comprises a good set of features in my opinion.

    As for the question of limited download I completely agree with the view I percieve Good Old Games to have: DRM-free + unlimited downloads = hassle free and user friendly => success. Last Heroes of Might and Magic game I bought required me to be connected to the internet to play my game and that means it's the last time I bought a game from them, whomever it was (Steam?). I'd never buy something from iTunes store either, or whatever they call it, since I want a moveable mp3 which I can put on any device I choose, just like I could put my old tapes and vinyl records in any matching playback device I chose.

  7. #7
    Senior Member Derokorian's Avatar
    Join Date
    Apr 2011
    Location
    Denver
    Posts
    1,784
    Zip files sound like a chore unless you just zip up your albums together in one file initially. I was rather hoping for folks to select songs a la carte.
    Most of the music I've purchased online, unless delivered by an application (such as iTunes or AmazonMP3), was delivered by downloading a zip file. Its not terribly complicated and PHP actually has the capabilities built in. See http://php.net/manual/en/book.zip.php
    Sadly, nobody codes for anyone on this forum. People taste your dishes and tell you what is missing, but they don't cook for you. ~anoopmail
    I'd rather be a comma, then a full stop.
    User Authentication in PHP with MySQLi - Don't forget to mark threads resolved - MySQL(i) warning

  8. #8
    Senior Member
    Join Date
    Apr 2003
    Location
    Silver Lake
    Posts
    4,886
    Thanks for the input, guys. I've seen the PHP zip library in action and it seems extremely easy to use. I was more thinking that it might be a real CPU and disk I/O drain on a busy server. I expect I might zip entire albums together in advance.

    Johana, I think you are correct about DRM being a hassle -- not only is it more coding effort but it's likely to bother customers.
    IMPORTANT: STOP using the mysql extension. Use mysqli or pdo instead.
    World War One happened 100 years ago. Visit Old Grey Horror for the agony and irony.

  9. #9
    Senior Member Derokorian's Avatar
    Join Date
    Apr 2011
    Location
    Denver
    Posts
    1,784
    I located an old function I wrote to add files/directories to a zip file. This is run on an as needed basis and even when I create large zip files it is fast as heck. You could do something like it to zip files and deliver them on the fly. Maybe from a db result, that has the file paths to all the files the person needs to download, just loop through and call it multiple times. I use this approach, combined with a download identifier, which creates a mapping to the zip file for that specific combination of files, that way if I need to create the same collection of data, I can just use an already existent file. I have a task that cleans up files that haven't been accessed in 72 hours, and removes their mapping from the database.

    I don't have actual benchmarking for this function of any kind, but I haven't noticed any slow downs on the server or wait times when requesting a download. Hope this helps.

    PHP Code:
    function addToZip($path,ZipArchive $zip,$reset FALSE) {
       static 
    $root;
       
       if( 
    $reset ) {
          
    $root realpath($path) . DIRECTORY_SEPARATOR;
       }
       
       if( 
    is_file($path) ) {
          
    $file str_replace($root,'',$path);
          if( !
    $zip->addFile($path,$file) ) {
             
    trigger_error('Failed adding '.$path.' to zip file.'E_USER_WARNING);
          }
       } elseif( 
    is_dir($path) ) {
          
    $dir realpath($path) . DIRECTORY_SEPARATOR;
          
    $dir glob($dir.'*');
          
    $dir array_filter($dir);
          
    usort($dir,function($a,$b) {
             if( 
    is_dir($a) && is_file($b) ) return -1;
             if( 
    is_file($a) && is_dir($b) ) return 1;
             return 
    strcasecmp($a,$b); 
          });
          
          foreach( 
    $dir as $sub ) {
             
    addToZip($sub,$zip);
          }
       } else {
          return 
    FALSE;
       }

    Edit: Also please, stick to your idea of NOT using DRM. When I come across DRM protected files, I refuse to use the purveyor which I obtained the files from again.
    Sadly, nobody codes for anyone on this forum. People taste your dishes and tell you what is missing, but they don't cook for you. ~anoopmail
    I'd rather be a comma, then a full stop.
    User Authentication in PHP with MySQLi - Don't forget to mark threads resolved - MySQL(i) warning

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •