From 05b10dec88fa18638e8a14509fb9e729ce54c32e Mon Sep 17 00:00:00 2001 From: yanzhenyu <308989414@qq.com> Date: Wed, 18 Sep 2019 16:25:52 +0800 Subject: [PATCH] init --- .gitignore | 7 ++ INFO | 10 ++ LICENSE | 201 +++++++++++++++++++++++++++++ README.md | 8 ++ build.sh | 11 ++ qqmusic.aum | Bin 0 -> 4018 bytes qqmusic.php | 357 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 594 insertions(+) create mode 100644 .gitignore create mode 100755 INFO create mode 100755 LICENSE create mode 100755 README.md create mode 100755 build.sh create mode 100644 qqmusic.aum create mode 100755 qqmusic.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..235f586 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# Ignore all dotfiles... +.* +# except for .gitignore +!.gitignore + +#忽略某文档 +.history \ No newline at end of file diff --git a/INFO b/INFO new file mode 100755 index 0000000..68dce14 --- /dev/null +++ b/INFO @@ -0,0 +1,10 @@ +{ + "name": "Zain_QQlrc", + "displayname": "QQ音乐", + "description": "特色:根据曲名、艺术家匹配程度高低自动排序;可自动添加中文翻译", + "version": "1.0", + "site": "http://music.qq.com", + "module": "qqmusic.php", + "type": "lyric", + "class": "LudysuQQLrc" +} diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100755 index 0000000..9359b7f --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# Synology Lrc Plugin For QQ Music +Lyrics plugin for Synology Audio Station/DS Audio. + +用于群晖 Audio Station/DS Audio 的歌词插件。 + +因为网易版权太少了,所以改版为QQ乐库,搜索歌词,腾讯爸爸有钱就是牛b。 + +改版自:https://github.com/LudySu/Synology-LrcPlugin 感谢 @LudySu 的肩膀。 diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..26a7f1d --- /dev/null +++ b/build.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Disable debug +sed -i.bak "s@^const.DEBUG.*;@const DEBUG = false;@" qqmusic.php + +sed -i.bak "s@^const.NEED_TRANSLATION.*;@const NEED_TRANSLATION = false;@" qqmusic.php +tar czf qqmusic.aum qqmusic.php INFO + +# Clean up +rm qqmusic.php.bak +git checkout qqmusic.php diff --git a/qqmusic.aum b/qqmusic.aum new file mode 100644 index 0000000000000000000000000000000000000000..1fa48fed780275355868ce0d8a78a009613c2fd3 GIT binary patch literal 4018 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 YMs3taZPZ3>)P}bI09Ghv#{f_O0BpUV5dZ)H literal 0 HcmV?d00001 diff --git a/qqmusic.php b/qqmusic.php new file mode 100755 index 0000000..0658d85 --- /dev/null +++ b/qqmusic.php @@ -0,0 +1,357 @@ + + * 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 + */ +class LudysuQQLrc { + private $mArtist = ""; + private $mTitle = ""; + + ///////////////////////////// Synology API /////////////////////////////////////// + + /** + * 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); + $title = trim($title); + $this->mArtist = $artist; + $this->mTitle = $title; + if ($this->isNullOrEmptyString($title)) { + return 0; + } + $response = $this->search($title); + if ($this->isNullOrEmptyString($response)) { + return 0; + } + + $json = json_decode($response, true); + $songArray = $json['data']['song']['list']; + + // var_dump($songArray); + + if(count($songArray) == 0) { + return 0; + } + + // Try to find the titles that match exactly + $exactMatchArray = array(); + $partialMatchArray = array(); + foreach ($songArray as $song) { + $lowTitle = strtolower($title); + // var_dump("源歌名:".$lowTitle); + $lowResult = strtolower($song['songname']); + // var_dump("匹配歌名:".$lowResult."
"); + if (strtolower($lowTitle) === strtolower($lowResult)) { + array_push($exactMatchArray, $song); + } else if (strpos($lowResult, $lowTitle) !== FALSE || strpos($lowTitle, $lowResult) !== FALSE) { + array_push($partialMatchArray, $song); + } + } + + if (count($exactMatchArray) != 0) { + $songArray = $exactMatchArray; + } else if (count($partialMatchArray != 0)) { + $songArray = $partialMatchArray; + } + + // Get information from songs + $foundArray = array(); + foreach ($songArray as $song) { + $elem = array( + 'id' => $song['songid'], + 'artist' => $song['singer'][0]["name"], + 'title' => $song['songname'], + 'alt' => $song['alias'][0] . "; Album: " . $song['albumname'] + ); + // Find the best match artist from all artists belong to a song + $max = 0; + foreach ($song['singer'] as $item) { + $score = $this->getStringSimilarity($artist, $item['name']); + if ($score > $max) { + $max = $score; + $elem['artist'] = $item['name']; + } + } + array_push($foundArray, $elem); + } + + // Sort by best match according to similarity of artist and title + usort($foundArray, array($this,'cmp')); + foreach ($foundArray as $song) { + // add artist, title, id, lrc preview (or additional comment) + $info->addTrackInfoToList($song['artist'], $song['title'], $song['id'], $song['id'] . "; " . $song['alt']); + } + + return count($foundArray); + } + + /** + * 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)) { + return FALSE; + } + + $info->addLyrics($lrc, $id); + + return true; + } + + ///////////////////////////// Utils /////////////////////////////////////// + + /** + * Gets all lyrics, apart from original one, translated and karaoke versions will also be returned if available. + */ + private function downloadLyric($music_id) { + $response = $this->download($music_id); + if ($this->isNullOrEmptyString($response)) { + return NULL; + } + + $json = json_decode($response, true); + $orgLrc = $json['lyric']; + $transLrc = $json['tlyric']['lyric']; // Chinese translation lyric, but only some songs have + + $resultLrc = $orgLrc; + if (NEED_TRANSLATION && !$this->isNullOrEmptyString($transLrc)) { + $resultLrc = ""; + $orgLines = $this->processLrcLine($orgLrc); + $transLines = $this->processLrcLine($transLrc); + + $transCursor = 0; + foreach ($orgLines as $elem) { + $key = $elem['tag']; // time tag + $value = $elem['lrc']; // lyric line + $resultLrc .= $key . $value; + + // Find matching translation + $trans = ""; + if (!$this->isNullOrEmptyString($key)) { + $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; + } + } + } + + if (!$this->isNullOrEmptyString($trans)) { // $key is empty when it's not time tag, just metadata + $resultLrc .= " 【" . $trans . "】"; + } + $resultLrc .= "\n"; + } + } + + // return $resultLrc; + $r = html_entity_decode($resultLrc); + return $r; + } + + // 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) { + $key = substr($line, 0, 10); + $value = substr($line, 10, strlen($line) - 10); + if (!$this->isValidLrcTime($key)) { + $key = ""; + $value = $line; + } + array_push($result, array( + 'tag' => $key, + 'lrc' => $value + )); + } + return $result; + } + + private function isValidLrcTime($str) { + if ($this->isNullOrEmptyString($str) || strlen($str) != 10 || $str[0] !== "[" || $str[9] != "]") { + return FALSE; + } + for ($count = 1; $count < 9; $count++) { + $ch = $str[$count]; + if ($ch !== ":" && $ch !== "." && !is_numeric($ch)) { + return FALSE; + } + } + return TRUE; + } + + // Function for basic field validation (present and neither empty nor only white space + private static function isNullOrEmptyString($question){ + return (!isset($question) || trim($question)===''); + } + + ///////////////////////////// Netease API /////////////////////////////////////// + + /** + * Searches for a song based on title. + */ + private static function search($word) { + $params = array( + 'w' => $word, + 'format' => 'json', + ); + + $curl = curl_init(); + curl_setopt_array($curl, array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_URL => "https://c.y.qq.com/soso/fcgi-bin/client_search_cp?".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) { + $url = "https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_yqq.fcg?nobase64=1&musicid=" . $music_id . "&format=json"; + + $curl = curl_init(); + + curl_setopt_array($curl, array( + CURLOPT_URL => $url, + CURLOPT_HTTPHEADER => array( + "Referer:http://y.qq.com/portal/song/002OrhQA0bNYFg.html" + ), + CURLOPT_RETURNTRANSFER => true + )); + $output = curl_exec($curl); + curl_close($curl); + return $output; + } +} // End of class + + +////////////////////////// DEBUG //////////////////////////////////// + +if (DEBUG == true) { + class TestObj { + private $items; + + function __construct() { + $this->items = array(); + } + + public function addLyrics($lyric, $id) { + printf("
"); + printf("song id: %s\n", $id); + printf("
"); + printf("== lyric ==\n"); + printf("%s\n", $lyric); + printf("** END of lyric **\n\n"); + } + + public function addTrackInfoToList($artist, $title, $id, $prefix) { + printf("
"); + printf("song id: %s\n", $id); + printf("artist [%s]\n", $artist); + printf("title [%s]\n", $title); + printf("prefix [%s]\n", $prefix); + printf("
"); + + array_push($this->items, array( + 'artist' => $artist, + 'title' => $title, + 'id' => $id + )); + } + + function getItems() { + return $this->items; + } + + function getFirstItem() { + if (count($this->items) > 0) { + return $this->items[0]; + } else { + return NULL; + } + } + } + + /** + * Main + */ + $title = "微光"; + $artist = "梁静茹"; + echo "Trying to find lyrics for ['$title'] by artist ['$artist'] ...
"; + + $testObj = new TestObj(); + $downloader = (new ReflectionClass("LudysuQQLrc"))->newInstance(); + $count = $downloader->getLyricsList($artist, $title, $testObj); + if ($count > 0) { + $item = $testObj->getFirstItem(); + // var_dump($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"; + } +} + +