HTML5 thingie for Spotify Web Player

asdf

Versione datata 14/12/2015. Vedi la nuova versione l'ultima versione.

// ==UserScript==
// @name        HTML5 thingie for Spotify Web Player
// @namespace   https://greasyfork.org/users/4813-swyter
// @match       https://play.spotify.com/*
// @version     2015.11.20
// @noframes
// @icon        https://i.imgur.com/CecLLKu.png
// @grant       none
// @run-at      document-start
// @description asdf
// ==/UserScript==

  / * it's random, trust me, I'm Sony! * /
  Object.defineProperty(window.Math, 'random',
  {
    configurable: false,
    writable: false,
    value: function(a) { return 0.1337; }
  });

  / * SWFobject.js faker, sneaky! * /
  Object.defineProperty(window, 'swfobject',
  {
    configurable: false,
    writable: false,
    value:
    {
      getFlashPlayerVersion: function(){ console.log("-<-< getFlashPlayerVersion =>", arguments); return { major: 11, minor: 2, release: 202 }; },
      hasFlashPlayerVersion: function(){ console.log("-<-< hasFlashPlayerVersion =>", arguments); return true; },
      embedSWF:              function(swf, parent_id, unk_a, unk_b, req_flash_ver, unk_c, properties)
      {
        console.log("-<-< embedSWF =>", arguments, arguments[9].toString(), this, properties);

        
        function get_pong(ping)
        {
           // http://crossorigin.me/http://ping-pong.spotify.nodestuff.net/64-104-120-204-164-75-214-221-224-109-28-127-73-236-239-150-88-238-177-90

           console.log("ping-pong", ping);
         
           var xhr = new XMLHttpRequest();
           xhr.open("GET", "https://crossorigin.me/http://ping-pong.spotify.nodestuff.net/" + ping.replace(/ /g,"-"), true);
           xhr.responseType = "json";
           xhr.send();
       
           // believe me, i know this sucks
           while(xhr.readyState != xhr.DONE){}
           
           console.log("pong", xhr);
           
           return xhr.response.pong.replace(/-/g," ");
         
         }
  
        //  http://dsu0uct5x2puz.cloudfront.net/mp3/f164a1770e3196f1ceac703389ea0bb828d26ed0?Expires=1450043810&Signature=-zgzKXjeioEVNAU~9UQJyjUl~rISxHryo06LF94Kg9JBZKc8hA3R2WnmHFCh58FbYNGBceQQdPIUZllHVQSnWmQmi3S2k5F11SvsuS1MmiYbg33TX2Ot26xIDgG6VfIQRe0coRnda99B8omcew55LElz1wp94Zaxg-DEat8Tk-k_&Key-Pair-Id=APKAIJ6GQLKYCDRS5HFA
        // uri :                          "mp3:/mp3/6b381db769d31beb544ba67eb7cbc3ce4fc8ab4c.mp3?Expires=1450045402&Signature=dgvyYa~K2P-v6ArrdVBmRxAF44JTJhpk6PJqQXzHbMOmtcHw~eY~E1C0GgviL~O63-EhejMzCB~dLjlgaug-TQej8mCjvroY8crd776GRsBx0AJz4pnp3ZH03T3PnUecBHRwMrg28pjAbi1xWmuybyNvwWpitB9Q~hiCKxMzUhnXRjqpWJKZVrLDY7~iXB2GlptZNz8RZoapexeEkNA2kgjnYXk4JTe4CNTdRSmn~Uf9YHvxJdA4ttlRfDt353eSxCDTXKQdA7GkEBTKJfDvN6NgXyw8~Tm8tBHk9VYYn7jZMLYckwqi3OJAonof2SZZlHZoepgympEYxK8BdkvZMg__&Key-Pair-Id=APKAJXKSII4ED2EOGZZA"
        // server: "http://dsu0uct5x2puz.cloudfront.net/cfx/st"
        // protocol: "http"
        
        var dummy = document.createElement("audio");
        
        dummy.id = properties.id;
        dummy["instanceid"] = properties.instanceId;
        
        dummy.loop = false;
        dummy.preload = 'auto';
        dummy.volume = "1.0";
        
        dummy.sp_run = function(proof){ console.log("-<-< sp_run =>", arguments); return get_pong(proof); };
        dummy.sp_hasSound = function(){ return true };
        dummy.sp_load = function(player_id, raw_uri, options)
        {
          this["uri"] = raw_uri;
          
          var uri = options.server.match(/(.+\/\/[^\/]+?)\//)[1] + raw_uri.split(":")[1];
          console.log("sp_load =>", uri, arguments, this);
          this.src = uri;
          this.play();
          
          unsafeWindow.Spotify.Instances.get(this.instanceid).audioManager.getPlayerById(player_id).trigger('LOAD', {}, {id: player_id});
          unsafeWindow.Spotify.Instances.get(this.instanceid).audioManager.getPlayerById(player_id).trigger('PLAYING', {}, {id: player_id});
        }
        dummy.sp_setVolume = function(player_id, vol) { console.log("=> volume", arguments); if(player_id == "main:A" && vol != 0) this.volume = vol; }
        dummy.sp_seek = function(player_id, pos) { console.log("=> seek", arguments); this.currentTime = Math.floor(0.001 * pos); }
        
        
        
        dummy.sp_playerState = function(player_id)
        {
          return {volume: this.volume,
                  position: Math.floor(1000 * this.currentTime),
                  duration: Math.floor(1000 * this.duration),
                  trackUri: this.uri,
                  isPlaying: true,
                  isStopped: false,
                  isPaused: false};
        }
        
        dummy.sp_addPlayer = function(player_index, player_id, player_protocol)
        {    
          this.addEventListener('canplay', function(e)
          {
            console.log("html5 audio canplay event", e);
            unsafeWindow.Spotify.Instances.get(this.instanceid).audioManager.getPlayerById(player_id).trigger('LOAD', {}, {id: player_id});
          });
          
          this.addEventListener('playing', function(e)
          {
            console.log("html5 audio playing event", e);
            unsafeWindow.Spotify.Instances.get(this.instanceid).audioManager.getPlayerById(player_id).trigger('PLAYING', {}, {id: player_id});
          });
          
          this.addEventListener('ended', function(e)
          {
            console.log("html5 audio ended event", e);
            unsafeWindow.Spotify.Instances.get(this.instanceid).audioManager.getPlayerById(player_id).trigger('TRACK_ENDED', {}, {id: player_id});
          });
          
          this.addEventListener('durationchange', function(e)
          {
            console.log("html5 audio durationchange event", e);
            unsafeWindow.Spotify.Instances.get(this.instanceid).audioManager.getPlayerById(player_id).trigger('DURATION', {duration: Math.floor(1000 * this.duration)}, {id: player_id});
          });
          
          this.addEventListener('timeupdate', function(e)
          {
            //console.log("html5 audio timeupdate event", e);
            unsafeWindow.Spotify.Instances.get(this.instanceid).audioManager.getPlayerById(player_id).trigger('PROGRESS', {position: Math.floor(1000 * this.currentTime)}, {id: player_id});
          });
          
          // .audioManager.getPlayerById(\'" + id + "\')._triggerDeferred",eventType,values,id);
          
          
          /*
          
          package com.spotify.utilities
{
   import flash.external.ExternalInterface;
   import com.spotify.bridge.models.Config;
   
   public class JSInterface
   {
       
      public function JSInterface()
      {
         super();
      }
      
      public static function notify(param1:String, param2:* = null, param3:Number = 0, param4:String = "") : void
      {
         var eventType:String = param1;
         var values:* = param2;
         var target:Number = param3;
         var id:String = param4;
         if(ExternalInterface.available)
         {
            try
            {
               if(target == 0)
               {
                  ExternalInterface.call("Spotify.Instances.get(\'" + Config.INSTANCE_ID + "\')._bridge._triggerDeferred",eventType,values);
               }
               else if(target == 1)
               {
                  ExternalInterface.call("Spotify.Instances.get(\'" + Config.INSTANCE_ID + "\').audioManager.getInterface()._triggerDeferred",eventType,values);
               }
               else if(target == 2)
               {
                  ExternalInterface.call("Spotify.Instances.get(\'" + Config.INSTANCE_ID + "\').audioManager.getPlayerById(\'" + id + "\')._triggerDeferred",eventType,values,id);
               }
               return;
            }
            catch(error:Error)
            {
               Log.debug("External interface error " + error.message);
               return;
            }
         }
      }
      
      public static function registerCallback(param1:String, param2:Function) : void
      {
         var functionName:String = param1;
         var callback:Function = param2;
         if(ExternalInterface.available)
         {
            try
            {
               ExternalInterface.addCallback(functionName,callback);
               return;
            }
            catch(error:Error)
            {
               Log.debug("External interface not available " + error.message);
               return;
            }
         }
      }
   }
}

          */
          
          this.addEventListener('error', function(e)
          {
            console.log("html5 audio error event", e);
            unsafeWindow.Spotify.Instances.get(this.instanceid).audioManager.trigger('PLAYBACK_FAILED', {}, {id: player_id});
          });
          
          return true;
        }
        
        dummy.__noSuchMethod__  = function(name,params) { console.log('==>>== > invalid function call', name, params); };

        window.document[properties.id] = dummy;

        parent_elem = document.getElementById(parent_id)
        parent_elem.appendChild(dummy);

        // tell the spotify framework that the swf embed is ready
        arguments[9].apply(this, [{success: true}]);
        
        
        // act as the swf bridge and tell it that the Flash-backend is ready
        // JSInterface.notify(ApplicationEvents.READY,null,1);
        //Spotify.Instances.get(properties.instanceId).audioManager.getInterface()._triggerDeferred('FLASH_AVAILABLE', null);
        Spotify.Instances.get(properties.instanceId).audioManager.getInterface()._triggerDeferred('READY', null);
        
        console.log("AUDIOMANAGER-INTERFACE", properties.id, window.document[properties.id], Spotify.Instances.get(properties.instanceId).audioManager.getInterface(),
                   Spotify.Instances.get(properties.instanceId).audioManager.getInterface().hasSound())
      }
    }
  });


function when_external_loaded()
{
// ---
  
/* wait until the page is ready for the code snippet to run */
document.addEventListener('DOMContentLoaded', function()
{
  console.log("!!! DOMContentLoaded");

  WebSocket.prototype.sond = WebSocket.prototype.send;
  WebSocket.prototype.send = function(msg)
  {
    if (this.onmessage && this.sucks !== true)
    {
      callback = this.onmessage;
      
      console.log("orig prev callback:", callback);

      this.onmessage = function(message)
      {
        var json_msg = JSON.parse(message.data);
        
        if (json_msg.id === window.last_track_msg)
          console.info("<- ws recv: ", window.last_track_msg, message.data);
        
        //if (json_msg && json_msg.result && json_msg.result.uri)
          //open(json_msg.result.uri);
        
        callback(message);
      }
      
      this.sucks = true;
    }

    var json_msg = JSON.parse(msg);

    // get http link
    if (json_msg && json_msg.name == 'sp/track_uri')
    {
      arguments[0] = msg.replace(',"rtmp"', '');
      window.last_track_msg = json_msg.id;
      console.info("-> ws send: ",  json_msg.id, msg);
    }
    //dec_msg = json_msg.name === 'sp/hm_b64' ? atob(json_msg.args[0]) : null;
    
    //console.info("-> ws send: ", msg); //json_msg, dec_msg);
    
    //if (json_msg.name !== 'sp/log')
     return WebSocket.prototype.sond.apply(this, arguments);
  }
  
});
  
// ---
}

/* inject this cleaning function right in the page to avoid silly sandbox-related greasemonkey limitations */
window.document.head.appendChild(
  inject_fn = document.createElement("script")
);

inject_fn.innerHTML = '(' + when_external_loaded.toString() + ')()';