diff --git a/INFO b/INFO index 22899f0..8e4c599 100644 --- a/INFO +++ b/INFO @@ -1,7 +1,7 @@ { "name": "ludysu_neteaselrc", "displayname": "网易云音乐", - "description": "特色:根据曲名、艺术家匹配程度高低自动排序", + "description": "特色:根据曲名、艺术家匹配程度高低自动排序;可自动添加中文翻译", "version": "1.0", "site": "http://music.163.com", "module": "netease.php", diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 index c4703d5..c0fd3fd --- a/build.sh +++ b/build.sh @@ -1,3 +1,16 @@ -!#/bin/bash +#!/bin/bash -tar czf netease.php +# Disable debug +sed -i.bak "s@^const.DEBUG.*;@const DEBUG = false;@" netease.php + +# Generate the no Chinsese translation version +sed -i.bak "s@^const.NEED_TRANSLATION.*;@const NEED_TRANSLATION = false;@" netease.php +tar czf netease_org.aum netease.php INFO + +# Generate the with Chinsese translation version +sed -i.bak "s@^const.NEED_TRANSLATION.*;@const NEED_TRANSLATION = true;@" netease.php +tar czf netease_trans.aum netease.php INFO + +# Clean up +rm netease.php.bak +git checkout netease.php diff --git a/netease.php b/netease.php index 9c902af..868a79a 100644 --- a/netease.php +++ b/netease.php @@ -1,11 +1,17 @@ + * Features: + * - Sort result according to similarity of artist and title. + * - Add Chinese translated lyric if {@code NEED_TRANSLATION} is {@code TRUE}. * * @author Ludy Su (https://github.com/LudySu/Synology-LrcPlugin) * @see https://global.download.synology.com/download/Document/DeveloperGuide/AS_Guide.pdf @@ -14,8 +20,10 @@ class LudysuNetEaseLrc { private $mArtist = ""; private $mTitle = ""; + ///////////////////////////// Synology API /////////////////////////////////////// + /** - * Searches for a lyric with the artist and title, and returns the result list. + * Searches for a song with the artist and title, and returns the matching result list. Result is sorted based on similarity of artist and title. */ public function getLyricsList($artist, $title, $info) { $artist = trim($artist); @@ -72,7 +80,7 @@ class LudysuNetEaseLrc { foreach ($song['artists'] as $item) { $score = $this->getStringSimilarity($artist, $item['name']); if ($score > $max) { - $max = $distance; + $max = $score; $elem['artist'] = $item['name']; } } @@ -91,12 +99,11 @@ class LudysuNetEaseLrc { } /** - * Downloads a file with the specific ID + * Downloads a lyric with the specific ID. Will have Chinese translation if {@code NEED_TRANSLATION} is {@code TRUE}. */ public function getLyrics($id, $info) { $lrc = $this->downloadLyric($id); if ($this->isNullOrEmptyString($lrc)) { - printf("download lyrics from server failed\n"); return FALSE; } @@ -105,55 +112,13 @@ class LudysuNetEaseLrc { return true; } - private function cmp($lhs, $rhs) { - $scoreArtistL = $this->getStringSimilarity($this->mArtist, $lhs['artist']); - $scoreArtistR = $this->getStringSimilarity($this->mArtist, $rhs['artist']); - $scoreTitleL = $this->getStringSimilarity($this->mTitle, $lhs['title']); - $scoreTitleR = $this->getStringSimilarity($this->mTitle, $rhs['title']); - - printf("artist " . $lhs['artist'] . " vs " . $rhs['artist'] . " | " . $scoreArtistL . " vs " . $scoreArtistR . "
"); - printf("title " . $lhs['title'] . " vs " . $rhs['title'] . " | " . $scoreTitleL . " vs " . $scoreTitleR. "
"); - - return $scoreArtistR + $scoreTitleR - $scoreArtistL - $scoreTitleL; - } - - /** - * Gets similarity score of 0-100 between 2 strings, the bigger the score is, the more similarity. - */ - private static function getStringSimilarity($lhs, $rhs) { - similar_text($lhs, $rhs, $percent); - return $percent; - } - - private static function search($word) { - $params = array( - 's' => $word, - 'offset' => '0', 'limit' => '20', - 'total' => true, - 'type' => '1', //搜索单曲(1),歌手(100),专辑(10),歌单(1000),用户(1002) - ); - - $curl = curl_init(); - curl_setopt_array($curl, array( - CURLOPT_URL => "http://music.163.com/api/search/pc", - CURLOPT_RETURNTRANSFER => true, - CURLOPT_POST => true, - CURLOPT_POSTFIELDS => http_build_query($params), - )); - - $output = curl_exec($curl); - curl_close($curl); - - return $output; - } + ///////////////////////////// Utils /////////////////////////////////////// /** * Gets all lyrics, apart from original one, translated and karaoke versions will also be returned if available. */ private function downloadLyric($music_id) { - // lv = original version; tv = translated version; kv = karaoke version, rarely available. Set value to 0 if don't want - $url = "http://music.163.com/api/song/lyric?os=pc&id=" . $music_id . "&lv=-1&kv=0&tv=-1"; - $response = '{"sgc":false,"sfy":false,"qfy":false,"transUser":{"id":479938456,"status":99,"demand":1,"userid":124108722,"nickname":"丶吟游","uptime":1495604317130},"lrc":{"version":2,"lyric":"[by:丶吟游]\n[00:00.00] 作曲 : 田淵智也\n[00:00.27] 作词 : LiSA\n[00:00.81]\n[00:21.81]そっと 吐き出す ため息を吸い込んだ 後悔は苦い味残して\n[00:31.37]いつも なんで? 肝心なこと言えないまま 次の朝日が顔だしてる\n[00:39.81]\n[00:40.14]嫌になった運命を ナイフで切り刻んで\n[00:46.26]もう一度やり直したら キミに出会えないかも\n[00:55.36]\n[00:55.89]僕の声が響いた瞬間に始まる 命のリミット 心臓がカウントしてる\n[01:07.22]叶えても叶えても 終わらない願い\n[01:16.05]汗をかいて走った 世界の秒針は いつか止まった僕を置いていく\n[01:27.48]あと何回キミと笑えるの?\n[01:33.82]試してるんだ 僕を Catch the Moment\n[01:37.82]\n[01:47.62]一個幸せを数えるたびに 変わっていく未来に怯えてしまうけど\n[01:57.05]\n[01:57.38]愛情の種を大切に育てよう\n[02:04.20]分厚い雲も やがて突き破るかな\n[02:11.43]\n[02:11.69]キミの声が響いた 僕の全身を通って 心臓のドアをノックしてる\n[02:23.03]「臆病」でも開けちゃうんだよ 信じたいから\n[02:31.74]何にもないと思ったはずの足元に いつか深く確かな根を生やす\n[02:43.24]嵐の夜が来たとしても 揺らいだりはしない\n[02:52.05]\n[02:52.29]何度でも\n[02:52.91]追いついたり 追い越したり キミがふいに分かんなくなって\n[02:57.98]息をしたタイミングが合うだけで 嬉しくなったりして\n[03:04.83]集めた一秒を 永遠にして行けるかな\n[03:16.45]\n[03:27.67]僕の声が響いた瞬間に始まる 命のリミット 心臓がカウントしてる\n[03:38.78]叶えても叶えても 終わらない願い\n[03:47.70]汗をかいて走った 世界の秒針が いつか止まった僕を置いていく\n[03:59.05]あと何回キミと笑えるの?\n[04:05.35]試してるんだ 僕を Catch the Moment\n[04:09.43]\n[04:10.60]逃さないよ僕は\n[04:12.86]この瞬間を掴め Catch the Moment\n[04:20.14]\n"},"klyric":{"version":0,"lyric":""},"tlyric":{"version":2,"lyric":"[00:21.81]轻轻吐出的叹息 又默默咽回腹中 反悔总是留下苦涩的余味\n[00:31.37]这到底是为什么?重要的话还未来得及说出口 翌日的朝阳却已露出了脸庞\n[00:40.14]深恶痛疾的命运 用利刃切成粉碎\n[00:46.26]即使一切能从头来过 或许你我也无法邂逅\n[00:55.89]在我的声音响起的瞬间 心脏也随即开始倒数着生命的极限\n[01:07.22]无论多少次得偿所愿 愿望却始终不见尽头\n[01:16.05]挥洒汗水竭力奔走 世界的秒针 终有一日会抛下裹足不前的我\n[01:27.48]余生我又能与你共笑多少次?\n[01:33.82]这正是上帝赋予我的严峻考验 把握这一瞬\n[01:47.62]细数每一个降临的幸福 就会对渐渐改变的未来感到恐惧\n[01:57.38]用心的培育爱情的种子\n[02:04.20]天那边厚重的乌云 不久也会云开雾散\n[02:11.69]你的声音响起 贯穿我的全身 叩响心脏之门\n[02:23.03]就算胆怯 也要把心扉打开 因为我想要坚信\n[02:31.74]曾以为 空无一物的脚边 不知何时 长出深根扎于大地\n[02:43.24]就算风雨交加的夜晚来袭 我也不会有任何动摇\n[02:52.29]无论多少次\n[02:52.91]我都会追寻着你 追赶上你 就算蓦然之间 变得不再懂你\n[02:57.98]只要有那么一瞬间与你情投意合 我就无比欣喜\n[03:04.83]所收集的每一秒 能否拼凑成永恒\n[03:27.67]在我的声音响起的瞬间 心脏也随即开始倒数着生命的极限\n[03:38.78]无论多少次得偿所愿 愿望却始终不见尽头\n[03:47.70]挥洒汗水竭力奔走 世界的秒针 终有一日会抛下裹足不前的我\n[03:59.05]余生我又能与你共笑多少次?\n[04:05.35]这正是上帝赋予我的严峻考验 把握这一瞬\n[04:10.60]我不会再错过\n[04:12.86]紧握这个瞬间"},"code":200}';//$this->download($url); + $response = $this->download($music_id); if ($this->isNullOrEmptyString($response)) { return NULL; } @@ -162,14 +127,13 @@ class LudysuNetEaseLrc { $orgLrc = $json['lrc']['lyric']; $transLrc = $json['tlyric']['lyric']; // Chinese translation lyric, but only some songs have - global $NEED_TRANSLATION; $resultLrc = $orgLrc; - if ($NEED_TRANSLATION && !$this->isNullOrEmptyString($transLrc)) { + if (NEED_TRANSLATION && !$this->isNullOrEmptyString($transLrc)) { $resultLrc = ""; $orgLines = $this->processLrcLine($orgLrc); $transLines = $this->processLrcLine($transLrc); - //var_dump($orgLines); + $transCursor = 0; foreach ($orgLines as $elem) { $key = $elem['tag']; // time tag $value = $elem['lrc']; // lyric line @@ -178,33 +142,61 @@ class LudysuNetEaseLrc { // Find matching translation $trans = ""; if (!$this->isNullOrEmptyString($key)) { - $lastMatchCursor = 0; - for ($i = $lastMatchCursor; $i < count($transLines); $i++) { - $tKey = $transLines[$i]['tag']; - $tValue = $transLines[$i]['lrc']; - echo $key . " =? " . $tKey . "\n"; - // Check for matching time tag - if ($key === $tKey) { - $lastMatchCursor = $i + 1; - $trans = $tValue; - break; + $time = $this->getTimeFromTag($key); + for ($i = $transCursor; $i < count($transLines); $i++) { + $tKey = $transLines[$i]['tag']; + if ($this->getTimeFromTag($tKey) > $time) { // trans time tag is greater than org, no match found + $transCursor = $i; + break; + } + + $tValue = $transLines[$i]['lrc']; + // Check for matching time tag + if ($key === $tKey) { + $transCursor = $i + 1; + $trans = $tValue; + break; + } } } - } - echo "\n"; - if (!$this->isNullOrEmptyString($trans)) { // $key is empty when it's not time tag, just metadata - $resultLrc .= " 【" . $trans . "】\n"; + $resultLrc .= " 【" . $trans . "】"; } $resultLrc .= "\n"; } - - //var_dump($resultLrc); } return $resultLrc; } + // Comparator that determines which matches better + private function cmp($lhs, $rhs) { + $scoreArtistL = $this->getStringSimilarity($this->mArtist, $lhs['artist']); + $scoreArtistR = $this->getStringSimilarity($this->mArtist, $rhs['artist']); + $scoreTitleL = $this->getStringSimilarity($this->mTitle, $lhs['title']); + $scoreTitleR = $this->getStringSimilarity($this->mTitle, $rhs['title']); + + // printf("artist " . $lhs['artist'] . " vs " . $rhs['artist'] . " | " . $scoreArtistL . " vs " . $scoreArtistR . "
"); + // printf("title " . $lhs['title'] . " vs " . $rhs['title'] . " | " . $scoreTitleL . " vs " . $scoreTitleR. "
"); + + return $scoreArtistR + $scoreTitleR - $scoreArtistL - $scoreTitleL; + } + + /** + * Gets similarity score of 0-100 between 2 strings, the bigger the score is, the more similarity. + */ + private static function getStringSimilarity($lhs, $rhs) { + similar_text($lhs, $rhs, $percent); + return $percent; + } + + private function getTimeFromTag($tag) { + $min = substr($tag, 1, 2); + $sec = substr($tag, 4, 2); + $milli = substr($tag, 7, 2); + return $milli + $sec * 100 + $min * 60 * 100; + } + private function processLrcLine($lrc) { $result = array(); foreach (explode("\n", $lrc) as $line) { @@ -214,7 +206,10 @@ class LudysuNetEaseLrc { $key = ""; $value = $line; } - array_push($result, array($key => $value)); + array_push($result, array( + 'tag' => $key, + 'lrc' => $value + )); } return $result; } @@ -237,7 +232,40 @@ class LudysuNetEaseLrc { return (!isset($question) || trim($question)===''); } - private static function download($url) { + ///////////////////////////// Netease API /////////////////////////////////////// + + /** + * Searches for a song based on title. + */ + private static function search($word) { + $params = array( + 's' => $word, + 'offset' => '0', 'limit' => '20', + 'total' => true, + 'type' => '1', //搜索单曲(1),歌手(100),专辑(10),歌单(1000),用户(1002) + ); + + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_URL => "http://music.163.com/api/search/pc", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => http_build_query($params), + )); + + $output = curl_exec($curl); + curl_close($curl); + + return $output; + } + + /** + * Downloads a lyric for a given music_id. + */ + private static function download($music_id) { + // lv = original version; tv = translated version; kv = karaoke version, rarely available. Set value to 0 if don't want + $url = "http://music.163.com/api/song/lyric?os=pc&id=" . $music_id . "&lv=-1&kv=0&tv=-1"; + $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $url, @@ -247,12 +275,12 @@ class LudysuNetEaseLrc { curl_close($curl); return $output; } -} +} // End of class -////////////////////////// Debug //////////////////////////////////// +////////////////////////// DEBUG //////////////////////////////////// -if ($DEBUG == true) { +if (DEBUG == true) { class TestObj { private $items; @@ -306,20 +334,20 @@ if ($DEBUG == true) { $testObj = new TestObj(); $downloader = (new ReflectionClass("LudysuNetEaseLrc"))->newInstance(); - // $count = $downloader->getLyricsList($artist, $title, $testObj); - // if ($count > 0) { - // $item = $testObj->getFirstItem(); + $count = $downloader->getLyricsList($artist, $title, $testObj); + if ($count > 0) { + $item = $testObj->getFirstItem(); - // if (array_key_exists('id', $item)) { + if (array_key_exists('id', $item)) { $downloader->getLyrics($item['id'], $testObj); - // } else { - // echo "\nno id to query lyric\n"; - // } - // } else { - // echo " ****************************\n"; - // echo " *** Failed to find lyric ***\n"; - // echo " ****************************\n"; - // } + } else { + echo "\nno id to query lyric\n"; + } + } else { + echo " ****************************\n"; + echo " *** Failed to find lyric ***\n"; + echo " ****************************\n"; + } }