支持播放本地MP3网页播放器

本地MP3网页播放器,支持选定文件夹

效 果

mp3.html

<!doctype html>
<html manifest="player.manifest">
  <head>
    <title>MP3 Player</title>
    <link rel="icon"  type="image/png" href="16.png">
    <style>
      body {
        background: #444;
        background-image: -webkit-gradient(
            linear,
            left top,
            right top,
            color-stop(0.8, rgb(133,133,133)),
            color-stop(1, rgb(68,68,68))
        );
        background-image: -moz-linear-gradient(
            left center,
            rgb(133,133,133) 80%,
            rgb(68,68,68) 100%
        );
        font-family: sans-serif;
        font-size: 14px;
        margin: 0;
        overflow-y: scroll;
      }
      #startup #prompt {
        width: 200px;
        background: #ddd;
        height: 20%;
        width: 30%;
        min-height: 180px;
        border-radius: 5px;
        z-index: 100;
        position: absolute;
        top: 35%;
        left: 35%;
      }
      
      #mask {
        position: absolute;
        opacity: 0.5;
        width: 100%;
        left: 0;
        top: 0;
        height: 100%;
        z-index: 4;
        background: #000;
      }
      
      #prompt input {
        margin-top: 15px;
        margin-left: 15px;
      }
      
      
      #search {
        width: 80%;
        position:fixed;
        top: 0;
        padding:0;
        margin:0;
      }
      
      
      #search input {
        width: 100%;
        height: 36px;
        font-size: 30px;
        border: none;
        padding-left: 5px;
        border-bottom: 1px solid #929292;
        outline: none;
        background: #929292;
        background-image: -webkit-gradient(
            linear,
            left top,
            left bottom,
            color-stop(0, rgb(255,255,255)),
            color-stop(1, rgb(221,221,221))
        );
        background-image: -moz-linear-gradient(
            center top,
            rgb(255,255,255) 0%,
            rgb(221,221,221) 100%
        );
        
      }
      audio {
        width: 100%;
        
      }
      
      #footer {
        position: fixed;
        bottom: -3px;
        width: 100%;
        background: #858585;
      }
      
      #songs {
        background: #ddd;
        width: 80%;
        padding-left: 5px;
      }
      
      #songs table {
        width: 100%;
      }
      
      #playlist {
        width: 20%;
        position: fixed;
        right: 0;
        color: white;
        top: 0;
      }
      
      #playlist table {
        width: 100%;
        margin: 5px;
        margin-left: 10px;
      }
      
      table tr:hover {
        background: #BBCEE9;
        border-radius: 5px;
      }
      
      table tr {
        -webkit-user-select: none;
        user-select: none;
        cursor: default;
        
      }
      table td {
        padding-top: 3px;
        padding-bottom: 3px;
        padding-right: 5px;
      }
      
      
      #playlist table tr.playing {
        /*background: #76AD8B;*/
        font-style: italic;
      }
      

      #playlist table tr.playing td {
        padding-left: 20px;
      }
      
      #listtools {
        position: fixed;
        bottom: 40px;
        right: 5px;
        color: #ddd;
      }
      #listtools a {
        color: white;
        text-decoration: none;
      }
      
      tr.hidden {
        display:none;
      }
      
    </style>
    <script src="id3v2.js"></script>
    <script>
      //beware! this code is pretty hacky and  ugly.
      /*
        This doesn't use jQuery or any js libraries, not because they aren't great
        in retrospect, I really think I should have used jQuery here.
        
        But yesterday when I started this, I was really offended by a chrome extension
        which was literally a one liner content script that had jquery as a dependency.
        
        That's just terrible.
        
        Anyway, this is more of a proof of concept and in a weird backwards way, I prefer
        to do my prototypes without jQuery.
      */
      function parseFile(file, callback){
        if(localStorage[file.name]) return callback(JSON.parse(localStorage[file.name]));
        ID3v2.parseFile(file,function(tags){
          //to not overflow localstorage
          localStorage[file.name] = JSON.stringify({
            Title: tags.Title,
            Artist: tags.Artist,
            Album: tags.Album,
            Genre: tags.Genre
          });
          callback(tags);
        })
      }
      
      function runSearch(query){
        console.log(query);
        var regex = new RegExp(query.trim().replace(/\s+/g, '.*'), 'ig');
        for(var i = $('songtable').getElementsByTagName('tr'), l = i.length; l--;){
          if(regex.test(i[l].innerHTML)){
            i[l].className = 'visible'
          }else{
            i[l].className = 'hidden';
          }
        }
      }
      
      function canPlay(type){
        var a = document.createElement('audio');
        return !!(a.canPlayType && a.canPlayType(type).replace(/no/, ''));
      }
      
      function $(id){return document.getElementById(id)}
      function getSongs(files){
        $("mask").style.display = 'none';
        $("startup").style.display = 'none';
        var queue = [];
        var mp3 = canPlay('audio/mpeg;'), ogg = canPlay('audio/ogg; codecs="vorbis"');
        for(var i = 0; i < files.length; i++){
          var file = files[i];
  
  var path = file.webkitRelativePath || file.mozFullPath || file.name;
          if (path.indexOf('.AppleDouble') != -1) {
// Meta-data folder on Apple file systems, skip
continue;
          }         
  var size = file.size || file.fileSize || 4096;
  if(size < 4095) { 
// Most probably not a real MP3
console.log(path);
continue;
  }
 
          if(file.name.indexOf('mp3') != -1){ //only does mp3 for now
            if(mp3){
              queue.push(file);
            }
          }
          if(file.name.indexOf('ogg') != -1  || file.name.indexOf('oga') != -1){
            if(ogg){
              queue.push(file);
            }
          }
        }
                                
        var process = function(){
          if(queue.length){
            
            var f = queue.shift();
            parseFile(f,function(tags){
              console.log(tags);
              var tr = document.createElement('tr');
              var t2 = guessSong(f.webkitRelativePath || f.mozFullPath || f.name); 
              //it should be innerText/contentText but its annoying.
              var td = document.createElement('td');
              td.innerHTML = tags.Title || t2.Title;
              tr.appendChild(td);
              
              var td = document.createElement('td');
              td.innerHTML = tags.Artist || t2.Artist;
              tr.appendChild(td);
              
              var td = document.createElement('td');
              td.innerHTML = tags.Album || t2.Album;
              tr.appendChild(td);
              
              var td = document.createElement('td');
              td.innerHTML = tags.Genre || "";
              tr.appendChild(td);
              tr.onclick = function(){
                var pl = document.createElement('tr');
                var st = document.createElement('td');
                st.innerHTML = tags.Title || t2.Title;
                pl.appendChild(st);
                $("playtable").appendChild(pl);
                pl.file = f;
                pl.className = 'visible';
                pl.onclick = function(e){
                  if(e && e.button == 1){
                    pl.parentNode.removeChild(pl);
                  }else{
                    var url;
                    if(window.createObjectURL){
                      url = window.createObjectURL(f)
                    }else if(window.createBlobURL){
                      url = window.createBlobURL(f)
                    }else if(window.URL && window.URL.createObjectURL){
                      url = window.URL.createObjectURL(f)
                    }else if(window.webkitURL && window.webkitURL.createObjectURL){
                      url = window.webkitURL.createObjectURL(f)
                    }
                    
                    $("player").src = url;
                    $("player").play();
                    for(var i = document.querySelectorAll('.playing'), l = i.length; l--;){
                      i[l].className = '';
                    }
                    pl.className += ' playing';
                    currentSong = pl;
                  }
                }
                if($("playtable").childNodes.length == 1) pl.onclick();
              }
              $('songtable').appendChild(tr);
              process();
            })
            var lq = queue.length;
            setTimeout(function(){
              if(queue.length == lq){
                process();
              }
            },300);
          }
        }
        process();
        
        console.log(files);
      }

      var currentSong = 0;

      function nextSong(){
        try{
          currentSong.nextSibling.onclick(); 
        }catch(e){
          currentSong = document.querySelector("#playtable tr");
          currentSong.onclick();
        }
      }
      
      function shuffle(){
        var pt = document.getElementById('playtable');
        //fisher yates shuffle. hopefully.
        for(var i = document.querySelectorAll("#playtable tr"), l = i.length;  l--;){
          var j = Math.floor(Math.random() * l);
          var jel = i[j], iel = i[l];
          var jref = jel.nextSibling, iref = iel.nextSibling;
          pt.insertBefore(jel, iref);
          pt.insertBefore(iel, jref);
        }
      }
      
      function empty(){
        var pt = document.getElementById('playtable');
        pt.innerHTML = '';
      }
      
      onload = function(){
        //with no dependencies, it should be fine to use this instead of ondomcontentloaded
        var a = document.createElement('audio');
        if(!a.canPlayType) $("support").innerHTML += "Your browser does not support HTML5 Audio<br>";
        if(!(a.canPlayType && a.canPlayType('audio/ogg; codecs="vorbis"').replace(/no/, ''))) 
          $("support").innerHTML += "Your browser does not support Ogg Vorbis Playback<br>";
        if(!(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, ''))) 
          $("support").innerHTML += "Your browser does not support MP3 Playback<br>";
        var f = document.createElement('input');
        f.type = 'file';
        if(!('multiple' in f)) $("support").innerHTML += "Your browser does not support selecting multiple files<br>";
        if(!('webkitdirectory' in f)) $("support").innerHTML += "Your browser probably does not support selecting directories<br>";
        if(window.createObjectURL){}else if(window.createBlobURL){}else if(window.URL && window.URL.createObjectURL){
        }else if(window.webkitURL && window.webkitURL.createObjectURL){}else{
          $("support").innerHTML += "Your browser probably does not support Object URLs<br>";
        }
        
        document.querySelector('#search input').onkeydown = function(e){
          if(e.keyCode == 13){
            for(var i = document.querySelectorAll('#songtable tr.visible'), l = i.length; l--;){
              i[l].onclick();
            }
          }
        }
      }
    </script>
  </head>
  <body>
    <div id="mask"></div>
    <div id="startup">
      <div id="prompt">
        <center>
          <input type="file" webkitdirectory directory multiple mozdirectory onchange="getSongs(this.files)">
        </center>
        <p style="padding-left: 15px">
           这是同类中第一款MP3播放器。在上面“选择文件”,选择你的音乐文件夹。它将索引你的硬盘上的歌曲,阅读ID3标签,离线播放歌曲。<b>HTML5真棒.</b>
        </p>
        <p style="padding-left: 15px">
          By <a href="http://antimatter15.com">http://antimatter15.com</a>
        </p>
      </div>
      <div id="support" style="position:absolute;z-index:99999;color:red;font-size:x-large"> <!-- insert cheap knockoff modernizer clone -->
      
      </div>
    </div>
    <div id="search">
      <input type="text" placeholder="filter library" spellcheck=off autocomplete=off oninput="runSearch(this.value)">
    </div>
    <div style="height: 37px"></div>
    <div id="playlist">
      <table id="playtable"></table>
    </div>
    <div id="listtools">
      <a href="javascript:shuffle()">排序</a> / 
      <a href="javascript:empty()">清除 </a>
    </div>
    <div id="songs">
      <table id="songtable" cellspacing=0 cellpadding=0>
      </table>
    </div>
    

    <div style="height: 50px"></div>
    
    <div id="footer">
      <audio onended="nextSong()" controls id="player">
    </div>
  </body>
</html>

id3v2.js

ID3v2 = {
parseStream: function(stream, onComplete){

var PICTURE_TYPES = {
"0": "Other",
"1": "32x32 pixels 'file icon' (PNG only)",
"2": "Other file icon",
"3": "Cover (front)",
"4": "Cover (back)",
"5": "Leaflet page",
"6": "Media (e.g. lable side of CD)",
"7": "Lead artist/lead performer/soloist",
"8": "Artist/performer",
"9": "Conductor",
"A": "Band/Orchestra",
"B": "Composer",
"C": "Lyricist/text writer",
"D": "Recording Location",
"E": "During recording",
"F": "During performance",
"10": "Movie/video screen capture",
"11": "A bright coloured fish", //<--- WTF?
"12": "Illustration",
"13": "Band/artist logotype",
"14": "Publisher/Studio logotype",
}

//from: http://bitbucket.org/moumar/ruby-mp3info/src/tip/lib/mp3info/id3v2.rb
//TODO: replace with something longer
var TAGS = {
"AENC": "Audio encryption",
"APIC": "Attached picture",
"COMM": "Comments",
"COMR": "Commercial frame",
"ENCR": "Encryption method registration",
"EQUA": "Equalization",
"ETCO": "Event timing codes",
"GEOB": "General encapsulated object",
"GRID": "Group identification registration",
"IPLS": "Involved people list",
"LINK": "Linked information",
"MCDI": "Music CD identifier",
"MLLT": "MPEG location lookup table",
"OWNE": "Ownership frame",
"PRIV": "Private frame",
"PCNT": "Play counter",
"POPM": "Popularimeter",
"POSS": "Position synchronisation frame",
"RBUF": "Recommended buffer size",
"RVAD": "Relative volume adjustment",
"RVRB": "Reverb",
"SYLT": "Synchronized lyric/text",
"SYTC": "Synchronized tempo codes",
"TALB": "Album",
"TBPM": "BPM",
"TCOM": "Composer",
"TCON": "Genre",
"TCOP": "Copyright message",
"TDAT": "Date",
"TDLY": "Playlist delay",
"TENC": "Encoded by",
"TEXT": "Lyricist",
"TFLT": "File type",
"TIME": "Time",
"TIT1": "Content group description",
"TIT2": "Title",
"TIT3": "Subtitle",
"TKEY": "Initial key",
"TLAN": "Language(s)",
"TLEN": "Length",
"TMED": "Media type",
"TOAL": "Original album",
"TOFN": "Original filename",
"TOLY": "Original lyricist",
"TOPE": "Original artist",
"TORY": "Original release year",
"TOWN": "File owner",
"TPE1": "Artist",
"TPE2": "Band",
"TPE3": "Conductor",
"TPE4": "Interpreted, remixed, or otherwise modified by",
"TPOS": "Part of a set",
"TPUB": "Publisher",
"TRCK": "Track number",
"TRDA": "Recording dates",
"TRSN": "Internet radio station name",
"TRSO": "Internet radio station owner",
"TSIZ": "Size",
"TSRC": "ISRC (international standard recording code)",
"TSSE": "Software/Hardware and settings used for encoding",
"TYER": "Year",
"TXXX": "User defined text information frame",
"UFID": "Unique file identifier",
"USER": "Terms of use",
"USLT": "Unsychronized lyric/text transcription",
"WCOM": "Commercial information",
"WCOP": "Copyright/Legal information",
"WOAF": "Official audio file webpage",
"WOAR": "Official artist/performer webpage",
"WOAS": "Official audio source webpage",
"WORS": "Official internet radio station homepage",
"WPAY": "Payment",
"WPUB": "Publishers official webpage",
"WXXX": "User defined URL link frame"
};

var TAG_MAPPING_2_2_to_2_3 = {
"BUF": "RBUF",
"COM": "COMM",
"CRA": "AENC",
"EQU": "EQUA",
"ETC": "ETCO",
"GEO": "GEOB",
"MCI": "MCDI",
"MLL": "MLLT",
"PIC": "APIC",
"POP": "POPM",
"REV": "RVRB",
"RVA": "RVAD",
"SLT": "SYLT",
"STC": "SYTC",
"TAL": "TALB",
"TBP": "TBPM",
"TCM": "TCOM",
"TCO": "TCON",
"TCR": "TCOP",
"TDA": "TDAT",
"TDY": "TDLY",
"TEN": "TENC",
"TFT": "TFLT",
"TIM": "TIME",
"TKE": "TKEY",
"TLA": "TLAN",
"TLE": "TLEN",
"TMT": "TMED",
"TOA": "TOPE",
"TOF": "TOFN",
"TOL": "TOLY",
"TOR": "TORY",
"TOT": "TOAL",
"TP1": "TPE1",
"TP2": "TPE2",
"TP3": "TPE3",
"TP4": "TPE4",
"TPA": "TPOS",
"TPB": "TPUB",
"TRC": "TSRC",
"TRD": "TRDA",
"TRK": "TRCK",
"TSI": "TSIZ",
"TSS": "TSSE",
"TT1": "TIT1",
"TT2": "TIT2",
"TT3": "TIT3",
"TXT": "TEXT",
"TXX": "TXXX",
"TYE": "TYER",
"UFI": "UFID",
"ULT": "USLT",
"WAF": "WOAF",
"WAR": "WOAR",
"WAS": "WOAS",
"WCM": "WCOM",
"WCP": "WCOP",
"WPB": "WPB",
"WXX": "WXXX"
};

//pulled from http://www.id3.org/id3v2-00 and changed with a simple replace
//probably should be an array instead, but thats harder to convert -_-
var ID3_2_GENRES = {
"0": "Blues",
"1": "Classic Rock",
"2": "Country",
"3": "Dance",
"4": "Disco",
"5": "Funk",
"6": "Grunge",
"7": "Hip-Hop",
"8": "Jazz",
"9": "Metal",
"10": "New Age",
"11": "Oldies",
"12": "Other",
"13": "Pop",
"14": "R&B",
"15": "Rap",
"16": "Reggae",
"17": "Rock",
"18": "Techno",
"19": "Industrial",
"20": "Alternative",
"21": "Ska",
"22": "Death Metal",
"23": "Pranks",
"24": "Soundtrack",
"25": "Euro-Techno",
"26": "Ambient",
"27": "Trip-Hop",
"28": "Vocal",
"29": "Jazz+Funk",
"30": "Fusion",
"31": "Trance",
"32": "Classical",
"33": "Instrumental",
"34": "Acid",
"35": "House",
"36": "Game",
"37": "Sound Clip",
"38": "Gospel",
"39": "Noise",
"40": "AlternRock",
"41": "Bass",
"42": "Soul",
"43": "Punk",
"44": "Space",
"45": "Meditative",
"46": "Instrumental Pop",
"47": "Instrumental Rock",
"48": "Ethnic",
"49": "Gothic",
"50": "Darkwave",
"51": "Techno-Industrial",
"52": "Electronic",
"53": "Pop-Folk",
"54": "Eurodance",
"55": "Dream",
"56": "Southern Rock",
"57": "Comedy",
"58": "Cult",
"59": "Gangsta",
"60": "Top 40",
"61": "Christian Rap",
"62": "Pop/Funk",
"63": "Jungle",
"64": "Native American",
"65": "Cabaret",
"66": "New Wave",
"67": "Psychadelic",
"68": "Rave",
"69": "Showtunes",
"70": "Trailer",
"71": "Lo-Fi",
"72": "Tribal",
"73": "Acid Punk",
"74": "Acid Jazz",
"75": "Polka",
"76": "Retro",
"77": "Musical",
"78": "Rock & Roll",
"79": "Hard Rock",
"80": "Folk",
"81": "Folk-Rock",
"82": "National Folk",
"83": "Swing",
"84": "Fast Fusion",
"85": "Bebob",
"86": "Latin",
"87": "Revival",
"88": "Celtic",
"89": "Bluegrass",
"90": "Avantgarde",
"91": "Gothic Rock",
"92": "Progressive Rock",
"93": "Psychedelic Rock",
"94": "Symphonic Rock",
"95": "Slow Rock",
"96": "Big Band",
"97": "Chorus",
"98": "Easy Listening",
"99": "Acoustic",
"100": "Humour",
"101": "Speech",
"102": "Chanson",
"103": "Opera",
"104": "Chamber Music",
"105": "Sonata",
"106": "Symphony",
"107": "Booty Bass",
"108": "Primus",
"109": "Porn Groove",
"110": "Satire",
"111": "Slow Jam",
"112": "Club",
"113": "Tango",
"114": "Samba",
"115": "Folklore",
"116": "Ballad",
"117": "Power Ballad",
"118": "Rhythmic Soul",
"119": "Freestyle",
"120": "Duet",
"121": "Punk Rock",
"122": "Drum Solo",
"123": "A capella",
"124": "Euro-House",
"125": "Dance Hall"
};

var tag = {
pictures: []
};


var max_size = Infinity;

function read(bytes, callback){
stream(bytes, callback, max_size);
}


function encode_64(input) {
var output = "", i = 0, l = input.length,
key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 
chr1, chr2, chr3, enc1, enc2, enc3, enc4;
while (i < l) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) enc3 = enc4 = 64;
else if (isNaN(chr3)) enc4 = 64;
output = output + key.charAt(enc1) + key.charAt(enc2) + key.charAt(enc3) + key.charAt(enc4);
}
return output;
}



function parseDuration(ms){
var msec = parseInt(cleanText(ms)) //leading nulls screw up parseInt
var secs = Math.floor(msec/1000);
var mins = Math.floor(secs/60);
var hours = Math.floor(mins/60);
var days = Math.floor(hours/24);

return {
milliseconds: msec%1000,
seconds: secs%60,
minutes: mins%60,
hours: hours%24,
days: days
};
}


function pad(num){
var arr = num.toString(2);
return (new Array(8-arr.length+1)).join('0') + arr;
}

function arr2int(data){
if(data.length == 4){
if(tag.revision > 3){
var size = data[0] << 0x15;
size += data[1] << 14;
size += data[2] << 7;
size += data[3];
}else{
var size = data[0] << 24;
size += data[1] << 16;
size += data[2] << 8;
size += data[3];
}
}else{
var size = data[0] << 16;
size += data[1] << 8;
size += data[2];
}
return size;
}

function parseImage(str){
var TextEncoding = str.charCodeAt(0);
str = str.substr(1);
var MimeTypePos = str.indexOf('\0');
var MimeType = str.substr(0, MimeTypePos);
str = str.substr(MimeTypePos+1);
var PictureType = str.charCodeAt(0);
var TextPictureType = PICTURE_TYPES[PictureType.toString(16).toUpperCase()];
str = str.substr(1);
var DescriptionPos = str.indexOf('\0');
var Description = str.substr(0, DescriptionPos);
str = str.substr(DescriptionPos+1);
var PictureData = str;
var Magic = PictureData.split('').map(function(e){return String.fromCharCode(e.charCodeAt(0) & 0xff)}).join('');
return {
dataURL: 'data:'+MimeType+';base64,'+encode_64(Magic),
PictureType: TextPictureType,
Description: Description,
MimeType: MimeType
};
}

function parseImage2(str){
var TextEncoding = str.charCodeAt(0);
str = str.substr(1);
var Type = str.substr(0, 3);
str = str.substr(3);

var PictureType = str.charCodeAt(0);
var TextPictureType = PICTURE_TYPES[PictureType.toString(16).toUpperCase()];

str = str.substr(1);
var DescriptionPos = str.indexOf('\0');
var Description = str.substr(0, DescriptionPos);
str = str.substr(DescriptionPos+1);
var PictureData = str;
var Magic = PictureData.split('').map(function(e){return String.fromCharCode(e.charCodeAt(0) & 0xff)}).join('');
return {
dataURL: 'data:img/'+Type+';base64,'+encode_64(Magic),
PictureType: TextPictureType,
Description: Description,
MimeType: MimeType
};
}

var TAG_HANDLERS = {
"APIC": function(size, s, a){
tag.pictures.push(parseImage(s));
},
"PIC": function(size, s, a){
tag.pictures.push(parseImage2(s));
},
"TLEN": function(size, s, a){
tag.Length = parseDuration(s);
},
"TCON": function(size, s, a){
s = cleanText(s);
if(/\([0-9]+\)/.test(s)){
var genre = ID3_2_GENRES[parseInt(s.replace(/[\(\)]/g,''))]
}else{
var genre = s;
}
tag.Genre = genre;
}
};

function read_frame(){
if(tag.revision < 3){
read(3, function(frame_id){
//console.log(frame_id)
if(/[A-Z0-9]{3}/.test(frame_id)){
var new_frame_id = TAG_MAPPING_2_2_to_2_3[frame_id.substr(0,3)];
read_frame2(frame_id, new_frame_id);
}else{
onComplete(tag);
return;
}
})
}else{
read(4, function(frame_id){
//console.log(frame_id)
if(/[A-Z0-9]{4}/.test(frame_id)){
read_frame3(frame_id);
}else{
onComplete(tag);
return;
}
})
}
}


function cleanText(str){
if(str.indexOf('http://') != 0){
var TextEncoding = str.charCodeAt(0);
str = str.substr(1);
}
//screw it i have no clue
return str.replace(/[^A-Za-z0-9\(\)\{\}\[\]\!\@\#\$\%\^\&\* \/\"\'\;\>\<\?\,\~\`\.\n\t]/g,'');
}


function read_frame3(frame_id){
read(4, function(s, size){
var intsize = arr2int(size);
read(2, function(s, flags){
flags = pad(flags[0]).concat(pad(flags[1]));
read(intsize, function(s, a){
if(typeof TAG_HANDLERS[frame_id] == 'function'){
TAG_HANDLERS[frame_id](intsize, s, a);
}else if(TAGS[frame_id]){
tag[TAGS[frame_id]] = (tag[TAGS[frame_id]]||'') + cleanText(s)
}else{
tag[frame_id] = cleanText(s)
}
read_frame();
})
})
})
}

function read_frame2(v2ID, frame_id){
read(3, function(s, size){
var intsize = arr2int(size);
read(intsize, function(s, a){
if(typeof TAG_HANDLERS[v2ID] == 'function'){
TAG_HANDLERS[v2ID](intsize, s, a);
}else if(typeof TAG_HANDLERS[frame_id] == 'function'){
TAG_HANDLERS[frame_id](intsize, s, a);
}else if(TAGS[frame_id]){
tag[TAGS[frame_id]] = (tag[TAGS[frame_id]]||'') + cleanText(s)
}else{
tag[frame_id] = cleanText(s)
}
//console.log(tag)
read_frame();
})
})
}


read(3, function(header){
if(header == "ID3"){
read(2, function(s, version){
tag.version = "ID3v2."+version[0]+'.'+version[1];
tag.revision = version[0];
//console.log('version',tag.version);
read(1, function(s, flags){
//todo: parse flags
flags = pad(flags[0]);
read(4, function(s, size){
max_size = arr2int(size);
read(0, function(){}); //signal max
read_frame()
})
})
})
}else{
onComplete(tag);
return false; //no header found
}
})
return tag;
},

parseURL: function(url, onComplete){
var xhr = new XMLHttpRequest();
xhr.open('get', url, true);
xhr.overrideMimeType('text/plain; charset=x-user-defined');

var pos = 0, 
bits_required = 0, 
handle = function(){},
maxdata = Infinity;

function read(bytes, callback, newmax){
bits_required = bytes;
handle = callback;
maxdata = newmax;
if(bytes == 0) callback('',[]);
}
var responseText = '';
(function(){
if(xhr.responseText){
responseText = xhr.responseText;
}
if(xhr.responseText.length > maxdata) xhr.abort();

if(responseText.length > pos + bits_required && bits_required){
var data = responseText.substr(pos, bits_required);
var arrdata = data.split('').map(function(e){return e.charCodeAt(0) & 0xff});
pos += bits_required;
bits_required = 0;
if(handle(data, arrdata) === false){
xhr.abort();
return;
}
}
setTimeout(arguments.callee, 0);
})()
xhr.send(null);
return [xhr, ID3v2.parseStream(read, onComplete)];
},
parseFile: function(file, onComplete){

var reader = new FileReader();

var pos = 0, 
bits_required = 0, 
handle = function(){},
maxdata = Infinity;

function read(bytes, callback, newmax){
bits_required = bytes;
handle = callback;
maxdata = newmax;
if(bytes == 0) callback('',[]);
}
var responseText = '';
reader.onload = function(){
responseText = reader.result;
};

(function(){

if(responseText.length > pos + bits_required && bits_required){
var data = responseText.substr(pos, bits_required);
var arrdata = data.split('').map(function(e){return e.charCodeAt(0) & 0xff});
pos += bits_required;
bits_required = 0;
if(handle(data, arrdata) === false){
return;
}
}
setTimeout(arguments.callee, 0);
})()
reader.readAsBinaryString(fileSlice(file, 0, 128 * 1024));
return [reader, ID3v2.parseStream(read, onComplete)];
}
}


function fileSlice(file, start, length){
  if(file.mozSlice) return file.mozSlice(start, start + length);
  if(file.webkitSlice) return file.webkitSlice(start, start + length);
  if(file.slice) return file.slice(start, length);
}


//doesnt quite belong here but doesnt quite belong elsewhere either
function guessSong(n){
var parts = unescape(n)
.replace(/^.*(music|desktop|document|video|home)/gi,'')
.replace(/\[[^\]]+\]/g,'')
.replace(/_/g,' ')
.replace(/[0-9]+\s*(kbps|\-)/,'')
.split(/[\/]/)
.reverse();

var name = parts[0]
.replace(/\.(mp3|ogg|flac)/,'')
.replace(/^\s|\s$/g,'')
.replace(/^\d+\s*/,'');

var artist='Unknown',album='Unknown';

if(/\-/.test(name)){
var np = name.split('-').reverse();
name = np[0];
if(np.length >= 2){
artist = np[1];
}
if(np.length >= 3){
artist = np[2];
album = np[1]
}
if(parts[1] && parts[2]){
var album = parts[2]
.replace(/^\s|\s$/g,'');
if(!album){
album = parts[1]
.replace(/^\s|\s$/g,'');
}
}
}else{
if(parts[1]){
var album = parts[1]
.replace(/^\s|\s$/g,'')
}
if(parts[2]){
var artist = parts[2]
.replace(/^\s|\s$/g,'')
.replace(/^\d+\s*/,'');
}
}
if(/\-/.test(album)){
var as = album.split('-');
album = as[1];
if(!artist){
artist = as[0];
}
}
return {
Title: name,
Artist: artist,
Album: album
}
}
赞赏

微信赞赏支付宝赞赏

Author: nzj88

1 thought on “支持播放本地MP3网页播放器

Comments are closed.