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

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

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

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


这是嵌入代码的示例:

<iframe width="100%" height="166" 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>

【问题讨论】:

标签: html responsive-design soundcloud


【解决方案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 项目使用以下库:

对于那些不熟悉的人,因为它是半新的: - 安卓数据绑定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://stackoverflow.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://stackoverflow.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 width="100%" height="20" 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 本身。
    猜你喜欢
    • 1970-01-01
    • 2019-03-19
    • 2013-02-12
    • 2015-09-07
    • 1970-01-01
    • 1970-01-01
    • 2017-09-30
    • 2021-12-22
    相关资源
    最近更新 更多