预览

image.png

migu.py

  1. # -*- coding: utf-8 -*-
  2. import os
  3. import threading
  4. import webbrowser
  5. import requests
  6. from flask import Flask
  7. from flask import render_template, send_from_directory
  8. from flask import request
  9. from flask_cors import CORS
  10. # html同级
  11. app = Flask(__name__, template_folder=".")
  12. CORS(app, resources='/*')
  13. app.jinja_env.variable_start_string = '{['
  14. app.jinja_env.variable_end_string = ']}'
  15. def request_parse():
  16. if request.method == 'POST' or request.method == 'PUT':
  17. return request.json
  18. elif request.method == 'GET' or request.method == 'DELETE':
  19. return request.args
  20. @app.route('/getList', methods=['GET'])
  21. def getList():
  22. # region 参数
  23. data = request_parse()
  24. pageNum = data.get("pageNum")
  25. pageSize = data.get("pageSize")
  26. keyword = data.get("keyword")
  27. if not keyword:
  28. return {
  29. "resultCode": 400,
  30. "msg": "未输入歌手|歌名"
  31. }
  32. params = {
  33. 'feature': '1111000000',
  34. 'isCopyright': 1,
  35. 'isCorrect': 1,
  36. 'pageIndex': pageNum,
  37. 'pageSize': pageSize,
  38. 'searchSwitch': '{"song":1,"album":0,"singer":0,"tagSong":1,"mvSong":0,"songlist":0,"bestShow":1,"lyricSong":0,"concert":0,"periodical":0,"ticket":0,"bit24":0,"verticalVideoTone":0}',
  39. 'sid': 'E02D5216B02446F0BB85363041291C22B8EAB6797442459B9C1C5B29DEED3612',
  40. 'sort': 0,
  41. 'text': keyword,
  42. 'uiVersion': 'I_music_3.0.2',
  43. }
  44. headers = {
  45. "User-Agent": "MGMobileMusic/6.9.9 (iPhone; iOS 13.1.3; Scale/2.00)",
  46. "version": "6.9.9",
  47. "uiVersion": "I_music_3.0.2",
  48. "timeStamp": "1605096959",
  49. "sign": "8a560d2511c002186d2dfa0bac44b9b7",
  50. }
  51. resp = requests.get('http://jadeite.migu.cn:7090/music_search/v2/search/searchAll', params=params, headers=headers)
  52. print(resp.url)
  53. resultList = resp.json()["songResultData"]["resultList"]
  54. resultNum = resp.json()["resultNum"]
  55. result = []
  56. for itemList in resultList:
  57. for item in itemList:
  58. singers = ','.join([singer.get("name", "") for singer in item.get('singers')])
  59. song = {
  60. 'song_name': item.get('name'), # 歌名
  61. 'singers': singers # 歌手字符串集合
  62. }
  63. # rate_list = item.get('rateFormats', [])
  64. rate_list = item.get('newRateFormats', [])
  65. urls = {}
  66. for x in rate_list:
  67. if x.get('url', None):
  68. urls[x.get('formatType')] = x.get('url').replace('ftp://218.200.160.122:21',
  69. 'http://freetyst.nf.migu.cn')
  70. urls[x.get('formatType') + "_size"] = int(x.get('size')) / 1024 / 1024
  71. urls[x.get('formatType') + "_size"] = round(urls[x.get('formatType') + "_size"], 2)
  72. else:
  73. format_type = x.get('formatType')
  74. if format_type == 'SQ':
  75. urls['SQ'] = x.get('androidUrl').replace('ftp://218.200.160.122:21',
  76. 'http://freetyst.nf.migu.cn')
  77. urls["SQ_size"] = int(x.get('androidSize')) / 1024 / 1024
  78. urls["SQ_size"] = round(urls["SQ_size"], 2)
  79. elif format_type == 'ZQ':
  80. urls['ZQ'] = x.get('androidUrl').replace('ftp://218.200.160.122:21',
  81. 'http://freetyst.nf.migu.cn')
  82. urls["ZQ_size"] = int(x.get('androidSize')) / 1024 / 1024
  83. urls["ZQ_size"] = round(urls["ZQ_size"], 2)
  84. song['urls'] = urls
  85. result.append(song)
  86. return {
  87. "resultCode": 200,
  88. "total": resultNum,
  89. "rows": result,
  90. }
  91. @app.route('/downloadSong', methods=['GET'])
  92. def downloadSong():
  93. data = request_parse()
  94. singers = data.get("singers")
  95. song_name = data.get("song_name").strip()
  96. url = data.get("url")
  97. size = data.get("size")
  98. # static/down/歌曲名/大小/文件
  99. dir_path = "static/down/" + song_name + "/" + size
  100. if not os.path.exists(dir_path):
  101. os.makedirs(dir_path)
  102. file_name = singers + " - " + song_name + url[url.rfind("."):] # 起风了.mp3
  103. file_path = dir_path + "/" + file_name
  104. if not os.path.exists(file_path):
  105. print("开始下载...")
  106. with open(file_path, mode="wb") as f:
  107. f.write(requests.get(url).content)
  108. print("下载成功:", file_path)
  109. else:
  110. print("文件存在,直接返回")
  111. return send_from_directory(dir_path, file_name, as_attachment=True)
  112. @app.route("/")
  113. def index():
  114. return render_template('index.html')
  115. def openUrl(port):
  116. if os.path.exists("static/down"):
  117. for root, dirs, files in os.walk("static/down"):
  118. for file in files:
  119. file_path = os.path.join(root, file)
  120. if os.path.getsize(file_path) == 0:
  121. os.remove(file_path)
  122. print("清理空文件:", file_path)
  123. webbrowser.open("http://127.0.0.1:{}".format(port), new=0)
  124. @app.errorhandler(Exception)
  125. def error_handler(e):
  126. print("全局异常捕获: {}".format(e))
  127. data = {
  128. "resultCode": 500,
  129. "msg": str(e)
  130. }
  131. return data
  132. if __name__ == '__main__':
  133. port = 777
  134. if not os.environ.get('WERKZEUG_RUN_MAIN'):
  135. th = threading.Thread(target=openUrl, args=(port,))
  136. th.start()
  137. else:
  138. print("热加载\n")
  139. app.run(host="0.0.0.0", port=port, debug=True)

index.html

  1. <!DOCTYPE html>
  2. <html lang="zh">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>咪咕音乐</title>
  6. <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
  7. <script src="https://unpkg.com/vue/dist/vue.js"></script>
  8. <script src="https://unpkg.com/element-ui/lib/index.js"></script>
  9. <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
  10. <link rel="shortcut icon" href="static/favicon.ico" type="image/x-icon"/>
  11. </head>
  12. <style>
  13. body {
  14. width: 70%;
  15. margin: auto;
  16. margin-top: 20px;
  17. }
  18. ::-webkit-scrollbar {
  19. width: 0;
  20. }
  21. </style>
  22. <body>
  23. <div id="app">
  24. <div class="app-container">
  25. <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
  26. <el-form-item label="" prop="keyword">
  27. <el-input
  28. v-model="queryParams.keyword"
  29. placeholder="请输入歌曲|歌手"
  30. clearable
  31. size="mini"
  32. @keyup.enter.native="handleQuery"/>
  33. </el-form-item>
  34. <el-form-item>
  35. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  36. </el-form-item>
  37. <el-form-item style="margin-top: 5px">
  38. <el-pagination
  39. background
  40. :current-page.sync="queryParams.pageNum"
  41. :page-sizes="[10, 15, 20, 25]"
  42. :page-size.sync="queryParams.pageSize"
  43. layout="total, sizes, prev, pager, next"
  44. @size-change="getList"
  45. @current-change="getList"
  46. :total="total">
  47. </el-pagination>
  48. </el-form-item>
  49. </el-form>
  50. <el-table v-loading="loading" element-loading-text="正在进行中,请等待..." :data="songList">
  51. <el-table-column label="歌名" align="center" prop="song_name"></el-table-column>
  52. <el-table-column label="歌手" align="center" prop="singers"></el-table-column>
  53. <el-table-column label="流畅" align="center">
  54. <template slot-scope="scope" v-if="scope.row.urls && scope.row.urls.LQ">
  55. <el-link type="primary" :underline="false" target="_blank"
  56. :href="`/downloadSong?singers=${scope.row.singers}&size=${scope.row.urls.LQ_size}&song_name=${scope.row.song_name}&url=${scope.row.urls.LQ}`">
  57. {{scope.row.urls.LQ_size}}M
  58. </el-link>
  59. </template>
  60. </el-table-column>
  61. <el-table-column label="普通" align="center">
  62. <template slot-scope="scope" v-if="scope.row.urls && scope.row.urls.PQ">
  63. <el-link type="primary" :underline="false" target="_blank"
  64. :href="`/downloadSong?singers=${scope.row.singers}&size=${scope.row.urls.PQ_size}&song_name=${scope.row.song_name}&url=${scope.row.urls.PQ}`">
  65. {{scope.row.urls.PQ_size}}M
  66. </el-link>
  67. </template>
  68. </el-table-column>
  69. <el-table-column label="高清" align="center">
  70. <template slot-scope="scope" v-if="scope.row.urls && scope.row.urls.HQ">
  71. <el-link type="primary" :underline="false" target="_blank"
  72. :href="`/downloadSong?singers=${scope.row.singers}&size=${scope.row.urls.HQ_size}&song_name=${scope.row.song_name}&url=${scope.row.urls.HQ}`">
  73. {{scope.row.urls.HQ_size}}M
  74. </el-link>
  75. </template>
  76. </el-table-column>
  77. <el-table-column label="无损" align="center">
  78. <template slot-scope="scope" v-if="scope.row.urls && scope.row.urls.SQ">
  79. <el-link type="primary" :underline="false" target="_blank"
  80. :href="`/downloadSong?singers=${scope.row.singers}&size=${scope.row.urls.SQ_size}&song_name=${scope.row.song_name}&url=${scope.row.urls.SQ}`">
  81. {{scope.row.urls.SQ_size}}M
  82. </el-link>
  83. </template>
  84. </el-table-column>
  85. <el-table-column label="至臻" align="center">
  86. <template slot-scope="scope" v-if="scope.row.urls && scope.row.urls.ZQ">
  87. <el-link type="primary" :underline="false" target="_blank"
  88. :href="`/downloadSong?singers=${scope.row.singers}&size=${scope.row.urls.ZQ_size}&song_name=${scope.row.song_name}&url=${scope.row.urls.ZQ}`">
  89. {{scope.row.urls.ZQ_size}}M
  90. </el-link>
  91. </template>
  92. </el-table-column>
  93. </el-table>
  94. </div>
  95. </div>
  96. </body>
  97. <script>
  98. baseUrl = "http://127.0.0.1:777"
  99. new Vue({
  100. el: '#app',
  101. data() {
  102. return {
  103. // 遮罩层
  104. loading: false,
  105. // 总条数
  106. total: 0,
  107. songList: [],
  108. // 查询参数
  109. queryParams: {
  110. pageNum: 1,
  111. pageSize: 10,
  112. keyword: "起风了",
  113. },
  114. };
  115. },
  116. created() {
  117. },
  118. methods: {
  119. // 查询列表
  120. getList() {
  121. this.loading = true;
  122. axios({
  123. method: 'get',
  124. url: baseUrl + '/getList',
  125. params: this.queryParams,
  126. }).then(res => {
  127. const response = res.data;
  128. const resultCode = response.resultCode
  129. if (resultCode === 200) {
  130. this.songList = response.rows;
  131. this.total = response.total;
  132. } else {
  133. this.$message({
  134. message: '查询失败,' + response.msg,
  135. type: 'error'
  136. });
  137. }
  138. this.loading = false;
  139. }).catch(function (error) {
  140. console.log(error);
  141. });
  142. },
  143. /** 搜索按钮操作 */
  144. handleQuery() {
  145. this.queryParams.pageNum = 1;
  146. this.getList();
  147. },
  148. downloadSong(singers, song_name, url) {
  149. axios({
  150. method: 'get',
  151. url: baseUrl + '/downloadSong',
  152. params: {
  153. "singers": singers,
  154. "song_name": song_name,
  155. "url": url
  156. },
  157. }).then(res => {
  158. /*const response = res.data;
  159. const resultCode = response.resultCode
  160. if (resultCode === 200) {
  161. } else {
  162. this.$message({
  163. message: '下载失败,' + response.msg,
  164. type: 'error'
  165. });
  166. }*/
  167. }).catch(function (error) {
  168. console.log(error);
  169. });
  170. },
  171. changeLink(link) {
  172. window.location.href = link
  173. }
  174. }
  175. })
  176. </script>
  177. </html>