jenkins-bot has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/402590 )

Change subject: Use MP3 with LAME instead of OGG for MIDI conversion
......................................................................


Use MP3 with LAME instead of OGG for MIDI conversion

This patch utilizes the lame command in order to convert
from fluidsynth/timidty generated WAV files to MP3.

Also increased max filesize for large MIDIs.

Depends on change 402148.

Bug: T181875
Change-Id: I4b87d0bb665e824fe934d714bf2e282b1bbe8318
---
M README
M extension.json
M i18n/en.json
M i18n/qqq.json
M includes/Score.php
5 files changed, 134 insertions(+), 79 deletions(-)

Approvals:
  Ebe123: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/README b/README
index d12f9fb..b407698 100644
--- a/README
+++ b/README
@@ -42,10 +42,11 @@
    $wgScoreAbc2Ly = '/path/to/your/abc2ly/executable'; /* if you want ABC to
                                                           LilyPond conversion 
*/
    $wgScoreFluidsynth = '/path/to/your/fluidsynth/executable'; /* if you want 
MIDI to
-                                                                  Vorbis 
conversion */
+                                                                  MP3 
conversion */
    $wgScoreSoundfont = '/path/to/your/soundfont/file'; /* required for 
Fluidsynth */
    $wgScoreTimidity = '/path/to/your/timidity/executable'; /* fallback when 
Fluidsynth
                                                               is not installed 
*/
+   $wgScoreLame = '/path/to/your/lame/executable' /* required to convert sound 
*/
    $wgScoreTrim = true; /* Set to false if you don't want score trimming */
    $wgScoreSafeMode = false; /* Set to true if the Lilypond executable is
                                 running in a Firejail or equivalent */
diff --git a/extension.json b/extension.json
index 7dc5b2c..5c27e5f 100644
--- a/extension.json
+++ b/extension.json
@@ -90,6 +90,7 @@
                "ScoreFluidsynth": "/usr/bin/fluidsynth",
                "ScoreSoundfont": "/usr/share/sounds/sf2/FluidR3_GM.sf2",
                "ScoreTimidity": "/usr/bin/timidity",
+               "ScoreLame": "/usr/bin/lame",
                "ScoreSafeMode": true,
                "ScorePath": false,
                "ScoreDirectory": false,
diff --git a/i18n/en.json b/i18n/en.json
index 3ec220c..87a2363 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -4,37 +4,39 @@
        },
        "score-abc2lynotexecutable": "ABC to LilyPond converter could not be 
executed: $1 is not an executable file. Make sure <code>$wgScoreAbc2Ly</code> 
is set correctly.",
        "score-abcconversionerr": "Unable to convert ABC file to LilyPond 
format:\n$1",
+       "score-audioconversionerr": "Unable to convert MIDI to MP3:\n$1",
+       "score-audiooverridenotfound": "The file \"<nowiki>$1</nowiki>\" you 
specified with override_audio does not exist.",
+       "score-backend-error": "Unable to copy the generated files to their 
final location:\n$1",
        "score-chdirerr": "Unable to change to directory $1",
        "score-cleanerr": "Unable to clean out old files before re-rendering",
        "score-compilererr": "Unable to compile LilyPond input file:\n$1",
-       "score-backend-error": "Unable to copy the generated files to their 
final location:\n$1",
+       "score-convertoverrideaudio": "You cannot request audio rendering and 
specify override_audio at the same time.",
        "score-desc": "Adds a tag for rendering musical scores with LilyPond",
        "score-download-midi-file": "Download MIDI file",
        "score-download-source-file": "Download lilypond file",
        "score-error-category": "Pages with score rendering errors",
        "score-error-category-desc": "There was an error while rendering the 
score.",
+       "score-fallbacknotexecutable": "TiMidity++ could not be executed as 
fallback: $1 is not an executable file. Make sure <code>$wgScoreTimidity</code> 
or <code>$wgScoreFluidsynth</code> is set correctly.",
        "score-getcwderr": "Unable to obtain current working directory",
+       "score-invalidaudiooverride": "The file \"<nowiki>$1</nowiki>\" you 
specified with override_audio is invalid. Please specify the file name only, 
omit <nowiki>[[…]]</nowiki> and the \"{{ns:file}}:\" prefix.",
        "score-invalidlang": "Invalid score language 
lang=\"<nowiki>$1</nowiki>\". Currently recognized languages are 
lang=\"lilypond\" (the default) and lang=\"ABC\".",
        "score-invalidnotelanguage": "Invalid 
note-language=\"<nowiki>$1</nowiki>\". Currently recognized note languages are: 
$2",
-       "score-invalidaudiooverride": "The file \"<nowiki>$1</nowiki>\" you 
specified with override_audio is invalid. Please specify the file name only, 
omit <nowiki>[[…]]</nowiki> and the \"{{ns:file}}:\" prefix.",
-       "score-notelanguagewithraw": "Attribute \"note-language\" cannot be 
used for raw mode scores",
+       "score-lamenotexecutable": "LAME could not be executed: $1 is not an 
executable file. Make sure <code>$wgScoreLame</code> is set correctly.",
        "score-midioverridenotfound": "The file \"<nowiki>$1</nowiki>\" you 
specified with override_midi could not be found. Please specify the file name 
only, omit <nowiki>[[…]]</nowiki> and the \"{{ns:file}}:\" prefix.",
        "score-noabcinput": "ABC source file $1 could not be created.",
+       "score-nocontent": "Could not load file $1 from server.",
        "score-noimages": "No score images were generated. Please check your 
score code.",
        "score-noinput": "Failed to create LilyPond input file $1.",
-       "score-nomediahandler": "Ogg/Vorbis conversion requires an installed 
and configured version of the 
[https://www.mediawiki.org/wiki/Extension:TimedMediaHandler TimedMediaHandler 
extension].",
+       "score-nomediahandler": "Audio conversion requires an installed and 
configured version of the 
[https://www.mediawiki.org/wiki/Extension:TimedMediaHandler TimedMediaHandler 
extension].",
        "score-nomidi": "No MIDI file generated despite being requested. If you 
are working in raw LilyPond mode, make sure to provide a proper \\midi block.",
        "score-nooutput": "Failed to create output directory $1.",
+       "score-notelanguagewithraw": "Attribute \"note-language\" cannot be 
used for raw mode scores",
        "score-notexecutable": "Could not execute LilyPond: $1 is not an 
executable file. Make sure <code>$wgScoreLilyPond</code> is set correctly.",
-       "score-nocontent": "Could not load file $1 from server.",
-       "score-oggconversionerr": "Unable to convert MIDI to Ogg/Vorbis:\n$1",
-       "score-audiooverridenotfound": "The file \"<nowiki>$1</nowiki>\" you 
specified with override_audio does not exist.",
        "score-page": "Page $1",
        "score-pregreplaceerr": "PCRE regular expression replacement failed",
        "score-readerr": "Unable to read file $1.",
-       "score-fallbacknotexecutable": "TiMidity++ could not be executed as 
fallback: $1 is not an executable file. Make sure <code>$wgScoreTimidity</code> 
or <code>$wgScoreFluidsynth</code> is set correctly.",
-       "score-soundfontnotexists": "Soundfont could not be found: $1 does not 
exists. Make sure <code>$wgScoreSoundfont</code> is set correctly.",
        "score-renameerr": "Error moving score files to upload directory.",
+       "score-soundfontnotexists": "Soundfont could not be found: $1 does not 
exists. Make sure <code>$wgScoreSoundfont</code> is set correctly.",
        "score-trimerr": "Image could not be trimmed:\n$1\nSet 
<code>$wgScoreTrim=false</code> if this problem persists.",
        "score-versionerr": "Unable to obtain LilyPond version:\n$1",
        "score-visualeditor-mwscoreinspector-card-advanced": "Advanced",
@@ -50,6 +52,5 @@
        "score-visualeditor-mwscoreinspector-override-ogg-placeholder": "Name 
of existing audio file (must be Ogg/Vorbis)",
        "score-visualeditor-mwscoreinspector-raw": "This is a complete LilyPond 
file",
        "score-visualeditor-mwscoreinspector-title": "Musical notation",
-       "score-visualeditor-mwscoreinspector-vorbis": "Include an audio file 
(auto-generated by default)",
-       "score-vorbisoverrideaudio": "You cannot request audio rendering and 
specify override_audio at the same time."
+       "score-visualeditor-mwscoreinspector-vorbis": "Include an audio file 
(auto-generated by default)"
 }
diff --git a/i18n/qqq.json b/i18n/qqq.json
index d2593ad..241191c 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -10,37 +10,39 @@
        },
        "score-abc2lynotexecutable": "Displayed if the ABC to LilyPond 
converter could not be executed. $1 is the path to the abc2ly binary.",
        "score-abcconversionerr": "Displayed if the ABC to LilyPond conversion 
failed. $1 is the error (generally big block of text in a pre tag)",
+       "score-audioconversionerr": "Displayed if the MIDI to MP3 conversion 
failed. $1 is the error (generally big block of text in a pre tag)",
+       "score-audiooverridenotfound": "Displayed if the file specified with 
the override_audio=\"…\" attribute could not be found. $1 is the value of the 
override_ogg attribute.",
+       "score-backend-error": "Parameters:\n* $1 - result message which was 
returned",
        "score-chdirerr": "Displayed if the extension cannot change its working 
directory. $1 is the path to the target directory.",
        "score-cleanerr": "Displayed if an old file cleanup operation fails.",
        "score-compilererr": "Displayed if the LilyPond code could not be 
compiled. $1 is the error (generally big block of text in a pre tag)",
-       "score-backend-error": "Parameters:\n* $1 - result message which was 
returned",
+       "score-convertoverrideaudio": "Displayed if both sound=\"1\" and 
override_audio=\"…\" were specified.",
        "score-desc": 
"{{desc|name=Score|url=https://www.mediawiki.org/wiki/Extension:Score}}";,
        "score-download-midi-file": "Content of link to download MIDI file of 
score shown in score popup",
        "score-download-source-file": "Content of link to download the lilypond 
file of score shown in score popup",
        "score-error-category": "Name of [[mw:Help:Tracking categories|tracking 
category]] to list pages where there was an error rendering the 
<code><nowiki><score></nowiki></code> tag.",
        "score-error-category-desc": "Description on 
[[Special:TrackingCategories]] for the {{msg-mw|score-error-category}} tracking 
category.",
+       "score-fallbacknotexecutable": "Displayed if TiMidity++ could not be 
executed as fallback. $1 is the path to the TiMidity++ binary.",
        "score-getcwderr": "Displayed if the extension cannot obtain the 
current working directory.",
+       "score-invalidaudiooverride": "Displayed if the file specified with the 
override_audio=\"…\" attribute is invalid. $1 is the value of the 
override_audio attribute.",
        "score-invalidlang": "Displayed if the lang=\"…\" attribute contains an 
unrecognized score language. $1 is the unrecognized language.",
        "score-invalidnotelanguage": "Displayed if the note-language=\"…\" 
attribute contains an unrecognized note language. $1 is the unrecognized note 
language. $2 is comma separated list of available languages.",
-       "score-invalidaudiooverride": "Displayed if the file specified with the 
override_audio=\"…\" attribute is invalid. $1 is the value of the 
override_audio attribute.",
-       "score-notelanguagewithraw": "Displayed if the \"note-language\" 
attribute is used for scores in raw mode",
+       "score-lamenotexecutable": "Displayed if LAME could not be executed. $1 
is the path to the LAME binary.",
        "score-midioverridenotfound": "Displayed if the file specified with the 
override_midi=\"…\" attribute could not be found. $1 is the value of the 
override_midi attribute.",
        "score-noabcinput": "Displayed if an ABC source file could not be 
created for lang=\"ABC\". $1 is the path to the file that could not be 
created.",
+       "score-nocontent": "Parameters:\n* $1 - filename",
        "score-noimages": "Displayed if no score images were rendered.",
        "score-noinput": "Displayed if the LilyPond input file cannot be 
created. $1 is the path to the input file.",
-       "score-nomediahandler": "Displayed if Ogg/Vorbis rendering was 
requested without the TimedMediaHandler extension installed.",
+       "score-nomediahandler": "Displayed if audio rendering was requested 
without the TimedMediaHandler extension installed.",
        "score-nomidi": "Displayed if MIDI file generation was requested but no 
MIDI file was generated.",
        "score-nooutput": "Displayed if an output directory could not be 
created. $1 is the name of the directory.",
+       "score-notelanguagewithraw": "Displayed if the \"note-language\" 
attribute is used for scores in raw mode",
        "score-notexecutable": "Displayed if LilyPond binary cannot be 
executed. $1 is the path to the LilyPond binary.",
-       "score-nocontent": "Parameters:\n* $1 - filename",
-       "score-oggconversionerr": "Displayed if the MIDI to Ogg/Vorbis 
conversion failed. $1 is the error (generally big block of text in a pre tag)",
-       "score-audiooverridenotfound": "Displayed if the file specified with 
the override_audio=\"…\" attribute could not be found. $1 is the value of the 
override_ogg attribute.",
        "score-page": "The word \"Page\" as used in pagination. Parameters:\n* 
$1 - the page number\n{{Identical|Page}}",
        "score-pregreplaceerr": "Displayed if a PCRE regular expression 
replacement failed.",
        "score-readerr": "Displayed if the extension could not read a file. $1 
is the path to the file that could not be read.",
-       "score-fallbacknotexecutable": "Displayed if TiMidity++ could not be 
executed as fallback. $1 is the path to the TiMidity++ binary.",
-       "score-soundfontnotexists": "Displayed if soundfont could not be found. 
$1 is the path to the soundfont file.",
        "score-renameerr": "Displayed if moving the resultant files from the 
working environment to the upload directory fails.",
+       "score-soundfontnotexists": "Displayed if soundfont could not be found. 
$1 is the path to the soundfont file.",
        "score-trimerr": "Displayed if the extension failed to trim an output 
image. $1 is the error (generally big block of text in a pre tag)",
        "score-versionerr": "Displayed if the extension failed to obtain the 
version string of LilyPond. $1 is the LilyPond stdout output generated by the 
attempt.",
        "score-visualeditor-mwscoreinspector-card-advanced": "Label for the 
advanced card of the score inspector\n{{Identical|Advanced}}",
@@ -56,6 +58,5 @@
        "score-visualeditor-mwscoreinspector-override-ogg-placeholder": 
"Placeholder for the label for the override_ogg attribute of the score node",
        "score-visualeditor-mwscoreinspector-raw": "Label for the raw attribute 
of the score node",
        "score-visualeditor-mwscoreinspector-title": "Title for the inspector 
to edit <nowiki><score></nowiki> blocks.",
-       "score-visualeditor-mwscoreinspector-vorbis": "Label for the vorbis 
attribute of the score node",
-       "score-vorbisoverrideaudio": "Displayed if both sound=\"1\" and 
override_audio=\"…\" were specified."
+       "score-visualeditor-mwscoreinspector-vorbis": "Label for the vorbis 
attribute of the score node"
 }
diff --git a/includes/Score.php b/includes/Score.php
index b7a2583..3016194 100644
--- a/includes/Score.php
+++ b/includes/Score.php
@@ -219,7 +219,7 @@
         * @return string Image link HTML, and possibly anchor to MIDI file.
         */
        public static function render( $code, array $args, Parser $parser, 
PPFrame $frame ) {
-               global $wgTmpDirectory;
+               global $wgTmpDirectory, $wgScoreLame;
 
                try {
                        $baseUrl = self::getBaseUrl();
@@ -242,6 +242,9 @@
                                        htmlspecialchars( $options['lang'] ) ) 
);
                        }
 
+                       // Set extension for audio output
+                       $options['audio_extension'] = is_executable( 
$wgScoreLame ) ? 'mp3' : 'ogg';
+
                        /* Override MIDI file? */
                        if ( array_key_exists( 'override_midi', $args ) ) {
                                $file = wfFindFile( $args['override_midi'] );
@@ -252,13 +255,13 @@
                                $parser->getOutput()->addImage( 
$file->getName() );
                                $options['override_midi'] = true;
                                $options['midi_file'] = $file;
-                               /* Set OGG stuff in case Vorbis rendering is 
requested */
+                               /* Set output stuff in case audio rendering is 
requested */
                                $sha1 = $file->getSha1();
-                               $oggRelDir = 
"override-midi/{$sha1[0]}/{$sha1[1]}";
-                               $oggRel = "$oggRelDir/$sha1.ogg";
-                               $options['audio_storage_dir'] = 
"$baseStoragePath/$oggRelDir";
-                               $options['audio_storage_path'] = 
"$baseStoragePath/$oggRel";
-                               $options['audio_url'] = "$baseUrl/$oggRel";
+                               $audioRelDir = 
"override-midi/{$sha1[0]}/{$sha1[1]}";
+                               $audioRel = 
"$audioRelDir/$sha1.{$options['audio_extension']}";
+                               $options['audio_storage_dir'] = 
"$baseStoragePath/$audioRelDir";
+                               $options['audio_storage_path'] = 
"$baseStoragePath/$audioRel";
+                               $options['audio_url'] = "$baseUrl/$audioRel";
                        } else {
                                $options['override_midi'] = false;
                        }
@@ -307,16 +310,16 @@
                        }
 
                        /* Audio rendering? */
-                       $options['generate_ogg'] = array_key_exists( 'sound', 
$args )
+                       $options['generate_audio'] = array_key_exists( 'sound', 
$args )
                                || array_key_exists( 'vorbis', $args );
 
-                       if ( $options['generate_ogg']
+                       if ( $options['generate_audio']
                                && !class_exists( 'TimedMediaTransformOutput' )
                        ) {
                                throw new ScoreException( wfMessage( 
'score-nomediahandler' ) );
                        }
-                       if ( $options['generate_ogg'] && ( 
$options['override_audio'] !== false ) ) {
-                               throw new ScoreException( wfMessage( 
'score-vorbisoverrideaudio' ) );
+                       if ( $options['generate_audio'] && ( 
$options['override_audio'] !== false ) ) {
+                               throw new ScoreException( wfMessage( 
'score-convertoverrideaudio' ) );
                        }
 
                        // Input for cache key
@@ -361,7 +364,7 @@
         *      - factory_directory: string Path to directory in which files
         *              may be generated without stepping on someone else's
         *              toes. The directory may not exist yet. Required.
-        *      - generate_ogg: bool Whether to create an Ogg/Vorbis file in
+        *      - generate_audio: bool Whether to create an audio file in
         *              TimedMediaHandler. If set to true, the override_audio 
option
         *              must be set to false. Required.
         *  - dest_storage_path: The path of the destination directory relative 
to
@@ -373,15 +376,17 @@
         *      - override_midi: bool Whether to use a user-provided MIDI file.
         *              Required.
         *      - midi_file: If override_midi is true, MIDI file object.
-        *      - audio_storage_dir: If override_midi and generate_ogg are 
true, the
+        *      - audio_extension: string If override_midi and generate_audio 
are true,
+        *              the audio output format in which the audio file is to 
be generated.
+        *      - audio_storage_dir: If override_midi and generate_audio are 
true, the
         *              backend directory in which the audio file is to be 
stored.
-        *      - audio_storage_path: string If override_midi and generate_ogg 
are true,
+        *      - audio_storage_path: string If override_midi and 
generate_audio are true,
         *              the backend path at which the generated audio file is 
to be
         *              stored.
-        *      - audio_url: string If override_midi and generate_ogg is true,
+        *      - audio_url: string If override_midi and generate_audio is true,
         *              the URL corresponding to audio_storage_path
         *      - override_audio: bool Whether to generate a wikilink to a
-        *              user-provided audio file. If set to true, the vorbis
+        *              user-provided audio file. If set to true, the sound
         *              option must be set to false. Required.
         *      - audio_name: string If override_audio is true, the audio file 
name
         *      - raw: bool Whether to assume raw LilyPond code. Ignored if the
@@ -432,24 +437,24 @@
                                $existingFiles += self::generatePngAndMidi( 
$code, $options, $metaData );
                        }
 
-                       /* Generate Ogg/Vorbis file if necessary */
-                       if ( $options['generate_ogg'] ) {
+                       /* Generate audio file if necessary */
+                       if ( $options['generate_audio'] ) {
                                if ( $options['override_midi'] ) {
-                                       $oggUrl = $options['audio_url'];
-                                       $oggPath = 
$options['audio_storage_path'];
+                                       $audioUrl = $options['audio_url'];
+                                       $audioPath = 
$options['audio_storage_path'];
                                        $exists = $backend->fileExists( [ 'src' 
=> $options['audio_storage_path'] ] );
                                        if ( !$exists ) {
                                                $backend->prepare( [ 'dir' => 
$options['audio_storage_dir'] ] );
                                                $sourcePath = 
$options['midi_file']->getLocalRefPath();
-                                               self::generateOgg( $sourcePath, 
$options, $oggPath, $metaData );
+                                               self::generateAudio( 
$sourcePath, $options, $audioPath, $metaData );
                                        }
                                } else {
-                                       $oggFileName = 
"{$options['file_name_prefix']}.ogg";
-                                       $oggUrl = 
"{$options['dest_url']}/$oggFileName";
-                                       $oggPath = 
"{$options['dest_storage_path']}/$oggFileName";
+                                       $audioFileName = 
"{$options['file_name_prefix']}.{$options['audio_extension']}";
+                                       $audioUrl = 
"{$options['dest_url']}/$audioFileName";
+                                       $audioPath = 
"{$options['dest_storage_path']}/$audioFileName";
                                        if (
-                                               !isset( 
$existingFiles[$oggFileName] ) ||
-                                               !isset( 
$metaData[$oggFileName]['length'] )
+                                               !isset( 
$existingFiles[$audioFileName] ) ||
+                                               !isset( 
$metaData[$audioFileName]['length'] )
                                        ) {
                                                // Maybe we just generated it
                                                $sourcePath = 
"{$options['factory_directory']}/file.midi";
@@ -459,7 +464,7 @@
                                                                [ 'src' => 
"{$options['dest_storage_path']}/$midiFileName" ] );
                                                        $sourcePath = 
$sourceFileRef->getPath();
                                                }
-                                               self::generateOgg( $sourcePath, 
$options, $oggPath, $metaData );
+                                               self::generateAudio( 
$sourcePath, $options, $audioPath, $metaData );
                                        }
                                }
                        }
@@ -497,14 +502,17 @@
                                /* No images; this may happen in raw mode or 
when the user omits the score code */
                                throw new ScoreException( wfMessage( 
'score-noimages' ) );
                        }
-                       if ( $options['generate_ogg'] ) {
-                               $length = $metaData[basename( $oggPath 
)]['length'];
+                       if ( $options['generate_audio'] ) {
+                               $length = $metaData[basename( $audioPath 
)]['length'];
+                               $mimetype = pathinfo( $audioUrl, 
PATHINFO_EXTENSION ) === 'mp3'
+                                       ? 'audio/mpeg'
+                                       : 'audio/ogg; codecs="vorbis"';
                                $player = new TimedMediaTransformOutput( [
                                        'length' => $length,
                                        'sources' => [
                                                [
-                                                       'src' => $oggUrl,
-                                                       'type' => 'audio/ogg; 
codecs="vorbis"'
+                                                       'src' => $audioUrl,
+                                                       'type' => $mimetype
                                                ]
                                        ],
                                        'disablecontrols' => 
'options,timedText',
@@ -627,7 +635,7 @@
                                $options['factory_directory'] );
                }
                $needMidi = false;
-               if ( !$options['raw'] || $options['generate_ogg'] && 
!$options['override_midi'] ) {
+               if ( !$options['raw'] || $options['generate_audio'] && 
!$options['override_midi'] ) {
                        $needMidi = true;
                        if ( !file_exists( $factoryMidi ) ) {
                                throw new ScoreException( wfMessage( 
'score-nomidi' ) );
@@ -798,23 +806,28 @@
        }
 
        /**
-        * Generates an Ogg/Vorbis file from a MIDI file using fluidsynth with 
TiMidity as fallback.
+        * Generates an audio file from a MIDI file using fluidsynth with 
TiMidity as fallback.
         *
         * @param string $sourceFile The local filename of the MIDI file
         * @param array $options array of rendering options.
-        * @param string $remoteDest The backend storage path to upload the Ogg 
file to
+        * @param string $remoteDest The backend storage path to upload the 
audio file to
         * @param array $metaData Array with metadata information
         *
         * @throws ScoreException if an error occurs.
         */
-       private static function generateOgg( $sourceFile, $options, 
$remoteDest, &$metaData ) {
-               global $wgScoreFluidsynth, $wgScoreSoundfont;
+       private static function generateAudio( $sourceFile, $options, 
$remoteDest, &$metaData ) {
+               global $wgScoreFluidsynth, $wgScoreSoundfont, $wgScoreLame;
                global $wgScoreTimidity; // TODO: Remove TiMidity++ as fallback
+
+               // Check whether the output is mp3 or ogg by extension
+               $extension = pathinfo( $remoteDest, PATHINFO_EXTENSION );
+               $isOutputMp3 = $extension === 'mp3';
 
                /* Working environment */
                $factoryDir = $options['factory_directory'];
                self::createDirectory( $factoryDir, 0700 );
-               $factoryOgg = "$factoryDir/file.ogg";
+               $factoryOutput = "$factoryDir/output.wav";
+               $factoryFile = "$factoryDir/file.$extension";
 
                if ( is_executable( $wgScoreFluidsynth ) ) {
                        if ( !file_exists( $wgScoreSoundfont ) ) {
@@ -825,9 +838,9 @@
                        $cmdArgs = [
                                $wgScoreFluidsynth,
                                '-T',
-                               'oga', // Vorbis output
+                               $isOutputMp3 ? 'wav' : 'oga', // wav output if 
mp3
                                '-F',
-                               $factoryOgg,
+                               $factoryOutput,
                                $wgScoreSoundfont,
                                $sourceFile
                        ];
@@ -835,8 +848,8 @@
                        // Use TiMidity++ as a fallback
                        $cmdArgs = [
                                $wgScoreTimidity,
-                               '-Ov', // Vorbis output
-                               '--output-file=' . $factoryOgg,
+                               $isOutputMp3 ? '-Ow' : '-Ov', // wav output if 
mp3
+                               '--output-file=' . $factoryOutput,
                                $sourceFile
                        ];
                } else {
@@ -847,32 +860,61 @@
                $result = Shell::command( $cmdArgs )
                        ->includeStderr()
                        ->restrict( Shell::RESTRICT_DEFAULT | Shell::NO_NETWORK 
)
+                       ->limits( [ 'filesize' => 153600 ] ) // 150 MB max. 
filesize (for large MIDIs)
                        ->execute();
 
-               if ( ( $result->getExitCode() != 0 ) || !file_exists( 
$factoryOgg ) ) {
+               if ( ( $result->getExitCode() != 0 ) || !file_exists( 
$factoryOutput ) ) {
                        self::throwCallException(
-                               wfMessage( 'score-oggconversionerr' ), 
$result->getStdout(), $factoryDir
+                               wfMessage( 'score-audioconversionerr' ), 
$result->getStdout(), $factoryDir
                        );
                }
-               $ops = [];
-               // Move resultant file to proper place
-               $ops[] = [
+
+               if ( $isOutputMp3 ) {
+                       if ( !is_executable( $wgScoreLame ) ) {
+                               throw new ScoreException( wfMessage( 
'score-lamenotexecutable', $wgScoreLame ) );
+                       }
+
+                       /* Convert wav -> mp3 using LAME */
+                       $result = Shell::command( $wgScoreLame, $factoryOutput, 
$factoryFile )
+                               ->includeStderr()
+                               ->restrict( Shell::RESTRICT_DEFAULT | 
Shell::NO_NETWORK )
+                               ->execute();
+
+                       if ( ( $result->getExitCode() != 0 ) || !file_exists( 
$factoryFile ) ) {
+                               self::throwCallException(
+                                       wfMessage( 'score-audioconversionerr' 
), $result->getStdout(), $factoryDir
+                               );
+                       }
+               } else {
+                       // No conversion required for ogg
+                       $factoryFile = $factoryOutput;
+               }
+
+               // Move file to the final destination
+               $backend = self::getBackend();
+               $status = $backend->doQuickOperation( [
                        'op' => 'store',
-                       'src' => $factoryOgg,
-                       'dst' => $remoteDest ];
+                       'src' => $factoryFile,
+                       'dst' => $remoteDest
+               ] );
+
+               if ( !$status->isOK() ) {
+                       throw new ScoreException( wfMessage( 
'score-backend-error', $status->getWikiText() ) );
+               }
 
                // Create metadata json
-               $metaData[basename( $remoteDest )]['length'] = self::getLength( 
$factoryOgg );
+               $metaData[basename( $remoteDest )]['length'] = self::getLength( 
$remoteDest );
                $dstFileName = "{$options['file_name_prefix']}.json";
                $dest = "{$options['dest_storage_path']}/$dstFileName";
-               $ops[] = [
+
+               // Store metadata in backend
+               $backend = self::getBackend();
+               $status = $backend->doQuickOperation( [
                        'op' => 'create',
                        'content' => FormatJson::encode( $metaData ),
-                       'dst' => $dest ];
+                       'dst' => $dest
+               ] );
 
-               // Execute the batch
-               $backend = self::getBackend();
-               $status = $backend->doQuickOperations( $ops );
                if ( !$status->isOK() ) {
                        throw new ScoreException( wfMessage( 
'score-backend-error', $status->getWikiText() ) );
                }
@@ -967,14 +1009,23 @@
        }
 
        /**
-        * get length of ogg vorbis file
+        * get length of audio file
         *
         * @param string $path file system path to file
         *
         * @return float duration in seconds
         */
        private static function getLength( $path ) {
-               $f = new File_Ogg( $path );
+               $isFileMp3 = pathinfo( $path, PATHINFO_EXTENSION ) === 'mp3';
+               $repo = new FileRepo( [
+                       'name' => 'foo',
+                       'backend' => self::$backend
+               ] );
+
+               $f = new UnregisteredLocalFile( false, $repo, $path, $isFileMp3
+                       ? 'audio/mpeg'
+                       : 'audio/ogg'
+               );
                return $f->getLength();
        }
 

-- 
To view, visit https://gerrit.wikimedia.org/r/402590
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I4b87d0bb665e824fe934d714bf2e282b1bbe8318
Gerrit-PatchSet: 20
Gerrit-Project: mediawiki/extensions/Score
Gerrit-Branch: master
Gerrit-Owner: Divadsn <divad.nnamtd...@gmail.com>
Gerrit-Reviewer: Brion VIBBER <br...@wikimedia.org>
Gerrit-Reviewer: Divadsn <divad.nnamtd...@gmail.com>
Gerrit-Reviewer: Ebe123 <beauleetien...@gmail.com>
Gerrit-Reviewer: Siebrand <siebr...@kitano.nl>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to