您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Play cued mixes right from Soundcloud!
// ==UserScript== // @name cuenation SCPlayer // @locale en // @description Play cued mixes right from Soundcloud! // @license http://creativecommons.org/licenses/by-sa/3.0/ // @namespace http://cuenation.com // @include http://cuenation.com/?page=tracklist*.cue // @version 0.7.3 // ==/UserScript== /** * Changelog * v0.1 Initial version * v0.2 switched to SC Widget API * v0.3 Added some style * v0.4 'Now playing...' caption * v0.4.1 Cursor changed to pointer * v0.5 Added cursor, play/pause fix * v0.6 Hidden/visible player * v0.6.1 Support for private tracks with secret_token * v0.6.2 LOAD_PROGRESS is not supported any more * v0.7 Chrome support, progress bar on tracks * v0.7.1 Bugfix at seeking * v0.7.2 Update cursor if seeking in paused state * v0.7.3 Typo fixes + SC ID * * * Todo list, known issues: * - volume control * - support for various cueNation layouts * - shuffle/reorder tracks * - document and optimize code * */ var doc = document; var forEach = Array.prototype.forEach; var $ = null; var SC = null; var widget = null; var loadedPercent = 0.0; var loadedMs = 0; var duration = 0; var lastLoadedTrack = 0; var tracks = new Array(); var currentIdx = -1; var paused = true; /* General class for the Tracks */ function Track(_o, _l, _e, _clickCallback) { this.offset = _o; this.element = _e; this.length = _l; var self = this; this._addEvent('click', function(e){ _clickCallback(self.offset); e.stopPropagation(); }); } Track.prototype.Length = function() { return this.length; }; Track.prototype.Offset = function() { return this.offset; }; Track.prototype.Element = function() { return this.element; }; Track.prototype._addEvent = function(eventName, callback) { if (this.element.addEventListener) { this.element.addEventListener(eventName, callback, false); } else { this.element.attachEvent(eventName, callback, false); } }; /***************************/ /******** Functions */ // Init Soundcloud player widget API function initSC1() { var sc = doc.createElement('script'); sc.src = 'https://w.soundcloud.com/player/api.js'; sc.type = 'text/javascript'; sc.id = 'api1'; sc.onload = function () { initSC2(); }; doc.getElementsByTagName('head')[0].appendChild(sc); } // Init Soundcloud general API function initSC2() { var sc = doc.createElement('script'); sc.src = 'https://connect.soundcloud.com/sdk.js'; sc.type = 'text/javascript'; sc.onload = function () { initJQ(); }; doc.getElementsByTagName('head')[0].appendChild(sc); } // Init JQuery function initJQ() { var jq = doc.createElement('script'); jq.src = 'http://code.jquery.com/jquery.min.js'; jq.type = 'text/javascript'; jq.onload = function() { initWidget(); }; doc.getElementsByTagName('head')[0].appendChild(jq); } // Function to retrieve the SC url from the links divs function getSCUrl() { var anchors = doc.querySelectorAll('a.clear'); var firstUrl = ''; var found = false; forEach.call(anchors, function(anchor){ if (anchor.href.indexOf('soundcloud.com') > 0 && !found) { firstUrl = anchor.href; found = true; } }); if (firstUrl !== ''){ var re = new RegExp('.*url=(.*soundcloud\.com.*)', 'g'); var arr = re.exec(firstUrl); var url = unescape(arr[1]); if (url.indexOf('/download') > 0) { url = url.replace('download', ''); } return url; }else { return ''; } } // Init the Soundcloud widget function initWidget() { $ = unsafeWindow.jQuery; SC = unsafeWindow.SC; var placeholder = $('h2.title'); var track_url = getSCUrl(); if (track_url !== '') { SC.initialize({ client_id: '24fe22b60396d3664c297e47f0f27929' }); SC.get('/resolve', {url: track_url}, function(track){ if (track.id === null || track.id === undefined) { addNoSC(placeholder, 'Track is removed. Player is disabled.'); } else { var minimized = false; var soundurl = track.uri; var woptions = '&auto_play=false&auto_advance=false&buying=true&liking=true'; woptions += '&download=true&sharing=true&show_artwork=false&show_comments=false'; woptions += '&show_playcount=true&show_user=false'; placeholder.after('<div id="box"><table id="boxCaption"><tr><td id="showhide"></td><td id="current"></td></tr></table><iframe class="ifr" width="100%" height="160" scrolling="no" frameborder="no"></iframe></div>'); var ifr = doc.querySelector('.ifr'); ifr.style.transition = 'height 0.5s linear 0s'; ifr.style.height = '160px'; ifr.src = 'https://w.soundcloud.com/player/?url=' + soundurl + woptions; widget = SC.Widget(ifr); initEvents(); var boxCaption = doc.querySelector('#boxCaption'); boxCaption.style.color = '#aaa'; boxCaption.style.margin = '0px'; boxCaption.style.cursor = 'pointer'; boxCaption.style.backgroundColor = '#f5f5f5'; boxCaption.style.border = '1px solid #e5e5e5'; boxCaption.style.width = '100%'; boxCaption.style.height = '10px'; boxCaption.style.tableLayout = 'fixed'; var sh = doc.querySelector('#showhide'); sh.style.paddingRight = '4px'; sh.style.width = '90px'; sh.innerHTML = '<< Hide Player'; var box = doc.querySelector('#box'); box.style.height = '170px'; box.style.transition = 'height 0.5s linear 0s'; boxCaption.addEventListener('click', function(e) { if (!minimized){ box.style.height = '10px'; ifr.style.height = '0px'; minimized = true; sh.innerHTML = '>> Show Player'; } else { box.style.height = '170px'; ifr.style.height = '160px'; minimized = false; sh.innerHTML = '<< Hide Player'; } }); } }); } else { addNoSC(placeholder, 'No soundcloud link is found. Player is disabled.'); } } function addNoSC(placeholder, msg) { placeholder.after('<div class="noSC">'+msg+'</div>'); var noSC = doc.querySelector('.noSC'); noSC.style.color = '#999'; noSC.style.paddingLeft = '0px'; noSC.style.paddingBottom = '0px'; noSC.style.fontStyle = 'italic'; } function updateCurrent (playData) { var res = {}; res = getCurrentTrack(playData); if (res.ix != currentIdx) { removeCursor(currentIdx); addCursor(res.ix); currentIdx = res.ix; } updateProgressBar(res); //res.ix, res.progress } // bind to SC events function initEvents(){ if (widget === null) { return; } widget.bind(SC.Widget.Events['READY'], function(){ widget.setVolume(20); widget.getDuration(function(val){ duration = val; }); }); widget.bind(SC.Widget.Events['PLAY_PROGRESS'], function(eData){ updateCurrent(eData); if (lastLoadedTrack == tracks.length-1) { return; } loadedPercent = eData.loadedProgress; loadedMs = duration * loadedPercent; for (var i = lastLoadedTrack; i < tracks.length; i++) { if (tracks[i].Offset() <= loadedMs) { tracks[i].Element().style.textDecoration = 'underline'; tracks[i].Element().style.color = 'rgba(255,102,0,1.0)'; tracks[i].Element().style.cursor = 'pointer'; lastLoadedTrack = i; } } }); widget.bind(SC.Widget.Events['PAUSE'], function(eData){ var res = {}; res = getCurrentTrack(eData); paused = true; var p = doc.querySelector('.pauseBtn'); if (p !== null){ p.innerHTML = 'play'; } }); widget.bind(SC.Widget.Events['PLAY'], function(eData){ var res = {}; res = getCurrentTrack(eData); paused = false; var p = doc.querySelector('.pauseBtn'); if (p !== null){ p.innerHTML = 'pause'; } }); widget.bind(SC.Widget.Events['FINISH'], function(eData){ seekTo(0); widget.pause(); }); } function removeCursor(idx) { if (idx < 0) { return; } var idxElement = tracks[idx].Element().nextSibling; var titleElement = tracks[idx].Element().nextSibling.nextSibling; if (titleElement !== null && idxElement !== null) { idxElement.style.backgroundColor = 'rgba(255,255,255,0.0)'; titleElement.style.backgroundColor = 'rgba(255,255,255,0.0)'; } var toRemove = doc.querySelector('.pauseBtn'); if (null !== toRemove) { toRemove.parentNode.removeChild(toRemove); } } function addCursor(idx) { var idxElement = tracks[idx].Element().nextSibling; var titleElement = tracks[idx].Element().nextSibling.nextSibling; if (titleElement !== null && idxElement !== null) { idxElement.style.backgroundColor = 'rgba(255,102,0,0.3)'; titleElement.style.backgroundColor = 'rgba(255,102,0,0.3)'; titleElement.style.height = '1.5pt'; } var span = document.createElement('span'); span.setAttribute('class', 'pauseBtn'); span.innerHTML = paused ? 'play' : 'pause'; span.style.paddingLeft = '7px'; span.style.color = 'rgba(255,102,0,1.0)'; span.style.fontWeight = 'bold'; span.style.cursor = 'pointer'; var cp = doc.querySelector('#current'); cp.style.color = 'rgba(255,102,0,1.0)'; cp.style.paddingLeft = '4px'; cp.style.paddingRight = '4px'; cp.style.borderLeft = '1px solid #e5e5e5'; cp.innerHTML = '<marquee behavior="alternate" scrollamount=1 scrolldelay=5>Now playing: ' + titleElement.innerHTML + '</marquee>'; span.addEventListener('click', function(e){ if (!paused) { widget.pause(); } else { widget.play(); } }); titleElement.appendChild(span); } function updateProgressBar(res) { //res.ix, res.progress var toUpdate = doc.querySelector('.pBar'); if (null !== toUpdate) { var i = toUpdate.getAttribute('ix'); if (i != res.ix) { toUpdate.parentNode.removeChild(toUpdate); } toUpdate.style.width = '' + res.progress*100 + '%'; return; } var toAdd = doc.querySelector('.pauseBtn'); if (null !== toAdd) { var span = document.createElement('span'); span.setAttribute('class', 'pBar'); span.setAttribute('ix', res.ix); span.style.backgroundColor = 'rgba(255,102,0,1.0)'; span.style.height = '2px'; span.style.display = 'block'; span.style.width = '' + res.progress*100 + '%'; toAdd.parentNode.appendChild(span); } } function getCurrentTrack(eData) { var currPos = eData.currentPosition; var currIdx = 0; var found = false; var percent = 0; if (currPos >= tracks[tracks.length-1].Offset()) { currIdx = tracks.length-1; percent = (currPos - tracks[tracks.length-1].Offset())/(duration - tracks[tracks.length-1].Offset()); found = true; } for (var i = 0; i < tracks.length - 1 && !found; i++) { if (tracks[i].Offset() <= currPos && tracks[i+1].Offset() > currPos) { currIdx = i; percent = (currPos - tracks[i].Offset())/(tracks[i+1].Offset() - tracks[i].Offset()); found = true; } } return {ix: currIdx, progress: percent}; } // seek to a certain position in milliseconds function seekTo(ms) { if (null !== widget) { if (ms <= loadedMs){ widget.seekTo(ms); var playData = {currentPosition: ms}; updateCurrent(playData); } else { //??? } } } // init the 'buttons': time offsets on the page function initButtons() { var times = doc.querySelectorAll('.timeindex'); for (var i = 0; i < times.length; i++) { var tracktimeParts = times[i].innerHTML.replace(/^.(\s+)?/, '').replace(/(\s+)?.$/, '').split(':'); var ms = tracktimeParts[0]*60000 + tracktimeParts[1]*1000; var trackLengthElement = times[i].nextSibling.nextSibling.nextSibling; if (trackLengthElement !== null) { var l = trackLengthElement.innerHTML.replace(/^.(\s+)?/, '').replace(/(\s+)?.$/, '').split(':'); var newTrack = new Track(ms, l, times[i], seekTo); tracks[i] = newTrack; } } } // MAIN initButtons(); initSC1();