mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-21 18:45:49 +00:00
XSLT 2.0 stylesheets for chapters to: cue sheets; shnsplit timecodes
This commit is contained in:
parent
1eea0bd871
commit
24d8258430
@ -1,5 +1,11 @@
|
||||
2011-11-04 Moritz Bunkus <moritz@bunkus.org>
|
||||
|
||||
* examples: Added XSLT 2.0 stylesheets in the
|
||||
"examples/stylesheets" directory for turning Matroska chapters
|
||||
into cue sheet and split points for "shntool" (useful for
|
||||
situations in which you have e.g. a live recording from a concert
|
||||
including chapters and want to create one audio file per song).
|
||||
|
||||
* mkvmerge: bug fix: Fixed reading VC1 video tracks from Matroska
|
||||
files that don't use VC1 start markers (0x00 0x00 0x01 ...).
|
||||
|
||||
|
152
examples/stylesheets/chapters-to-cuesheet.xsl
Normal file
152
examples/stylesheets/chapters-to-cuesheet.xsl
Normal file
@ -0,0 +1,152 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
|
||||
Chapters to cue sheet
|
||||
=====================
|
||||
|
||||
This is a XSLT 2.0 stylesheet for turning the chapter format that mkvmerge
|
||||
understands into a cue sheet as used by e.g. CD recording software.
|
||||
|
||||
You need a XSLT 2.0 compatible XSLT processor, e.g. Saxon-HE (see
|
||||
http://saxon.sourceforge.net/).
|
||||
|
||||
Usage depends on your processor. For Saxon-HE on Linux this should work:
|
||||
|
||||
$ java -classpath saxon9he.jar net.sf.saxon.Transform \
|
||||
-o:output-filename.cue \
|
||||
-xsl:chapters-to-cuesheet.xsl \
|
||||
input-filename.xml
|
||||
|
||||
You have to extract the chapters from a Matroska file with mkvextract first if
|
||||
you want to turn the chapters included in one into a cue sheet.
|
||||
|
||||
This can be useful if you want to turn an audio file contain in a Matroska
|
||||
file into one file per chapter (e.g. for music videos). For this you need both
|
||||
"chapters-to-cuesheet.xsl" and "chapters-to-shnsplit.xsl" as well as the
|
||||
"cuetools" (see https://github.com/svend/cuetools) and "shntool" (see
|
||||
http://etree.org/shnutils/shntool/) packages.
|
||||
|
||||
Assumptions are (meaning: adjust these values in the commands):
|
||||
|
||||
- the source file contains an audio track with track ID 2 of type FLAC,
|
||||
- the band is "Perpetuum Jazzile",
|
||||
- the album is "Live 2009",
|
||||
- the source file name is "source.mkv".
|
||||
|
||||
Here we go:
|
||||
|
||||
# 1. Extract audio track from Matroska file:
|
||||
$ mkvextract source.mkv tracks 2:source.flac
|
||||
|
||||
# 2. Extract chapters from same file:
|
||||
$ mkvextract source.mkv chapters > chapters.xml
|
||||
|
||||
# 3. Generate the split points used in step 4:
|
||||
$ java -classpath saxon9he.jar net.sf.saxon.Transform \
|
||||
-o:splitpoints.txt \
|
||||
-xsl:chapters-to-cuesheet.xsl \
|
||||
chapters.xml
|
||||
|
||||
# 4. Split the audio file into multiple files:
|
||||
$ shnsplit -o flac source.flac < splitpoints.txt
|
||||
|
||||
# 5. Generate the cue sheet used in step 6:
|
||||
$ java -classpath saxon9he.jar net.sf.saxon.Transform \
|
||||
-o:source.cue \
|
||||
-xsl:chapters-to-cuesheet.xsl \
|
||||
chapters.xml \
|
||||
artist="Perpetuum Jazzile" \
|
||||
album="Live 2009"
|
||||
|
||||
# 6: Assign tags (artist and title):
|
||||
$ cuetag source.cue split-*.flac
|
||||
|
||||
Written by Moritz Bunkus <moritz@bunkus.org>.
|
||||
|
||||
-->
|
||||
|
||||
<xsl:stylesheet version="2.0"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xsl:param name="album" select="'ChangeMeAlbum'"/>
|
||||
<xsl:param name="artist" select="'ChangeMeAlbumArtist'"/>
|
||||
<xsl:param name="track-artist" select="if ($artist eq 'ChangeMeAlbumArtist') then 'ChangeMeTrackArtist' else $artist"/>
|
||||
|
||||
<xsl:output method="text"/>
|
||||
<xsl:strip-space elements="*"/>
|
||||
|
||||
<xsl:template match="/Chapters">
|
||||
<xsl:apply-templates select="EditionEntry"/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="timecode-to-ns" as="xs:integer">
|
||||
<xsl:param name="timecode" select="'0'"/>
|
||||
<xsl:choose>
|
||||
<xsl:when test="$timecode eq ''">
|
||||
<xsl:value-of select="0"/>
|
||||
</xsl:when>
|
||||
<xsl:when test="matches($timecode, '^\d+:\d+:\d+\.\d{9}$')">
|
||||
<xsl:analyze-string select="$timecode"
|
||||
regex="^ (\d+) : (\d+) : (\d+) \. (\d+) $"
|
||||
flags="x">
|
||||
<xsl:matching-substring>
|
||||
<xsl:value-of select=" (regex-group(1) cast as xs:integer) * 3600000000000
|
||||
+ (regex-group(2) cast as xs:integer) * 60000000000
|
||||
+ (regex-group(3) cast as xs:integer) * 1000000000
|
||||
+ (regex-group(4) cast as xs:integer)"/>
|
||||
</xsl:matching-substring>
|
||||
</xsl:analyze-string>
|
||||
</xsl:when>
|
||||
<xsl:when test="matches($timecode, '^\d+:\d+:\d+\.\d*$')">
|
||||
<xsl:call-template name="timecode-to-ns">
|
||||
<xsl:with-param name="timecode" select="concat($timecode, '0')"/>
|
||||
</xsl:call-template>
|
||||
</xsl:when>
|
||||
<xsl:when test="matches($timecode, '^\d+:\d+:\d+$')">
|
||||
<xsl:call-template name="timecode-to-ns">
|
||||
<xsl:with-param name="timecode" select="concat($timecode, '.')"/>
|
||||
</xsl:call-template>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:call-template name="timecode-to-ns">
|
||||
<xsl:with-param name="timecode" select="concat('0:', $timecode)"/>
|
||||
</xsl:call-template>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="EditionEntry">
|
||||
<xsl:text>PERFORMER "</xsl:text>
|
||||
<xsl:value-of select="$artist"/>
|
||||
<xsl:text>"
|
||||
TITLE "</xsl:text>
|
||||
<xsl:value-of select="$album"/>
|
||||
<xsl:text>"
|
||||
FILE "ChangeMe.mp3" MP3
|
||||
</xsl:text>
|
||||
<xsl:for-each select="ChapterAtom">
|
||||
<xsl:variable name="timecode" as="xs:integer">
|
||||
<xsl:call-template name="timecode-to-ns">
|
||||
<xsl:with-param name="timecode" select="ChapterTimeStart"/>
|
||||
</xsl:call-template>
|
||||
</xsl:variable>
|
||||
<xsl:text> TRACK </xsl:text>
|
||||
<xsl:number format="01" value="position()"/>
|
||||
<xsl:text> AUDIO
|
||||
PERFORMER "</xsl:text>
|
||||
<xsl:value-of select="$track-artist"/>
|
||||
<xsl:text>"
|
||||
TITLE "</xsl:text>
|
||||
<xsl:value-of select="ChapterDisplay[1]/ChapterString"/>
|
||||
<xsl:text>"
|
||||
INDEX 01 </xsl:text>
|
||||
<xsl:number format="01" value=" $timecode idiv 60000000000"/>
|
||||
<xsl:text>:</xsl:text>
|
||||
<xsl:number format="01" value="($timecode idiv 1000000000) mod 60"/>
|
||||
<xsl:text>:</xsl:text>
|
||||
<xsl:value-of select="($timecode mod 1000000000) * 75 idiv 1000000000"/>
|
||||
<xsl:text>
</xsl:text>
|
||||
</xsl:for-each>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
137
examples/stylesheets/chapters-to-shnsplit.xsl
Normal file
137
examples/stylesheets/chapters-to-shnsplit.xsl
Normal file
@ -0,0 +1,137 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
|
||||
Chapters to shnsplit
|
||||
====================
|
||||
|
||||
This is a XSLT 2.0 stylesheet for turning the chapter format that mkvmerge
|
||||
understands into a list of timecodes for the "shnsplit" tool from the
|
||||
"shntool" package (see http://etree.org/shnutils/shntool/). It allows you to
|
||||
split a large audio file into smaller ones, optionally encoding/decoding in
|
||||
the process.
|
||||
|
||||
You need a XSLT 2.0 compatible XSLT processor, e.g. Saxon-HE (see
|
||||
http://saxon.sourceforge.net/).
|
||||
|
||||
Usage depends on your processor. For Saxon-HE on Linux this should work:
|
||||
|
||||
$ java -classpath saxon9he.jar net.sf.saxon.Transform \
|
||||
-o:splitpoints.txt \
|
||||
-xsl:chapters-to-shnsplit.xsl \
|
||||
input-filename.xml \
|
||||
artist="Name of the artist" \
|
||||
album="Name of the album"
|
||||
|
||||
You have to extract the chapters from a Matroska file with mkvextract first if
|
||||
you want to turn the chapters included in one into split points.
|
||||
|
||||
This can be useful if you want to turn an audio file contain in a Matroska
|
||||
file into one file per chapter (e.g. for music videos). For this you need both
|
||||
"chapters-to-cuesheet.xsl" and "chapters-to-shnsplit.xsl" as well as the
|
||||
"cuetools" (see https://github.com/svend/cuetools) and "shntool" (see
|
||||
http://etree.org/shnutils/shntool/) packages.
|
||||
|
||||
Assumptions are (meaning: adjust these values in the commands):
|
||||
|
||||
- the source file contains an audio track with track ID 2 of type FLAC,
|
||||
- the band is "Perpetuum Jazzile",
|
||||
- the album is "Live 2009",
|
||||
- the source file name is "source.mkv".
|
||||
|
||||
Here we go:
|
||||
|
||||
# 1. Extract audio track from Matroska file:
|
||||
$ mkvextract source.mkv tracks 2:source.flac
|
||||
|
||||
# 2. Extract chapters from same file:
|
||||
$ mkvextract source.mkv chapters > chapters.xml
|
||||
|
||||
# 3. Generate the split points used in step 4:
|
||||
$ java -classpath saxon9he.jar net.sf.saxon.Transform \
|
||||
-o:splitpoints.txt \
|
||||
-xsl:chapters-to-cuesheet.xsl \
|
||||
chapters.xml
|
||||
|
||||
# 4. Split the audio file into multiple files:
|
||||
$ shnsplit -o flac source.flac < splitpoints.txt
|
||||
|
||||
# 5. Generate the cue sheet used in step 6:
|
||||
$ java -classpath saxon9he.jar net.sf.saxon.Transform \
|
||||
-o:source.cue \
|
||||
-xsl:chapters-to-cuesheet.xsl \
|
||||
chapters.xml \
|
||||
artist="Perpetuum Jazzile" \
|
||||
album="Live 2009"
|
||||
|
||||
# 6: Assign tags (artist and title):
|
||||
$ cuetag source.cue split-*.flac
|
||||
|
||||
Written by Moritz Bunkus <moritz@bunkus.org>.
|
||||
|
||||
-->
|
||||
|
||||
<xsl:stylesheet version="2.0"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xsl:output method="text"/>
|
||||
<xsl:strip-space elements="*"/>
|
||||
|
||||
<xsl:template match="/Chapters">
|
||||
<xsl:apply-templates select="EditionEntry"/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="timecode-to-ns" as="xs:integer">
|
||||
<xsl:param name="timecode" select="'0'"/>
|
||||
<xsl:choose>
|
||||
<xsl:when test="$timecode eq ''">
|
||||
<xsl:value-of select="0"/>
|
||||
</xsl:when>
|
||||
<xsl:when test="matches($timecode, '^\d+:\d+:\d+\.\d{9}$')">
|
||||
<xsl:analyze-string select="$timecode"
|
||||
regex="^ (\d+) : (\d+) : (\d+) \. (\d+) $"
|
||||
flags="x">
|
||||
<xsl:matching-substring>
|
||||
<xsl:value-of select=" (regex-group(1) cast as xs:integer) * 3600000000000
|
||||
+ (regex-group(2) cast as xs:integer) * 60000000000
|
||||
+ (regex-group(3) cast as xs:integer) * 1000000000
|
||||
+ (regex-group(4) cast as xs:integer)"/>
|
||||
</xsl:matching-substring>
|
||||
</xsl:analyze-string>
|
||||
</xsl:when>
|
||||
<xsl:when test="matches($timecode, '^\d+:\d+:\d+\.\d*$')">
|
||||
<xsl:call-template name="timecode-to-ns">
|
||||
<xsl:with-param name="timecode" select="concat($timecode, '0')"/>
|
||||
</xsl:call-template>
|
||||
</xsl:when>
|
||||
<xsl:when test="matches($timecode, '^\d+:\d+:\d+$')">
|
||||
<xsl:call-template name="timecode-to-ns">
|
||||
<xsl:with-param name="timecode" select="concat($timecode, '.')"/>
|
||||
</xsl:call-template>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:call-template name="timecode-to-ns">
|
||||
<xsl:with-param name="timecode" select="concat('0:', $timecode)"/>
|
||||
</xsl:call-template>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="EditionEntry">
|
||||
<xsl:for-each select="ChapterAtom">
|
||||
<xsl:variable name="timecode" as="xs:integer">
|
||||
<xsl:call-template name="timecode-to-ns">
|
||||
<xsl:with-param name="timecode" select="ChapterTimeStart"/>
|
||||
</xsl:call-template>
|
||||
</xsl:variable>
|
||||
<xsl:if test="$timecode > 0">
|
||||
<xsl:number format="01" value=" $timecode idiv 60000000000"/>
|
||||
<xsl:text>:</xsl:text>
|
||||
<xsl:number format="01" value="($timecode idiv 1000000000) mod 60"/>
|
||||
<xsl:text>.</xsl:text>
|
||||
<xsl:number format="001" value="($timecode mod 1000000000) idiv 1000000"/>
|
||||
<xsl:text>
</xsl:text>
|
||||
</xsl:if>
|
||||
</xsl:for-each>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
Loading…
Reference in New Issue
Block a user