voice_upload.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. const { default: axios } = require('axios')
  2. var xml2js = require('xml2js')
  3. const createOption = require('../util/option.js')
  4. var parser = new xml2js.Parser(/* options */)
  5. function createDupkey() {
  6. // 格式:3b443c7c-a87f-468d-ba38-46d407aaf23a
  7. var s = []
  8. var hexDigits = '0123456789abcdef'
  9. for (var i = 0; i < 36; i++) {
  10. s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
  11. }
  12. s[14] = '4' // bits 12-15 of the time_hi_and_version field to 0010
  13. s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1) // bits 6-7 of the clock_seq_hi_and_reserved to 01
  14. s[8] = s[13] = s[18] = s[23] = '-'
  15. return s.join('')
  16. }
  17. module.exports = async (query, request) => {
  18. let ext = 'mp3'
  19. if (query.songFile.name.indexOf('flac') > -1) {
  20. ext = 'flac'
  21. }
  22. const filename =
  23. query.songName ||
  24. query.songFile.name
  25. .replace('.' + ext, '')
  26. .replace(/\s/g, '')
  27. .replace(/\./g, '_')
  28. if (!query.songFile) {
  29. return Promise.reject({
  30. status: 500,
  31. body: {
  32. msg: '请上传音频文件',
  33. code: 500,
  34. },
  35. })
  36. }
  37. const tokenRes = await request(
  38. `/api/nos/token/alloc`,
  39. {
  40. bucket: 'ymusic',
  41. ext: ext,
  42. filename: filename,
  43. local: false,
  44. nos_product: 0,
  45. type: 'other',
  46. },
  47. createOption(query, 'weapi'),
  48. )
  49. const objectKey = tokenRes.body.result.objectKey.replace('/', '%2F')
  50. const docId = tokenRes.body.result.docId
  51. const res = await axios({
  52. method: 'post',
  53. url: `https://ymusic.nos-hz.163yun.com/${objectKey}?uploads`,
  54. headers: {
  55. 'x-nos-token': tokenRes.body.result.token,
  56. 'X-Nos-Meta-Content-Type': 'audio/mpeg',
  57. },
  58. data: null,
  59. })
  60. // return xml
  61. const res2 = await parser.parseStringPromise(res.data)
  62. const fileSize = query.songFile.data.length
  63. const blockSize = 10 * 1024 * 1024 // 10MB
  64. let offset = 0
  65. let blockIndex = 1
  66. let etags = []
  67. while (offset < fileSize) {
  68. const chunk = query.songFile.data.slice(
  69. offset,
  70. Math.min(offset + blockSize, fileSize),
  71. )
  72. const res3 = await axios({
  73. method: 'put',
  74. url: `https://ymusic.nos-hz.163yun.com/${objectKey}?partNumber=${blockIndex}&uploadId=${res2.InitiateMultipartUploadResult.UploadId[0]}`,
  75. headers: {
  76. 'x-nos-token': tokenRes.body.result.token,
  77. 'Content-Type': 'audio/mpeg',
  78. },
  79. data: chunk,
  80. })
  81. // get etag
  82. const etag = res3.headers.etag
  83. etags.push(etag)
  84. offset += blockSize
  85. blockIndex++
  86. }
  87. let completeStr = '<CompleteMultipartUpload>'
  88. for (let i = 0; i < etags.length; i++) {
  89. completeStr += `<Part><PartNumber>${i + 1}</PartNumber><ETag>${
  90. etags[i]
  91. }</ETag></Part>`
  92. }
  93. completeStr += '</CompleteMultipartUpload>'
  94. // 文件处理
  95. await axios({
  96. method: 'post',
  97. url: `https://ymusic.nos-hz.163yun.com/${objectKey}?uploadId=${res2.InitiateMultipartUploadResult.UploadId[0]}`,
  98. headers: {
  99. 'Content-Type': 'text/plain;charset=UTF-8',
  100. 'X-Nos-Meta-Content-Type': 'audio/mpeg',
  101. 'x-nos-token': tokenRes.body.result.token,
  102. },
  103. data: completeStr,
  104. })
  105. // preCheck
  106. await request(
  107. 'post',
  108. `/api/voice/workbench/voice/batch/upload/preCheck`,
  109. {
  110. dupkey: createDupkey(),
  111. voiceData: JSON.stringify([
  112. {
  113. name: filename,
  114. autoPublish: query.autoPublish == 1 ? true : false,
  115. autoPublishText: query.autoPublishText || '',
  116. description: query.description,
  117. voiceListId: query.voiceListId,
  118. coverImgId: query.coverImgId,
  119. dfsId: docId,
  120. categoryId: query.categoryId,
  121. secondCategoryId: query.secondCategoryId,
  122. composedSongs: query.composedSongs
  123. ? query.composedSongs.split(',')
  124. : [],
  125. privacy: query.privacy == 1 ? true : false,
  126. publishTime: query.publishTime || 0,
  127. orderNo: query.orderNo || 1,
  128. },
  129. ]),
  130. },
  131. {
  132. ...createOption(query),
  133. headers: {
  134. 'x-nos-token': tokenRes.body.result.token,
  135. },
  136. },
  137. )
  138. const result = await request(
  139. 'post',
  140. `/api/voice/workbench/voice/batch/upload/v2`,
  141. {
  142. dupkey: createDupkey(),
  143. voiceData: JSON.stringify([
  144. {
  145. name: filename,
  146. autoPublish: query.autoPublish == 1 ? true : false,
  147. autoPublishText: query.autoPublishText || '',
  148. description: query.description,
  149. voiceListId: query.voiceListId,
  150. coverImgId: query.coverImgId,
  151. dfsId: docId,
  152. categoryId: query.categoryId,
  153. secondCategoryId: query.secondCategoryId,
  154. composedSongs: query.composedSongs
  155. ? query.composedSongs.split(',')
  156. : [],
  157. privacy: query.privacy == 1 ? true : false,
  158. publishTime: query.publishTime || 0,
  159. orderNo: query.orderNo || 1,
  160. },
  161. ]),
  162. },
  163. {
  164. ...createOption(query),
  165. headers: {
  166. 'x-nos-token': tokenRes.body.result.token,
  167. },
  168. },
  169. )
  170. return {
  171. status: 200,
  172. body: {
  173. code: 200,
  174. data: result.body.data,
  175. },
  176. }
  177. }