Soundcloud 移动设备上的嵌入式播放器

Posted

技术标签:

【中文标题】Soundcloud 移动设备上的嵌入式播放器【英文标题】:Soundcloud embedded player on mobile 【发布时间】:2017-01-30 05:35:24 【问题描述】:

以下是 html 页面上的 SoundCloud 嵌入式播放器在移动设备上的外观:

这很烦人,因为用户必须单击“在浏览器中收听”,然后,通常情况下,它并没有按应有的方式启动,因此用户必须再次单击“暂停”按钮和“播放”。

如何保持正常外观,即使在移动设备上也是如此?


这是嵌入代码的示例:

<iframe   scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/271188615&amp;color=ff5500&amp;auto_play=false&amp;hide_related=false&amp;show_comments=true&amp;show_user=true&amp;show_reposts=false"></iframe>

【问题讨论】:

设置 WebView 以显示桌面版本的网站有帮助。 *** ref. 【参考方案1】:

我建议不要为播放器使用嵌入式 iframe,而是使用SoundCloud's HTTP API

我的回答并不关注任何方法来欺骗嵌入式 iframe 代码使其不认为它是移动的。相反,我展示了如何制作自己的 SoundCloud 播放器的替代方法。

这样做可以保证:

您可以完全控制您的用户界面 您可以完全控制播放

我已经着手在 android 中构建了一个示例应用程序。假设由于发布的问题图像中的状态栏,您正在这里寻找 Android。

还应要求提供一个可在移动设备上运行的网络项目。该网络项目使用 SoundCloud 的 api javascript 包装器。

2016 年 10 月 20 日更新: 我在网络浏览器中对移动设备上的自动播放进行了一些研究。事实证明,有很多很好的问题可以回答这个问题。可悲的是,我得出的结论是不可能的。 "Autoplay" HTML5 audio player on mobile browsers 我已将 javascript sn-p 更新为现在在移动设备上加载时不会自动播放。它需要用户按下播放按钮。

音频无法在页面加载时播放,并且需要至少与页面进行一次用户交互(触摸事件)才能播放。我很想在这方面被证明是错误的,所以如果有人知道其他任何事情,那就开火吧!

您可以在这里找到我的示例项目:

网络项目: https://github.com/davethomas11/stackoverlow_Q_39625513/tree/master/WebPlayer 托管在这里 -> https://www.daveanthonythomas.com/remote/so39625513/

安卓: https://github.com/davethomas11/stackoverlow_Q_39625513/SoundCloudPlayer

检查一下,如果有任何不清楚的地方,可以问我有关实施的任何问题。这适用于任何阅读此答案的人。

解决方案是在 Java 中本地完成的。但如果您喜欢,也可以使用 HTML 和 Javascript 来完成,因为我们使用的是他们的 HTTP Rest API,平台无关紧要。

完全自定义,这种方式让我们可以完全控制 UI。 我的 UI 不是最漂亮的,但在这种级别的控制下,它可以随心所欲地变丑或变美 ;) ->

我将分解使用 Sound Cloud 的 api 来完成此操作的基本步骤。

幸运的是,我们的播放非常简单。您可以跳过所有身份验证要求。因为您将使用的任何端点都不需要身份验证。

您只需要一个客户端 ID 即可提出您的请求。 我建议使用 sound cloud 注册一个应用程序,但您可以像我一样使用嵌入式播放器的客户端 id。

注意:嵌入式播放器使用客户端id -> cUa40O3Jg3Emvp6Tv4U6ymYYO50NUGpJ

此实现的基础是 tracks 端点: https://developers.soundcloud.com/docs/api/reference#tracks

这个端点几乎为我们提供了我们需要的一切:

流媒体网址 标题、艺术家姓名 艺术品

但是缺少一件事,那就是显示 SoundCloud 品牌识别波形的波形数据点。

获取这些数据的基础知识需要一些黑客攻击。但数据以足够纯净的形式存在。

如果您检查调用的响应以获取嵌入式播放器,您会注意到源代码中加载了名为waveform_url 的资源。这个 url 返回一个包含所有波点信息的漂亮 json 文档:https://wis.sndcdn.com/sTEoteC5oW3r_m.json

我已经调整了我的解决方案,通过从该 url 检索它来解析来自嵌入式播放器的波形数据。

你会注意到我做了一个非常粗略的版本。用一点肘部油脂,这可以变成漂亮的东西,甚至是独一无二的。但是,获得它的基础已经存在。

我在解决方案中实现的另一个端点是 cmets 端点:https://developers.soundcloud.com/docs/api/reference#comments

我还没有将它添加到 UI。但是 API 代码应该能够说明它的使用。

Android 项目使用以下库:

改造http://square.github.io/retrofit/ 毕加索http://square.github.io/picasso/ Stetho http://facebook.github.io/stetho/

对于那些不熟悉的人,因为它是半新的: - 安卓数据绑定https://developer.android.com/topic/libraries/data-binding/index.html

请随意使用我的解决方案作为基础,因为我已根据 GNU 许可发布它。任何阅读本文的人都会如此。

我也想考虑在 git-hub 存储库中添加一个类似的 ios 解决方案。

这是一个作为 sn-p 的网络项目: 编辑我已将其更新为使用 cmets 中建议的波形图像,而不是承担渲染波形的复杂任务。 如果有人能够对 soundcloud 画布绘图进行逆向工程,那将是非常酷的。 JavaScript 在该 iframe 中可用。

/*!
 * jQuery UI Touch Punch 0.2.3
 *
 * Copyright 2011–2014, Dave Furfero
 * Dual licensed under the MIT or GPL Version 2 licenses.
 *
 * Depends:
 *  jquery.ui.widget.js
 *  jquery.ui.mouse.js
 */
!function(a)function f(a,b)if(!(a.originalEvent.touches.length>1))a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)if(a.support.touch="ontouchend"in document,a.support.touch)var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a)var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown")),b._touchMove=function(a)e&&(this._touchMoved=!0,f(a,"mousemove")),b._touchEnd=function(a)e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1),b._mouseInit=function()var b=this;b.element.bind(touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")),c.call(b),b._mouseDestroy=function()var b=this;b.element.unbind(touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")),d.call(b)(jQuery);

function WaveForm(waveformPngUrl) 

	$('.track_waveform').append("<img src=\""+waveformPngUrl+"\" />");
	$('.track_waveform').append("<div class='wvprogress'></div>")

	this.setProgress = function (newProgress) 

		var width = $('.track_waveform').width();
		var progressPoint =  width - ((1 - newProgress) * width);
		$('.wvprogress').css( width: "" + progressPoint + "px" );

	


var player, mTrack, audio, seekBarInterval, waveForm;
var updatingSeekBar = false;
var clientId = 'cUa40O3Jg3Emvp6Tv4U6ymYYO50NUGpJ';

$(function () 

    SC.initialize(
        client_id: clientId
    );

    player = document.getElementById("SoundCloudPlayer");

    checkQueryURLForTrackId();
    loadTrackEnteredInInput();

    $("form button").button();
);

function loadTrackEnteredInInput() 

    loadTrack(getTrackId());


function loadTrack(trackId) 


    SC.get('/tracks/' + trackId).then(function (track) 

        // Inspect for info on track you want:
        console.log(track);
        mTrack = track;

        renderTrack(track);
        streamTrack(track);

        waveForm = new WaveForm(track.waveform_url);

    , function () 

        alert("Sorry no track found for track id: "+ trackId)
    );


function renderTrack(track) 

    $(player).find(".track_artist").text(track.user.permalink);
    $(player).find(".track_title").text(track.title);
    $(player).find(".track_artwork").attr('src', track.artwork_url);
    $(player).find(".track_seek_bar").slider(
        
            orientation: "horizontal",
            range: "min",
            max: track.duration,
            value: 0,
            change: seek
        );



function streamTrack(track) 

    var trackUrl = track.stream_url + "?client_id=" + clientId;

    audio = new Audio(trackUrl);
    console.log(trackUrl);

    if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) 
        
        // Sorry can not auto play on mobile =_(
        // https://***.com/questions/26066062/autoplay-html5-audio-player-on-mobile-browsers
        $(player).find(".track_pause").hide();
        $(player).find(".track_play").fadeIn();
     else 
        play();
    
    


function play() 

    $(player).find(".track_play").hide();
    $(player).find(".track_pause").fadeIn();

    audio.play();

    seekBarInterval = setInterval(updateSeekBar, 500);


function pause() 

    $(player).find(".track_pause").hide();
    $(player).find(".track_play").fadeIn();

    audio.pause();

    clearInterval(seekBarInterval);


function seek(event) 

    if (event.originalEvent) 
        audio.currentTime = $(player).find(".track_seek_bar").slider("value") / 1000;
    
    waveForm.setProgress((audio.currentTime * 1000) / mTrack.duration); 


function updateSeekBar() 

    var time = (audio.currentTime * 1000);
    $(player).find(".track_seek_bar").slider("value", time);


/**
 * Loads a different track id based on
 * url query
 */
function checkQueryURLForTrackId() 
    var query = getUrlVars();
    if (query.trackId) 
        $('[name=trackId]').val(query.trackId);
    


//https://***.com/questions/4656843/jquery-get-querystring-from-url
// Read a page's GET URL variables and return them as an associative array.
function getUrlVars()

    var vars = , hash;
    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
    for(var i = 0; i < hashes.length; i++)
    
        hash = hashes[i].split('=');
        vars[hash[0]] = hash[1];
    
    return vars;


function getTrackId() 
    return trackId = $('[name=trackId]').val();
body 
    font-family: 'Raleway', sans-serif;


#SoundCloudPlayer .track_artwork 
    float:left;
    margin-right: 6px;


#SoundCloudPlayer .track_artist 
    font-size: small;
    margin-bottom: 4px;


#SoundCloudPlayer .track_title 
    margin-top: 0px;
    font-weight: bold;


#SoundCloudPlayer .track_control 
    cursor: pointer;
    display: none;


#SoundCloudPlayer .track_seek_bar .ui-slider-range  background: orange; 
#SoundCloudPlayer .track_seek_bar .ui-slider-handle  border-color: orange; 

#SoundCloudPlayer .track_waveform 
    width: 100%;
    height: 80px;
    margin-top: 5px;
    margin-bottom: 5px;
    position: relative;


#SoundCloudPlayer .track_waveform img 
    
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    position: absolute;
    z-index: 0;


#SoundCloudPlayer .track_waveform .wvprogress
    height: 100%;
    position: absolute;
    opacity: 0.25;
    background-color: #ed970e;
    width: 0px;
    z-index: 1;
    left: 0;
    top: 0;
<html>
<head>
    <meta name="viewport" content="initial-scale=1, maximum-scale=1">
    <title>SoundCloud API Web Player Demo</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
    <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.0/themes/smoothness/jquery-ui.css" />
    <script src="jquery.ui.touch-punch.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.js"></script>
    <script src="https://connect.soundcloud.com/sdk/sdk-3.1.2.js"></script>
    <script src="waveformImage.js"></script>
    <script src="player.js"></script>
    <link href="https://fonts.googleapis.com/css?family=Raleway" rel="stylesheet" />
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
    <link href="style.css" rel="stylesheet" />
</head>
<body>

<form method="get">
    <label for="trackId">Load Track:</label>
    <input name="trackId" type="text" value="271188615" />
    <button>GO</button>
</form>

<section id="SoundCloudPlayer">

    <img class="track_artwork" />
    <p class="track_artist"></p>
    <p class="track_title"></p>
    <i class="material-icons track_play track_control" onClick="play()">play_circle_filled</i>
    <i class="material-icons track_pause track_control" onClick="pause()">pause_circle_filled</i>
    <br style="clear:both"/>
    <div class="track_waveform"></div>
    <div class="track_seek_bar" ></div>
</section>
</body>
</html>

【讨论】:

是的,当然没问题。在我发帖后我想这可能是这种情况。稍后我会回到那个例子:)。 添加了 Web 项目。 @Basj 惊人的答案! 我确实得到了一个模糊的波形。我的最终解决方案对我来说并不模糊?只需指定画布的宽度并允许高度自动为我消除模糊。模糊与画布元素的大小有关。搜索模糊的 HTML 画布。 如果您不需要从头开始绘制自己的波形,API 的第 1 版实际上会给您返回由 SoundCloud 创建的波形图像。像这个。 w1.sndcdn.com/gOCegnm5qXQm_m.png 只需使用 api.soundcloud.com 而不是 api-v2.soundcloud.com,waveform_url 就会返回一个图像。【参考方案2】:

迷你播放器(高度=20)在台式机和移动设备上具有相似的外观和感觉。

<iframe   scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/271188615&color=ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false"></iframe>

【讨论】:

能否添加一个可运行的代码 sn-p 和屏幕截图,以供将来参考@VadimGulyakin?【参考方案3】:

您可以设置WebView 显示桌面版本。网站:

WebView view = new WebView(this);
view.getSettings().setUserAgentString("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36");

【讨论】:

【参考方案4】:

使用 show_teaser=false 作为参数来隐藏叠加层。

【讨论】:

明确地说,这是 URL 上的参数,而不是 iframe 本身。

以上是关于Soundcloud 移动设备上的嵌入式播放器的主要内容,如果未能解决你的问题,请参考以下文章

Youtube 自动播放无法在具有嵌入式 HTML5 播放器的移动设备上运行

React (HTML) 视频标签不会在移动设备上自动播放

来自 YouTube 的嵌入式 360 度视频无法在 iOS 浏览器上正确播放

移动设备上的 HTML5 视频行为

播放 Soundcloud embed 时暂停 Youtube embed

为啥移动设备上的浏览器可以播放比设备支持的分辨率更高的视频,但显示相同 Web 应用程序的原生应用程序可能会失败?