Skip to content

GMscrobber 脚本编写示例

justan edited this page Mar 26, 2017 · 1 revision

layout: post
title: "怎样记录 QQ 音乐到 last.fm"
date: 2012-06-15 09:36

GMscrobber 脚本编写示例

如果我想记录 QQ 音乐到 Last.fm 怎么办? 下面将介绍整个用户脚本的编写过程.

关于页面接口

现在的 javascript 程序越来越复杂, 再经过上线压缩过程后, 程序几乎没有可读性. 我们希望知道一些页面程序的调用情况, 而页面作者却没有义务为用户脚本提供接口. 好在 WEB 前端程序在运行过程中调用 javascript 的结果最终都反馈到 HTML 中, 对于一些不需要太精细的需要我们都可以通过监控 HTML 的变化来获得. 实际上, HTML 是 userscript 最好的接口.

新建 userscript, 并引用 simple scrobbler

进入 [QQ 音乐]页面, 并新建用户脚本, 并 require simple scrobbler, 大概如下:

    // ==UserScript==
    // @name        qq music scrobbler
    // @namespace   http://gmscrobber.whosemind.net
    // @description 记录QQ音乐到last.fm
    // @include     http://y.qq.com/*
    // @require     https://raw.github.com/justan/gmscrobber/master/simple_scrobbler_user.js
    // @version     0.0.1
    // ==/UserScript==

初始化

gmscrobber 提供了一个构造函数 Scrobbler, 我们首先需要新建一个 Scrobbler 示例和一个初始化函数:

    var init = function(){
      log('init');
    };

    var scrobber = new Scrobbler({
      name: 'QQ 音乐',
      ready: init
    });

校正匹配 url

刷新页面, 在控制台发现打印出不止一个的 init. 分析页面, 发现页面中含有 iframe, 其地址同样匹配 @include, 所以我们需要对 include 进行校正, 据官方说法, match 比较安全:

    // ==UserScript==
    // @name        QQ音乐 online scrobbler
    // @namespace   http://gmscrobber.whosemind.net
    // @description 记录qq在线音乐到 last.fm
    // @match       http://y.qq.com/
    // @match       http://y.qq.com/?*
    // @match       http://y.qq.com/#*
    // @exclude     http://y.qq.com/y/*
    // @require     https://raw.github.com/justan/gmscrobber/master/simple_scrobbler_user.js
    // @version     0.0.1
    // ==/UserScript==

核心代码: 分析页面, 取得播放的歌曲信息

对于那些播放器界面不是 Flash 的音乐网站, gmscrobber 提供了一个自动化的歌曲播放状态监测函数(scrobber.setSongInfoFN), 在 QQ 音乐 scrobbler 用户脚本中只需要编写函数来提供歌曲信息. 通过页面调试工具, 找出包含歌曲信息的元素. 需要信息有: 歌名(song.title), 歌手(song.artist), 曲长(song.duration), 专辑名(song.album), 当前播放时间(song.playTime)

    var init = function(){
      log('init');
      scrobber.setSongInfoFN(getSongInfo, {checktime: 4000});
    };

    var getSongInfo = function(){
      var song = {};
      var songinfo = document.getElementById('divsonginfo');
      song.title = songinfo.getElementsByClassName('music_name')[0].title;
      song.artist = songinfo.getElementsByClassName('singer_name')[0].title;
      song.duration = timeParse(document.getElementById('ptime').innerHTML);
      song.playTime = song.duration * document.getElementById('spanplaybar').style.width.replace(/%/, '') / 100;
      song.album = songinfo.getElementsByClassName('album_pic')[0].title;
      return song;
    };

    var timeParse = function(timeStr){
      var ts = timeStr.split(':');
      return ts[0] * 60 + ts[1] * 1;
    };

scrobber.setSongInfoFN 接受第二个参数, checktime 是重复检测歌曲信息变化的 timer 定时时长.

这一步完成后, QQ music scrobbler 就已经可以使用了, 它会向 last.fm 发送正在播放的歌曲, 播放一段时间后会自动记录.

更精准的 scrobbler 时间

last.fm 歌曲记录时间有一定的规则, 一首歌一般会在播放了 90%(可配置) 的时候记录, 最长会在 4 分钟后记录. 如果一首歌在刚刚开始播放的时候将其拖动到 90%(记录点) 的时候, 程序不应该将其记录. 增加 seek 的支持即可解决这个问题:

    addEventListener('click', function(e){
      var oldTime = getSongInfo().playTime;
      setTimeout(function(){
        var newTime = getSongInfo().playTime;
        offset = oldTime - newTime;
        scrobber.seek(offset);
      }, 0);
    }, true);

事件, 显示记录次数

gmscrobber 提供了简单的事件系统, 默认的事件有 nowplaying, scrobble, love, unlove, ban, unban.

每播放一首歌我希望看到该歌曲我总共记录了多少次. 调用 scrobber.getInfo 可以获得这一信息:

    scrobber.on('nowplaying', function(){
      scrobber.getInfo(scrobber.song, function(info){
        document.getElementById('divplayer').title = '在 last.fm 中记录: ' + info.len + ' 次';
      });
    });

红心歌曲的同步

贪婪的红心

QQ 音乐和 last.fm 都有提供了爱歌曲功能, 现在我们需要将它们互相同步. 获取 QQ 音乐和 last.fm 上同一首歌曲的信息进行对比, 如果一方标示为喜爱, 而另一方未标示为喜爱, 程序应当将其标示为喜爱, 这样就完成了红心歌曲的同步.

仔细想想, 发现程序并不知道我们曾经的操作: 对于一首没有标为红心的歌曲, 它并不知道我是曾经将其红心取消还是我从来未操作过, 对于前者, 将其重新标为喜爱显然会惹恼我. 解决这个问题的办法是将 last.fm 的红心库列为标准, 程序只会自动新增/删除 QQ 音乐上歌曲的红心.

    scrobber.on('nowplaying', function(){
      var loveEle = document.getElementsByClassName('music_op')[0].firstChild;
      loveEle.addEventListener('click', function(e){
        if(loveEle.title == '喜欢'){
          scrobber.love();
        }else if(loveEle.title == '取消喜欢'){
          scrobber.unlove();
        }
      }, false);
      scrobber.getInfo(scrobber.song, function(info){
        document.getElementById('divplayer').title = '在 last.fm 中记录: ' + info.len + ' 次';
        //同步 last.fm 红心歌曲到 qq music
        if(info.islove == '1' && loveEle.title == '喜欢' || info.islove == '0' && loveEle.title == '取消喜欢'){
          unsafeWindow.g_topPlayer.like(null, loveEle, unsafeWindow.g_topPlayer.getCurSongInfo());
        }
      });
    });

PS: Firefox 装了 greasemonkey 后打开 github 上以 *.user.js 为结尾的代码页面有些问题, 如果不能打开, 请禁用 greasemonkey 后重试.