r107191 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r107190‎ | r107191 | r107192 >
Date:00:12, 24 December 2011
Author:grafzahl
Status:deferred (Comments)
Tags:score 
Comment:
Integration with [[Extension:OggHandler]]
Modified paths:
  • /trunk/extensions/Score/README (modified) (history)
  • /trunk/extensions/Score/Score.body.php (modified) (history)
  • /trunk/extensions/Score/Score.i18n.php (modified) (history)
  • /trunk/extensions/Score/Score.php (modified) (history)

Diff [purge]

Index: trunk/extensions/Score/Score.i18n.php
@@ -40,12 +40,17 @@
4141 'score-noabcinput' => 'ABC source file $1 could not be created.',
4242 'score-nofactory' => 'Failed to create LilyPond factory directory.',
4343 'score-noinput' => 'Failed to create LilyPond input file $1.',
 44+ 'score-noogghandler' => 'Ogg/Vorbis conversion requires an installed and configured OggHandler extension, see https://www.mediawiki.org/wiki/Extension:OggHandler.',
4445 '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.',
4546 'score-nooutput' => 'Failed to create LilyPond image directory $1.',
4647 'score-notexecutable' => 'Could not execute LilyPond: $1 is not an executable file. Make sure <code>$wgLilyPond</code> is set correctly.',
 48+ 'score-novorbislink' => 'Unable to generate Ogg/Vorbis link: $1',
 49+ 'score-oggconversionerr' => 'Unable to convert MIDI to Ogg/Vorbis:
 50+$1',
4751 'score-page' => 'Page $1',
4852 'score-pregreplaceerr' => 'PCRE regular expression replacement failed',
4953 'score-readerr' => 'Unable to read file $1.',
 54+ 'score-timiditynotexecutable' => 'TiMidity++ could not be executed: $1 is not an executable file. Make sure <code>$wgTimidity</code> is set correctly.',
5055 'score-renameerr' => 'Error moving score files to upload directory.',
5156 'score-trimerr' => 'Image could not be trimmed:
5257 $1
@@ -67,13 +72,17 @@
6873 '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.',
6974 'score-nofactory' => 'Displayed if the LilyPond/ImageMagick working directory cannot be created.',
7075 'score-noinput' => 'Displayed if the LilyPond input file cannot be created. $1 is the path to the input file.',
 76+ 'score-noogghandler' => 'Displayed if Ogg/Vorbis rendering was requested without the OggHandler extension installed.',
7177 'score-nomidi' => 'Displayed if MIDI file generation was requested but no MIDI file was generated.',
7278 'score-nooutput' => 'Displayed if the LilyPond image/midi dir cannot be created. $1 is the name of the directory.',
7379 'score-notexecutable' => 'Displayed if LilyPond binary cannot be executed. $1 is the path to the LilyPond binary.',
 80+ 'score-novorbislink' => 'Displayed if an Ogg/Vorbis link could not be generated. $1 is the explanation why.',
 81+ 'score-oggconversionerr' => 'Displayed if the MIDI to Ogg/Vorbis conversion failed. $1 is the error (generally big block of text in a pre tag)',
7482 'score-page' => 'The word "Page" as used in pagination. $1 is the page number',
7583 'score-pregreplaceerr' => 'Displayed if a PCRE regular expression replacement failed.',
7684 'score-readerr' => 'Displayed if the extension could not read a file. $1 is the path to the file that could not be read.',
7785 'score-renameerr' => 'Displayed if moving the resultant files from the working environment to the upload directory fails.',
 86+ 'score-timiditynotexecutable' => 'Displayed if TiMidity++ could not be executed. $1 is the path to the TiMidity++ binary.',
7887 '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)',
7988 'score-versionerr' => 'Displayed if the extension failed to obtain the version string of LilyPond. $1 is the LilyPond stdout output generated by the attempt.',
8089 );
Index: trunk/extensions/Score/Score.php
@@ -45,15 +45,22 @@
4646 /* Whether to trim the score images. Requires ImageMagick.
4747 * Default is $wgUseImageMagick and set in efScoreExtension */
4848 $wgScoreTrim = null;
 49+
4950 /* Path of lilypond executable */
5051 if ( !isset( $wgLilyPond ) ) {
5152 $wgLilyPond = '/usr/bin/lilypond';
5253 }
 54+
5355 /* Path to converter from ABC */
5456 if ( !isset( $wgAbc2Ly ) ) {
5557 $wgAbc2Ly = '/usr/bin/abc2ly';
5658 }
5759
 60+/* Path to TiMidity++ */
 61+if ( !isset( $wgTimidity ) ) {
 62+ $wgTimidity = '/usr/bin/timidity';
 63+}
 64+
5865 /*
5966 * Extension credits
6067 */
Index: trunk/extensions/Score/README
@@ -7,9 +7,17 @@
88 LilyPond installation. If you want the extension to trim the score files for
99 you, you will also need ImageMagick.
1010
11 -This extension was tested with MediaWiki 1.18.0 and LilyPond 2.12.3.
 11+The extension is also capable of creating Ogg/Vorbis files from the MIDI files
 12+generated by LilyPond. If you want to make use of this functionality, you need
 13+to have the OggHandler extension installed, see
1214
 15+https://www.mediawiki.org/wiki/Extension:OggHandler
1316
 17+for more information.
 18+
 19+This extension was tested with MediaWiki 1.19alpha from SVN and LilyPond 2.12.3.
 20+
 21+
1422 Setup
1523 =====
1624
@@ -28,6 +36,8 @@
2937 $wgLilyPond = '/path/to/your/lilypond/executable'; /* required */
3038 $wgAbc2Ly = '/path/to/your/abc2ly/executable'; /* if you want ABC to
3139 LilyPond conversion */
 40+ $wgTimidty = '/path/to/your/timidty/executable'; /* if you want MIDI to
 41+ Vorbis conversion */
3242 $wgScoreTrim = true; /* Set to false if you don't want score trimming */
3343
3444 to your LocalSettings.php file. If you get unexpected out-of-memory errors,
@@ -52,13 +62,22 @@
5363 <score midi="1">…</score>
5464
5565 and the rendered image will be embedded into a hyperlink to an appropriate
56 -MIDI file. For more complex scores, you may supply a complete lilypond file
 66+MIDI file. Use
 67+
 68+<score vorbis="1">…</score>
 69+
 70+and the rendered image will be embedded in an Ogg/Vorbis handler (provided you
 71+installed the OggHandler extension, see above under Prerequisites). Since the
 72+Vorbis file is created from the midi, the "vorbis" attribute implies the
 73+"midi" attribute.
 74+
 75+For more complex scores, you may supply a complete lilypond file
5776 with
5877
5978 <score raw="1">…</score>
6079
61 -You may also combine the "raw" and "midi" attributes, but remember that in
62 -this case, you need to provide the necessary \midi block yourself.
 80+You may also combine the "raw" and "midi"/"vorbis" attributes, but remember
 81+that in this case, you need to provide the necessary \midi block yourself.
6382
6483 The extension also supports the ABC music notation through LilyPond's abc2ly
6584 converter (see the documentation for $wgAbc2Ly above). Use
Index: trunk/extensions/Score/Score.body.php
@@ -67,6 +67,11 @@
6868 const LILYPOND_DIR_NAME = 'lilypond';
6969
7070 /**
 71+ * Default audio player width.
 72+ */
 73+ const DEFAULT_PLAYER_WIDTH = 300;
 74+
 75+ /**
7176 * Supported score languages.
7277 */
7378 private static $supportedLangs = array( 'lilypond', 'ABC' );
@@ -163,8 +168,20 @@
164169 throw new ScoreException( wfMessage( 'score-invalidlang', $options['lang'] ) );
165170 }
166171
 172+ /* Vorbis rendering? */
 173+ if ( array_key_exists( 'vorbis', $args ) ) {
 174+ $options['vorbis'] = $args['vorbis'];
 175+ } else {
 176+ $options['vorbis'] = false;
 177+ }
 178+ if ( $options['vorbis'] && !( class_exists( 'OggHandler' ) && class_exists( 'OggAudioDisplay' ) ) ) {
 179+ throw new ScoreException( wfMessage( 'score-noogghandler' ) );
 180+ }
 181+
167182 /* Midi rendering? */
168 - if ( array_key_exists( 'midi', $args ) ) {
 183+ if ( $options['vorbis'] ) {
 184+ $options['midi'] = true;
 185+ } elseif ( array_key_exists( 'midi', $args ) ) {
169186 $options['midi'] = $args['midi'];
170187 } else {
171188 $options['midi'] = false;
@@ -191,6 +208,7 @@
192209 * @param $code score code.
193210 * @param $options array of music rendering options. Available options keys are:
194211 * * lang: score language,
 212+ * * vorbis: whether to create an Ogg/Vorbis file in an OggHandler,
195213 * * midi: whether to link to a MIDI file,
196214 * * raw: whether to assume raw LilyPond code.
197215 *
@@ -199,7 +217,7 @@
200218 * @throws ScoreException if an error occurs.
201219 */
202220 private static function generateHTML( $code, $options ) {
203 - global $wgUploadDirectory, $wgUploadPath;
 221+ global $wgUploadDirectory, $wgUploadPath, $wgTmpDirectory, $wgOut;
204222
205223 /* Various paths and file names */
206224 $cacheName = md5( $code ); /* always use MD5 of $code, regardless of language */
@@ -215,6 +233,8 @@
216234 $multiFormat = "$filePrefix-%d.png"; // for multi-page scores
217235 $multiPathFormat = "$pathPrefix-%d.png";
218236 $multi1 = "$filePrefix-1.png";
 237+ $ogg = "$filePrefix.ogg";
 238+ $oggPath = "$pathPrefix.ogg";
219239
220240 /* Make sure $lilypondDir exists */
221241 if ( !file_exists( $lilypondDir ) ) {
@@ -224,33 +244,57 @@
225245 }
226246 }
227247
228 - /* Generate PNG and MIDI files if necessary */
229 - if ( ( !file_exists( $image ) && !file_exists( $multi1 ) ) || ( $options['midi'] && !file_exists( $midi ) ) ) {
230 - self::generatePngAndMidi( $code, $options, $filePrefix );
231 - }
 248+ /* Generate working environment data */
 249+ $fuzz = md5( mt_rand() );
 250+ $factoryDirectory = $wgTmpDirectory . "/MWLP.$fuzz";
232251
233 - /* return output link(s) */
234 - if ( file_exists( $image ) ) {
235 - $link = Html::rawElement( 'img', array(
236 - 'src' => $imagePath,
237 - 'alt' => $code,
238 - ) );
239 - } elseif ( file_exists( $multi1 ) ) {
240 - $link = '';
241 - for ( $i = 1; file_exists( sprintf( $multiFormat, $i ) ); ++$i ) {
242 - $link .= Html::rawElement( 'img', array(
243 - 'src' => sprintf( $multiPathFormat, $i ),
244 - 'alt' => wfMessage( 'score-page' )->inContentLanguage()->numParams( $i )->plain()
 252+ try {
 253+ /* Generate PNG and MIDI files if necessary */
 254+ if ( ( !file_exists( $image ) && !file_exists( $multi1 ) ) || ( $options['midi'] && !file_exists( $midi ) ) ) {
 255+ self::generatePngAndMidi( $code, $options, $filePrefix, $factoryDirectory );
 256+ }
 257+
 258+ /* Generate Ogg/Vorbis file if necessary */
 259+ if ( $options['vorbis'] && !file_exists( $ogg ) ) {
 260+ self::generateOgg( $options, $filePrefix, $factoryDirectory );
 261+ }
 262+
 263+ /* return output link(s) */
 264+ if ( file_exists( $image ) ) {
 265+ $link = Html::rawElement( 'img', array(
 266+ 'src' => $imagePath,
 267+ 'alt' => $code,
245268 ) );
 269+ } elseif ( file_exists( $multi1 ) ) {
 270+ $link = '';
 271+ for ( $i = 1; file_exists( sprintf( $multiFormat, $i ) ); ++$i ) {
 272+ $link .= Html::rawElement( 'img', array(
 273+ 'src' => sprintf( $multiPathFormat, $i ),
 274+ 'alt' => wfMessage( 'score-page' )->inContentLanguage()->numParams( $i )->plain()
 275+ ) );
 276+ }
 277+ } else {
 278+ /* No images; this may actually happen in raw lilypond mode */
 279+ self::debug( "No output images $image or $multi1!\n" );
 280+ $link = 'No image';
246281 }
247 - } else {
248 - /* No images; this may actually happen in raw lilypond mode */
249 - self::debug( "No output images $image or $multi1!\n" );
250 - $link = 'No image';
 282+ if ( $options['midi'] ) {
 283+ $link = Html::rawElement( 'a', array( 'href' => $midiPath ), $link );
 284+ }
 285+ if ( $options['vorbis'] ) {
 286+ try {
 287+ $oh = new OggHandler();
 288+ $oh->setHeaders( $wgOut );
 289+ $oad = new OggAudioDisplay( new UnregisteredLocalFile( false, false, $ogg ), $oggPath, self::DEFAULT_PLAYER_WIDTH, 0, 0, $oggPath, false );
 290+ $link .= $oad->toHtml( array( 'alt' => $code ) );
 291+ } catch ( Exception $e ) {
 292+ throw new ScoreException( wfMessage( 'score-novorbislink', $e->getMessage() ), 0, $e );
 293+ }
 294+ }
 295+ } catch ( Exception $e ) {
 296+ self::eraseFactory( $factoryDirectory );
 297+ throw $e;
251298 }
252 - if ( $options['midi'] ) {
253 - $link = Html::rawElement( 'a', array( 'href' => $midiPath ), $link );
254 - }
255299
256300 return $link;
257301 }
@@ -261,11 +305,12 @@
262306 * @param $code score code.
263307 * @param $options rendering options, see Score::generateHTML() for explanation.
264308 * @param $filePrefix prefix for the generated files.
 309+ * @param $factoryDirectory directory of the working environment.
265310 *
266311 * @throws ScoreException on error.
267312 */
268 - private static function generatePngAndMidi( $code, $options, $filePrefix ) {
269 - global $wgTmpDirectory, $wgLilyPond, $wgScoreTrim;
 313+ private static function generatePngAndMidi( $code, $options, $filePrefix, $factoryDirectory ) {
 314+ global $wgLilyPond, $wgScoreTrim;
270315
271316 wfProfileIn( __METHOD__ );
272317
@@ -291,9 +336,7 @@
292337 throw new ScoreException( wfMessage( 'score-cleanerr' ) );
293338 }
294339
295 - /* Create a working environment */
296 - $fuzz = md5( mt_rand() );
297 - $factoryDirectory = $wgTmpDirectory . "/MWLP.$fuzz";
 340+ /* Create the working environment */
298341 self::createFactory( $factoryDirectory );
299342 $factoryLy = "$factoryDirectory/file.ly";
300343 $factoryMidi = "$factoryDirectory/file.midi";
@@ -383,13 +426,10 @@
384427 throw new ScoreException( wfMessage( 'score-renameerr' ) );
385428 }
386429 } catch ( Exception $e ) {
387 - self::eraseFactory( $factoryDirectory );
388430 wfProfileOut( __METHOD__ );
389431 throw $e;
390432 }
391433
392 - /* tear down working environment */
393 - self::eraseFactory( $factoryDirectory );
394434 wfProfileOut( __METHOD__ );
395435 }
396436
@@ -429,6 +469,54 @@
430470 }
431471
432472 /**
 473+ * Generates an Ogg/Vorbis file from a MIDI file using timidity.
 474+ *
 475+ * @param $options rendering options, see Score::generateHTML() for explanation.
 476+ * @param $filePrefix prefix for the generated Ogg file.
 477+ * @param $factoryDirectory directory of the working environment.
 478+ *
 479+ * @throws ScoreException if an error occurs.
 480+ */
 481+ private static function generateOgg( $options, $filePrefix, $factoryDirectory ) {
 482+ global $wgTimidity;
 483+
 484+ wfProfileIn( __METHOD__ );
 485+
 486+ try {
 487+ /* Working environment */
 488+ self::createFactory( $factoryDirectory );
 489+ $factoryOgg = "$factoryDirectory/file.ogg";
 490+ $midi = "$filePrefix.midi";
 491+ $ogg = "$filePrefix.ogg";
 492+
 493+ /* Run timidity */
 494+ if ( !is_executable( $wgTimidity ) ) {
 495+ throw new ScoreException( wfMessage( 'score-timiditynotexecutable', $wgTimidity ) );
 496+ }
 497+ $cmd = wfEscapeShellArg( $wgTimidity )
 498+ . ' -Ov' // Vorbis output
 499+ . ' --output-file=' . wfEscapeShellArg( $factoryOgg )
 500+ . ' ' . wfEscapeShellArg( $midi )
 501+ . ' 2>&1';
 502+ $output = wfShellExec( $cmd, $rc );
 503+ if ( ( $rc != 0 ) || !file_exists( $factoryOgg ) ) {
 504+ self::throwCallException( wfMessage( 'score-oggconversionerr' ), $output );
 505+ }
 506+
 507+ /* Move resultant file to proper place */
 508+ $rc = rename( $factoryOgg, $ogg );
 509+ if ( !$rc ) {
 510+ throw new ScoreException( wfMessage( 'score-renameerr' ) );
 511+ }
 512+ } catch ( Exception $e ) {
 513+ wfProfileOut( __METHOD__ );
 514+ throw $e;
 515+ }
 516+
 517+ wfProfileOut( __METHOD__ );
 518+ }
 519+
 520+ /**
433521 * Generates LilyPond code.
434522 *
435523 * @param $code score code.

Follow-up revisions

RevisionCommit summaryAuthorDate
r107216Use protocol-relative URL. Fixes r107191.grafzahl18:38, 24 December 2011

Comments

#Comment by Reedy (talk | contribs)   17:23, 24 December 2011

Minor issue:

+	'score-noogghandler' => 'Ogg/Vorbis conversion requires an installed and configured OggHandler extension, see [https://www.mediawiki.org/wiki/Extension:OggHandler.', https://www.mediawiki.org/wiki/Extension:OggHandler.',]

We try to use Protocol Relative urls now, or at least, default to https if we know we can. Use something like:

+	'score-noogghandler' => 'Ogg/Vorbis conversion requires an installed and configured OggHandler extension, see [[//www.mediawiki.org/wiki/Extension:OggHandler|OggHandler]].',
#Comment by GrafZahl (talk | contribs)   18:39, 24 December 2011

Fixed in r107216, thanks!

Status & tagging log