Source: routers/sheetMusicPart.js

  1. const express = require('express');
  2. const auth = require('../middleware/authenticator.js');
  3. const verify = require("../middleware/verifier.js");
  4. const mysql = require("../db/mysql.js");
  5. const alphaProcesser = require('../AlphaTab/ProcessAlphaTab.js');
  6. const Manipulator = require("../Parser/ParsedOutputManipulator.js");
  7. const ParserPData = require("../Parser/ParserPerformanceData.js");
  8. const ParserMeasures = require("../Parser/ParserMeasures.js");
  9. const updateExerciseTable = require("../util/updateExercise.js");
  10. const router = express.Router();
  11. /**
  12. * @typedef {object} SheetMusicPartGetPackage
  13. * @property {string} sheet_music The created AlphaTex isolating the user's part
  14. * @property {string[]} part_list A list of part (i.e. track) names in the generated AlphaTex
  15. * @property {string[]} clefs Clefs per staff
  16. * @property {number[]} performance_expectation A 1D array of even size with the ith index as the midi value and the i+1 index as the duration of that note for i%2==0
  17. * @property {number[]} lower_upper A 1D two valued array with the lower and upper midi values
  18. * @property {number[]} measure_lengths A collection of the lengths of each Measure. The ith index contains the length in seconds of the i+1 Measure
  19. * @property {string} exerciseId The ID of the exercise created
  20. */
  21. /**
  22. * Route for getting only the user's part from the given sheet music. Creates AlphaTex with their part isolated
  23. * @name sheet-music-part/get
  24. * @function
  25. * @inner
  26. * @param {*} req
  27. * @param {string} req.query.sheetMusicId The sheet music ID to be accessed
  28. * @param {*} res
  29. * @returns {SheetMusicPartGetPackage|string} The created isolated sheet music and information about it. Otherwise, an error message if an error occured
  30. * @async
  31. */
  32. router.get('/', auth.token, async (req,res) => {
  33. verify.memberOfSheetMusic(req, res).then((choirId) => {
  34. if (choirId === null) {
  35. res.status(403).send('Unauthorized');
  36. } else {
  37. let sheetMusicId = req.query.sheetMusicId;
  38. const errorMessage = "Unable to get single part from sheet music";
  39. if (sheetMusicId) {
  40. mysql.getClient().then((client) => {
  41. client.query("SELECT hex(member_id) AS member_id FROM tma.member WHERE person_id=? AND choir_id=unhex(?)", [res.locals.uid, choirId], function(error, results, fields) {
  42. if (error) {
  43. res.status(503).send(errorMessage);
  44. } else if (results.length === 0) {
  45. res.status(204).send(errorMessage);
  46. } else {
  47. let memberId = results[0]["member_id"];
  48. client.query("SELECT part FROM tma.member_sheet_music_part WHERE member_id=unhex(?) AND sheet_music_id=unhex(?)", [memberId, sheetMusicId], function(error, results, fields) {
  49. if (error) {
  50. res.status(503).send(errorMessage);
  51. } else if (results.length === 0) {
  52. res.status(204).send(errorMessage);
  53. } else {
  54. let partName = results[0]["part"];
  55. client.query("SELECT performance_data FROM tma.sheet_music WHERE sheet_music_id = unhex(?)", [sheetMusicId], function(error, results, fields) {
  56. if (error) {
  57. res.status(503).send(errorMessage);
  58. } else {
  59. let performanceData = JSON.parse(results[0]['performance_data']);
  60. const measureParser = new ParserMeasures(performanceData);
  61. let measureEnd = measureParser.manipulator.getMeasureEnd(partName, 1);
  62. let trackIndex = measureParser.manipulator.getTrackIndexByName(partName);
  63. if (measureEnd === null) {
  64. res.status(400).send(errorMessage);
  65. } else {
  66. let {lyricText, alphaTex} = measureParser.measuresToAlphaTex([1, measureEnd, "all"], trackIndex + 1, 1, false);
  67. let exercisePerformanceData = alphaProcesser.process(alphaTex, {0:lyricText});
  68. let partNames = ParserPData.getPartNames(exercisePerformanceData.mainObj);
  69. let clefs = ParserPData.getClefs(exercisePerformanceData.mainObj);
  70. let noteStream = ParserPData.getNoteStream(exercisePerformanceData.mainObj, partName);
  71. let lowerAndUpper = ParserPData.getLowerAndUpper(noteStream);
  72. let manipulator = new Manipulator(exercisePerformanceData.mainObj);
  73. let measureLengths = manipulator.getMeasureTimeLength(partName);
  74. let retObj = {
  75. "sheet_music": alphaTex,
  76. "part_list": partNames,
  77. clefs,
  78. "performance_expectation": noteStream,
  79. "lower_upper": lowerAndUpper,
  80. "measure_lengths": measureLengths,
  81. };
  82. updateExerciseTable(sheetMusicId, trackIndex + 1, 0, 1, measureEnd, false).then((exerciseId) => {
  83. retObj.exerciseId = exerciseId;
  84. res.status(200).json(retObj);
  85. });
  86. }
  87. }
  88. });
  89. }
  90. });
  91. }
  92. });
  93. });
  94. } else {
  95. res.status(400).send(errorMessage);
  96. }
  97. }
  98. }).catch((error) => {
  99. res.status(403).send('Unauthorized');
  100. });
  101. });
  102. /**
  103. * Route for assigning a part to a user for a piece of sheet music
  104. * @name sheet-music-part/post
  105. * @function
  106. * @inner
  107. * @param {*} req
  108. * @param {string} req.body.memberId The member ID to be accessed
  109. * @param {string} req.body.sheetMusicId The sheet music ID for the part
  110. * @param {string} req.body.part The part to be assigned to the member
  111. * @param {*} res
  112. * @returns {string} An error message if an error occured
  113. * @async
  114. */
  115. router.post('/', auth.token, async (req,res) => {
  116. verify.userIsMember(req, res).then((isMember) => {
  117. if (!isMember) {
  118. res.status(403).send('Unauthorized');
  119. } else {
  120. verify.memberOfSheetMusic(req, res).then((choirId) => {
  121. if (choirId === null) {
  122. res.status(403).send('Unauthorized');
  123. } else {
  124. let sheetMusicId = req.body.sheetMusicId;
  125. let part = req.body.part;
  126. let memberId = req.body.memberId;
  127. const errorMessage = "Unable to add part for member in sheet music";
  128. if (sheetMusicId && part && memberId) {
  129. mysql.getClient().then((client) => {
  130. client.beginTransaction(function(error) {
  131. client.query("INSERT INTO tma.member_sheet_music_part (member_id, sheet_music_id, part) values(unhex(?), unhex(?), ?)", [memberId, sheetMusicId, part], function(error, results, fields) {
  132. if (error) {
  133. res.status(503).send(errorMessage);
  134. } else {
  135. client.query("SELECT performance_data FROM tma.sheet_music WHERE sheet_music_id = unhex(?)", [sheetMusicId], function(error, results, fields) {
  136. if (error) {
  137. client.rollback(function() {
  138. res.status(503).send(errorMessage);
  139. });
  140. } else {
  141. let performanceData = JSON.parse(results[0]['performance_data']);
  142. let {progressData, trackIndex} = ParserPData.createEmptyProgressData(performanceData, part);
  143. let progress = {
  144. index: 0,
  145. data: [progressData]
  146. };
  147. client.query("INSERT INTO tma.performance_progress (performance_progress_id, member_id, sheet_music_id, staff_number, progress_data) values(unhex(replace(uuid(),\"-\",\"\")), unhex(?), unhex(?), ?, ?)", [memberId, sheetMusicId, trackIndex+1, JSON.stringify(progress)], function(error, results, fields) {
  148. if (error) {
  149. client.rollback(function() {
  150. res.status(503).send(errorMessage);
  151. });
  152. } else {
  153. client.commit(function(err) {
  154. if (err) {
  155. client.rollback(function() {
  156. res.status(503).send(errorMessage);
  157. });
  158. } else {
  159. res.status(200).send();
  160. }
  161. });
  162. }
  163. });
  164. }
  165. });
  166. }
  167. });
  168. });
  169. });
  170. } else {
  171. res.status(400).send(errorMessage);
  172. }
  173. }
  174. }).catch((error) => {
  175. res.status(403).send('Unauthorized');
  176. });
  177. }
  178. }).catch((error) => {
  179. res.status(403).send('Unauthorized');
  180. });
  181. });
  182. module.exports = router;