# Fur Affinity Streaming Tool - Code unleashed



## ilobmirt (Jan 31, 2010)

This simple 2 file coding project was originally developed from my desire for FA to have a streaming music service (much like LAST.FM) The project was different from many of my earlier works in that its content is entirely based upon html and client-side scripting versus having the user download and run a binary.

Perhaps someday, FA will add streaming music to its site. in the meantime, you can take a gander at this. . .

*faHTTP.js -* used for most client-side operations. Made HTTP requests for content. Picks a random song.

```
/* 

I, Ilobmirt Tenk Have created this along time ago. This piece of code is under >>>Creative Commons Attribution-Share Alike 3.0<<< (please see http://creativecommons.org )

If you care to discuss your thoughts, I'll be over here... ~~~> http://www.furaffinity.net/user/ilobmirt/

If you make modifications to this code, feel free to add yourself here :3

*/


//Define a simple datatype for httpResults 
function httpResponse(strHTTPText, objHTTPXML){ 
 
    this.Text = strHTTPText; 
 
    this.XML = objHTTPXML; 
 
} 
 
//Define a datatype for browsing songs 
function faSongBrowser(){ 
 
    //Is it in the middle of changing a song? 
    var blnChangingSong = false; 
 
    //check ready state 
    var objPlayerState = "dummy"; 
 
    //What kind of genre of music will this browser look for? 
    this.Genre = "all"; 
 
    //How many songs will this object grab per page? 
    this.QueryAtATime = 24; 
 
    //How many pages would this object like to read? 
    this.PagesToRead = 1; 
 
    //How many pages is this object allowed to read before it can start using its play list? 
    this.PagesReadToPlay = 1; 
 
    //How many pages has it read already? 
    this.PagesRead = 0; 
 
    //This is an array that would hold all of the song id's 
    this.SongIDList = []; 
 
    //What songs have already been played? 
    this.AlreadyPlayedSongs = []; 
 
    //What songs does the user wish to skip? 
    this.SkipList = []; 
 
    //Should this object make shure a song doesn't get played twice? 
    this.FreshList = true; 
 
    //Should the object play songs randomly, or should it play them in order? 
    this.RandomList = true; 
 
    //This is the index for SongIDList 
    this.SongIndex = 0; 
 
    //This points to the html tag designed to take audio urls 
    this.SongTagID = ""; 
 
    //This points to the html tag designed for the user image 
    this.UserImageTagID = ""; 
 
    //This points to the html tag designed for the song name and owner 
    this.SongDescTagID = ""; 
 
    //This points to  the html tag designed to provide song description 
    this.SubmissionDescTagID = ""; 
 
    //// *** //// 
 
    //This will get the ball rolling on playing some FA music... 
 
    this.fn_Config = function(strGenre, intQueryPerPage, intPagesToRead, intReadToPlay, blnFresh, blnRandom, strSongTagID, strImageTagID , strSongDescTagID , strSubmissionDescTagID){ 
 
        //Initialise inner variables... 
         
        this.Genre = strGenre; 
        this.QueryAtATime = intQueryPerPage; 
        this.PagesReadToPlay= intReadToPlay; 
        this.FreshList = blnFresh; 
        this.RandomList = blnRandom; 
        this.SongTagID = strSongTagID; 
        this.UserImageTagID = strImageTagID; 
        this.SongDescTagID = strSongDescTagID; 
        this.SubmissionDescTagID = strSubmissionDescTagID; 
     
        alert("initialized tag = \"" + this.SongTagID + "\"" ); 
 
        setInterval("window.status = 'status' + document.getElementById('faPlayer').readyState;", 100); 
         
        //setInterval(fn_HandleSongEnd, 5000) 
 
        this.fn_ReadPages(intPagesToRead); 
 
    }; 
 
    //Request a page to be read 
    this.fn_ReadPages = function(intAmount){ 
 
        this.PagesToRead += intAmount; 
 
        //Move along to the page request mechanism 
        this.fn_ListPageRequest(); 
 
    }; 
 
    //Automatic Page request for a song list 
    this.fn_ListPageRequest = function(){ 
 
        getHTTP("http://www.furaffinity.net/browse/"+ (this.PagesRead + 1) + "/", "POST", "cat=Music&atype="+ this.Genre +"&species=&gender=&perpage=" + this.QueryAtATime , this , "list"); 
 
    }; 
     
    //Automatic Page response to process a song list 
    this.fn_ListPageResponse = function(objResponse){ 
 
        //Add a list of song ID's 
 
        var searchStart = 0; 
        var searchEnd = 0; 
        var songID = ""; 
 
        //While there's a song to be loaded 
        while(objResponse.indexOf("<li id=\"id_",searchStart+1) != -1){ 
 
            searchStart = objResponse.indexOf("<li id=\"id_",searchStart+1) + 11; 
            searchEnd = objResponse.indexOf("\">",searchStart+1); 
            songID = objResponse.substring(searchStart,searchEnd); 
 
            //Add song ID to list 
            this.SongIDList[this.SongIDList.length] = songID;     
 
        }             
 
        //Yey! We finally read one page of song Id's 
        this.PagesRead++; 
 
        //Did a certain # of pages get read to start playing? 
 
        if(this.PagesRead == this.PagesReadToPlay){ 
 
            //execute song playlist function 
            this.fn_PlaySongList(); 
 
        } 
 
        //If there are more pages to read... execute more requests 
        if(this.PagesRead < this.PagesToRead) this.fn_ListPageRequest(); 
         
 
    }; 
 
    //Automatic Page request for the song itself 
    this.fn_SongPageRequest = function(){ 
 
        getHTTP("http://www.furaffinity.net/view/" + this.SongIDList[this.SongIndex] + "/", "GET", "" , this, "song"); 
 
    }; 
     
    //Automatic Page response to process the song itself 
    this.fn_SongPageResponse = function(objResponse){ 
 
        var searchStart = 0; 
        var searchEnd = 0; 
        var songURL = ""; 
        var songName = ""; 
        var songDesc = ""; 
        var userName = ""; 
        var userImage = ""; 
 
        /// Get Song URL /// 
 
        //Get the index of the embed tag 
        ////alert(objResponse); 
        searchStart = objResponse.indexOf("<embed"); 
        searchEnd = objResponse.indexOf(">",searchStart+1)+1; 
 
        //Usually, the song url can be found as a property within the first 
        //Embed element as there is just one embed tag. 
        songURL = objResponse.substring(searchStart,searchEnd); 
 
        //find the src property within the tag 
        searchStart = songURL.indexOf("flashvars=\"file=") + 16 ; 
        searchEnd = songURL.indexOf(".mp3\"",searchStart+1)+4; 
        songURL = songURL.substring(searchStart,searchEnd); 
 
        /// Get Song Name /// 
 
        searchStart = objResponse.indexOf("<td valign=\"top\" align=\"left\" width=\"70%\" class=\"cat\"") + 54; 
        searchEnd = objResponse.indexOf("</td",searchStart+1); 
        songName = objResponse.substring(searchStart,searchEnd); 
 
        searchStart = songName.indexOf("b>") + 2; 
        searchEnd = songName.indexOf("</b", searchStart+1); 
        songName = songName.substring(searchStart,searchEnd); 
 
        /// Get User Name /// 
 
        searchStart = objResponse.indexOf("<b"+">"+songName+"</b"+"> - by"); 
        searchStart = objResponse.indexOf("\">",searchStart+1)+2;  
        searchEnd = objResponse.indexOf("</a",searchStart+1); 
        userName = objResponse.substring(searchStart,searchEnd); 
 
        /// Get User Image /// 
 
        searchStart = objResponse.indexOf("<td valign=\"top\" align=\"left\" width=\"70%\" class=\"alt1\"") + 54; 
        searchEnd = objResponse.indexOf("</td",searchStart+1); 
        userImage = objResponse.substring(searchStart,searchEnd); 
         
        searchStart = userImage.indexOf("<img src=\"") + 10;         
        searchEnd = userImage.indexOf("\">",searchStart+1); 
        userImage = userImage.substring(searchStart,searchEnd); 
 
        /// Get Song Description /// 
        searchStart = objResponse.indexOf("<td valign=\"top\" align=\"left\" width=\"70%\" class=\"alt1\"") + 54; 
        searchEnd = objResponse.indexOf("</td",searchStart+1); 
        songDesc = objResponse.substring(searchStart,searchEnd); 
 
        searchStart = songDesc.indexOf("<"+"img src=\""+ userImage +"\"><"+"/a> <"+"br/><"+"br/>"); 
        searchEnd = songDesc.indexOf("<br"+" />",searchStart+1); 
        songDesc = songDesc.substring(searchStart,searchEnd); 
 
        /// Update page's html tags /// 
 
        //document.getElementById(this.SongTagID).src = songURL; 
        //document.getElementById(this.SongTagID).contentWindow.open(); 
        document.getElementById(this.SongTagID).contentDocument.body.innerHTML = "<em"+"bed src='"+songURL+"'></em"+"bed>"; 
        //document.getElementById(this.SongTagID).contentWindow.close(); 
 
        //document.getElementById(this.SongTagID).contentWindow.innerHTML = songURL; //"<em"+"bed src='"+songURL+"'></em"+"bed>"; 
 
        document.getElementById(this.UserImageTagID).src = userImage; 
        document.getElementById(this.SongDescTagID).innerHTML = songName + " - By " + userName ; 
        document.getElementById(this.SubmissionDescTagID).innerHTML = songDesc; 
 
        ///If playlist needs to be unique, add this song id to the already played list... /// 
 
        if(this.FreshList == true)this.AlreadyPlayedSongs[this.AlreadyPlayedSongs.length] = this.SongIDList[this.SongIndex]; 
 
        blnChangingSong = false; 
 
    }; 
 
    //Play a song 
    this.fn_PlaySongList = function(){ 
 
        //exit prematurely if it hasn't read enough pages yet... 
        if(this.PagesRead < this.PagesReadToPlay) return; 
 
        //Sequential or random? 
        if(this.RandomList == false){ 
         
            //Sequential 
 
            //Advance song index 
            this.SongIndex++; 
             
            if(this.SongIndex >= this.SongIDList.length ) this.SongIndex = 0; 
 
        } 
        else{ 
 
            //Random 
 
            //If we covered all the songs, clear out our already played song list. 
            if(this.AlreadyPlayedSongs.length == this.SongIDList.length){ 
 
                this.AlreadyPlayedSongs = []; 
 
            } 
 
            //Randomize song index while avoiding dupes 
            do{ 
 
                this.SongIndex = Math.floor(Math.random()*this.SongIDList.length); 
 
            }while(this.AlreadyPlayedSongs.indexOf(this.SongIDList[this.SongIndex]) != -1); 
 
 
        } 
 
        //Now that we have our index, we request the song to be played. 
        this.fn_SongPageRequest(); 
 
    }; 
 
    //When a song has stopped playing... go to the next song 
    var fn_HandleSongEnd = function(){ 
 
        alert("Song state = " + faPlayer.readyState); 
        //If song tag finished loading ang playing song... 
        if(faPlayer.readyState == "complete" && blnChangingSong == false){ 
 
            alert("changing Song"); 
            blnChangingSong = true;             
            this.fn_PlaySongList(); 
             
        } 
 
    }; 
 
    //Handle a response sent from a web page 
    this.fn_handleHTTPResponse = function(strType,objResponse){ 
 
        switch(strType){ 
 
            case "song": 
                this.fn_SongPageResponse(objResponse); 
                break; 
            case "list": 
                this.fn_ListPageResponse(objResponse); 
                break;         
            default: 
                return; 
 
        } 
 
    }; 
 
} 
 
//Allows the user to enable cross-site scripting 
//So far, it's Firefox Only 
function allowForXSS(){ 
 
    try{netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");} 
    catch(e){return;} 
 
} 
 
//Lets the user make an http request  
function getHTTP(strInputURL, strGetOrPost, strParams, returnFaPlayerObject, strObjectParams){ 
 
            var xmlHTTP = new XMLHttpRequest(); 
 
            //Let the user handle the http response 
            xmlHTTP.onreadystatechange = function(){ 
                     
                    ////alert("ready state =" + xmlHTTP.readyState); 
                //Simply get the http code when loaded 
                //alert("http state = " + xmlHTTP.readyState); 
                if(xmlHTTP.readyState==4){ 
 
                    returnFaPlayerObject.fn_handleHTTPResponse(strObjectParams,xmlHTTP.responseText); 
                    //alert("sent:\n\n" + xmlHTTP.responseText); 
 
                } 
 
            }; 
             
            try { 
 
                //allowForXSS(); 
                netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); 
                xmlHTTP.open(strGetOrPost,strInputURL,true); 
                if(strGetOrPost == "GET"){ 
 
                    xmlHTTP.send(null); 
                    //alert("HTTP GET"); 
 
                } 
                else if(strGetOrPost == "POST"){ 
 
                    //Send the proper header information along with the request 
                    xmlHTTP.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 
                    xmlHTTP.setRequestHeader("Content-length", strParams.length); 
                    xmlHTTP.setRequestHeader("Connection", "close"); 
 
                    xmlHTTP.send(strParams); 
                    //alert("http POST"); 
 
                } 
                else{ 
 
                    alert("Unrecognised Request Type: \"" + strGetOrPost + "\""); 
 
                } 
 
            } 
            catch (e) { 
 
                alert("Permission for Cross-Site Scripitng denied.") 
 
            }  
 
        }
```

*FAST.html -* Your FA streaming web app here

```
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<HTML> 
<HEAD> 
    <META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=windows-1252"> 
    <TITLE>Fur Affinity Streaming Tool</TITLE> 
    <META NAME="GENERATOR" CONTENT="OpenOffice.org 3.1  (Win32)"> 
    <META NAME="AUTHOR" CONTENT="Ilobmirt Tenk"> 
    <STYLE TYPE="text/css"> 
    <!-- 
        TD P { text-align: center } 
    --> 
    </STYLE> 
</HEAD> 
<BODY LANG="en-US" DIR="LTR"> 
<P ALIGN=CENTER><FONT FACE="Impact, sans-serif"><FONT SIZE=6 STYLE="font-size: 22pt">Fur 
Affinity Streaming Tool</FONT></FONT></P> 
 
<CENTER> 
    <TABLE WIDTH=987 BORDER=1 BORDERCOLOR="#000000" CELLPADDING=4 CELLSPACING=0 FRAME=BELOW RULES=ROWS STYLE="page-break-inside: avoid"> 
        <COLGROUP> 
            <COL WIDTH=321> 
        </COLGROUP> 
        <COLGROUP> 
            <COL WIDTH=102> 
            <COL WIDTH=102> 
            <COL WIDTH=102> 
        </COLGROUP> 
        <COLGROUP> 
            <COL WIDTH=321> 
        </COLGROUP> 
        <TR VALIGN=TOP> 
            <TD WIDTH=321 HEIGHT=38> 
                <P ALIGN=CENTER><BR> 
                </P> 
            </TD> 
            <TD WIDTH=102 BGCOLOR="#cccccc"> 
                <P> 
 
                <input id="btnStopPlay" type="button" value="--Pause--"></input> 
 
                </P> 
            </TD> 
            <TD WIDTH=102 BGCOLOR="#cccccc"> 
                <P ALIGN=CENTER><BR> 
                </P> 
            </TD> 
            <TD WIDTH=102 BGCOLOR="#cccccc"> 
                <P> 
                 
                <input id="btnNext" type="button" value=">>NEXT>>" onClick="objFASTPlayerObject.fn_PlaySongList()"></input> 
 
                </P> 
            </TD> 
            <TD WIDTH=321> 
                <P ALIGN=CENTER><BR> 
                </P> 
            </TD> 
        </TR> 
        <TR> 
            <TD COLSPAN=5 WIDTH=979 HEIGHT=95 BGCOLOR="#355e00"> 
                <P ALIGN=CENTER STYLE="background: transparent"> 
                <IMG SRC="dummy.gif" id="imgUser" ALIGN=MIDDLE WIDTH=100 HEIGHT=100 BORDER=0> 
                <FONT FACE="Arial, sans-serif"><FONT SIZE=5 id="txtSongDesc">Song 
                &ndash; By Author</FONT></FONT></P> 
            </TD> 
        </TR> 
        <TR> 
            <TD COLSPAN=5 WIDTH=979 VALIGN=TOP BGCOLOR="#94bd5e"> 
                <DIV ID="txtSubDescription"> 
                    <P>Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
                    Praesent tortor purus, facilisis quis porttitor a, convallis a 
                    mi. Duis euismod, libero ac tempus venenatis, leo orci vehicula 
                    lacus, vitae elementum ante enim id libero. Vestibulum 
                    vestibulum dignissim vulputate. In semper molestie risus, vel 
                    pharetra lectus commodo a. Duis vulputate ipsum sed augue 
                    volutpat vel aliquet justo tempus. Morbi non lacus consectetur 
                    odio bibendum elementum. Maecenas eget justo ipsum, ut porta 
                    neque. Vestibulum at dui lorem, sed posuere lectus. Nullam sed 
                    faucibus magna. Aenean sem tortor, auctor a lobortis ac, 
                    interdum vel erat. Fusce ultricies, dui at dignissim dictum, 
                    arcu elit pretium leo, ac aliquet massa felis nec elit. Praesent 
                    ligula elit, tincidunt vitae pellentesque quis, blandit vitae 
                    mi. In molestie urna sed magna varius non sollicitudin tortor 
                    vehicula. Vestibulum ante ipsum primis in faucibus orci luctus 
                    et ultrices posuere cubilia Curae; Nam pretium, mauris quis 
                    fringilla fermentum, risus erat ornare sapien, vel consectetur 
                    nulla leo at odio.</P> 
                </DIV> 
            </TD> 
        </TR> 
    </TABLE> 
</CENTER> 
<P ALIGN=CENTER><BR><BR> 
</P> 
 
<iframe id="faPlayer"></iframe> 
 
<script src="faHTTP.js"></script> 
 
<script> 
 
    var objFASTPlayerObject = new faSongBrowser(); 
     
    //function runFAPlayerNow() { 
 
        //alert("loaded"); 
        objFASTPlayerObject.fn_Config("all",24,21,1,true,true,"faPlayer","imgUser","txtSongDesc","txtSubDescription");  
 
    //} 
 
</script> 
 
</BODY> 
</HTML>

<!--- 

I, Ilobmirt Tenk Have created this along time ago. This piece of code is under >>>Creative Commons Attribution-Share Alike 3.0<<< (please see http://creativecommons.org )

If you care to discuss your thoughts, I'll be over here... ~~~> http://www.furaffinity.net/user/ilobmirt/

If you make modifications to this code, feel free to add yourself here :3

 --->
```
The code is old. I wrote this on my free time. (Like I have much of that anymore >_>) Conducting safe XSS is basically "LOL". And web browsers should really work on what the _*readyState*_ is. Other than that...yey I made something :3


----------



## yak (Feb 1, 2010)

*sigh*
Have some common courtesy people, please. From where does this notion come from that we are sitting on a bandwidth goldmine? 

I don't know how many times I must repeat myself. *FA is not your music player.*
We are not LAST.fm. The music featured on the site are for presentational purposes only; if you like it that much, download it to your PC and listen to it from there.

Why do you think we don't have audio streaming, when it's so easy to do? Why don't we feature videos, or allow for higher resolution image uploads? Or even larger filesize music? Our own internet radio station? 

Please don't make me waste time by developing active anti-leeching features, I'd prefer for that time to go into developing a better FA.


----------



## ilobmirt (Feb 1, 2010)

Sorry for the fuss yak.

I do understand the concept of bandwidth and the fact that you do not have an unlimited amount of it. I merely wanted the music producers to get a lil more love and recognition. The current method of browsing and discovering music is a bit slow. That's all.

And then again, this is primarily an image host that's been nice enough to host audio, text, and flash content. I won't argue if you don't code in features that enhance personal multimedia discovery. Don't worry about it. We understand.


----------



## yak (Feb 5, 2010)

Good. I do admit that I came off looking somewhat bitchy in my last post. 
So, ugh, I don't mind coding something to enhance personal multimedia discovery, as you put it, if you'd have only suggested it instead of going and doing it in one of the least efficient ways.


----------



## Verin Asper (Feb 5, 2010)

I would...oddly have that turned off myself if it was coded...

just way to much remixes that dont work well and techno that over laps the rare good finds


----------



## ilobmirt (Feb 5, 2010)

yak said:


> Good. I do admit that I came off looking somewhat bitchy in my last post.
> So, ugh, I don't mind coding something to enhance personal multimedia discovery, as you put it, if you'd have only suggested it instead of going and doing it in one of the least efficient ways.




Hehe. :3 I'm not the least offended Yak. It's okay.

I'd love to see something done with the current version of FA. I wish I could help, but I only have the concepts, not the technical aptitude. I am certain there are others active on these forums that'd blow me out of the water with their 1337 computer language skills. I suppose I could send a request later.

~~~*~~~

Crysix Corps - I'm sure if Fa made a system that recommended good crap instead of those overdone techno re-mixes, you'd be listening and +faving FA hosted music more.


----------

