Jenis Script Movable


Script XSLT untuk menghasilkan daftar album dari iTunes XML

Ketika aku sendiri sebuah iPod , saya menemukan dicetak daftar tersedia dari iTunes Program tidak terlalu besar (meskipun antarmuka interaktif yang luar biasa), dan aku tidak bisa mendapatkan daftar ringkasan singkat dari album.Sejak iTunes menyimpan perpustakaan sebagai XML, saya menggunakan XSLT untuk membuat daftar yang lebih sesuai dengan keinginan saya. Karena saya cenderung untuk mendengarkan album daripada single, saya ingin daftar album dikelompokkan berdasarkan genre .

Untuk menggunakan script ini, menyimpan albumList.xml dan albumList.xsl ditunjukkan di bawah untuk Anda My Music / iTunes folder, dan albumList.xml terbuka di Internet Explorer 6 + atau Mozilla Firefox (IE5 tidak akan bekerja, saya belum diuji setiap browser lain).

'Daftar properti' iTunes XML

ITunes library disimpan sebagai XML dalam file yang disebut iTunes Music library.xml . Menggunakan bahasa XML generik dikenal sebagai daftar properti , atau plist , yang memegang informasi sebagai pasangan kunci / nilai. Bukan dirancang untuk menggambarkan lagu, dapat digunakan untuk menggambarkan sifat lebih atau kurang apa pun. Ini adalah bagaimana XML terlihat:

<plist version="1.0">
  <dict>
    <key>Tracks</key>
    <dict>
      <key>638</key>
      <dict>
        <key>Track ID</key><integer>638</integer>
        <key>Name</key><string>Take Me To The River</string>
        <key>Artist</key><string>Talking Heads</string>
        <key>Composer</key><string>Al Green &#38; M. Hodges</string>
        <key>Album</key><string>Stop Making Sense</string>
        <key>Genre</key><string>Alternative &#38; Punk</string>
        ... etc ...
      </dict>
    </dict>
  </dict>
</plist>   

Ini berarti bahwa untuk menemukan, katakanlah, nama album, kita perlu mencocokkan dengan saudara segera sebelum dari 'Album':

string [sebelumnya-saudara :: tombol [1] = 'Album']

Artinya, elemen mana 1 unsur dalam sumbu sebelumnya-saudara memiliki nilai 'Album'.

Sementara ini adalah tujuan umum bahasa XML nyaman, sudah pasti lebih kompleks untuk memanipulasi dari bahasa XML disesuaikan untuk mewakili daftar trek, sehingga mengambil napas dalam-dalam untuk apa berikut!

Mendapatkan daftar album

ITunes XML adalah daftar lagu. Album lagu muncul pada tidak lebih, benar-benar, daripada atribut lagu, sehingga pendekatan yang sederhana akan memberikan nama album sekali untuk setiap lagu yang muncul di atasnya. Jadi untuk mendapatkan daftar yang benar album, saya menggunakan trik agak pintar yang tersedia dengan  elemen dan kunci () dan menghasilkan-id () fungsi.

orang lain telah menjelaskan penggunaan lebih baik maka saya bisa, tapi secara singkat, di sini, elemen membangun daftar node mewakili lagu. The elemen kemudian menyeleksi (yaitu lagu) unsur-unsur di mana id secara otomatis sesuai dengan id otomatis dihasilkan dari node pertama dikembalikan oleh tombol () fungsi. Tidak, itu membuat kepalaku berputar, juga. stylesheet yang dihasilkan untuk mendapatkan daftar album (dengan artis terkait) adalah: 

<xsl:key name="songsByAlbum" match="dict"
  use="string[preceding-sibling::key[1]='Album']"/>

<xsl:template match="/plist/dict/dict">
  <html>
    <body>
      <table>

        <xsl:for-each select="dict[generate-id(.)=
            generate-id(key('songsByAlbum',string)[1])]">
          <xsl:sort select="string[preceding-sibling::key[1]='Album']"/>
          <tr>
            <td><xsl:value-of select="string[preceding-sibling::key[1]='Album']"/></td>
            <td><xsl:value-of select="string[preceding-sibling::key[1]='Artist']"/></td>
          </tr>
        </xsl:for-each>

      </table>
    </body>
  </html>
</xsl:template>

Ini memiliki masalah dengan kompilasi, meskipun. Ini akan mengambil siapa pun adalah artis untuk lagu pertama dari kompilasi, yang terlihat agak aneh. Untuk memperbaiki ini, saya menggunakan 'Bagian dari kompilasi' bendera sebagai berikut: 

<xsl:key name="songsByAlbum" match="dict"
  use="string[preceding-sibling::key[1]='Album']"/>

<xsl:template match="/plist/dict/dict">
  <html>
    <body>
      <table>

        <xsl:for-each select="dict[generate-id(.)=generate-id(key('albums',string)[1])]">
          <xsl:sort select="string[preceding-sibling::key[1]='Album']"/>
          <tr>
            <td><xsl:call-template name="albumName"/></td>
            <td><xsl:call-template name="artistName"/></td>
          </tr>
        </xsl:for-each>

      </table>
    </body>
  </html>
</xsl:template>

<xsl:template name="albumName">
  <xsl:value-of select="string[preceding-sibling::key[1]='Album']"/>
</xsl:template>

<xsl:template name="artistName">
  <xsl:choose>
    <xsl:when test="true[preceding-sibling::key[1]='Compilation']">
      <i>Compilation</i>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="string[preceding-sibling::key[1]='Artist']"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Aku sudah dipecah template utama ke template terpisah untuk album dan artis - XSLT bisa sulit untuk mengikuti di saat terbaik, jadi saya ingin modularise template sebanyak yang saya bisa.

Daftar album dikelompokkan berdasarkan genre

Masalahnya dengan daftar ini adalah cara musik adalah semua tumbuk bersama-sama. Aku tidak benar-benar seperti memiliki St Matius Gairah menggosok bahu dengan Berhenti Membuat Rasa .

Untuk kelompok album dengan genre, saya menggunakan teknik yang dirancang oleh Steve Muench, Oracle 'XML Teknis Penginjil', dijuluki 'pengelompokan Muenchian'.

Sekali lagi, orang lain telah menjelaskan hal ini lebih baik daripada aku bisa, jadi cukup untuk mengatakan (jika Anda ingin mencoba untuk mengikuti operasi XSL) yang menggunakan fungsi menghasilkan-id () pada tombol XSL didefinisikan pada lagu oleh Genre dan album, dan pada titik di mana template yang albumsInGenre disebut, konteks saat ini adalah yang pertama lagu dalam setiap genre.

Dalam albumsInGenre Template, teknik yang sama digunakan untuk mendapatkan lagu pertama dalam setiap Album, untuk mendapatkan nama album dan artis. Saya juga menggunakan predikat tambahan untuk lebih menyaring simpul-set ini untuk genre lulus sebagai parameter untuk template.

(Hidup akan lebih mudah dengan XSLT 2.0 yang akan memiliki untuk-setiap-kelompok elemen).

stylesheet terlihat seperti ini:

<xsl:key name="songsByAlbum" match="dict"
  use="string[preceding-sibling::key[1]='Album']"/>
<xsl:key name="songsByGenre" match="dict"
  use="string[preceding-sibling::key[1]='Genre']"/>


<xsl:template match="/plist/dict/dict">
  <html>
    <body>
      <table>

        <xsl:for-each select="dict[generate-id(.)=
            generate-id(key('songsByGenre',string)[1])]">
          <xsl:sort select="string[preceding-sibling::key[1]='Genre']"/>
          <xsl:for-each select="key('songsByGenre',string)[1]">
            <xsl:call-template name="albumsInGenre">
              <xsl:with-param name="genre"
                  select="string[preceding-sibling::key[1]='Genre']"/>
            </xsl:call-template>
          </xsl:for-each>
        </xsl:for-each>

      </table>
    </body>
  </html>
</xsl:template>


<xsl:template name="albumsInGenre">
  <xsl:param name="genre"/>

  <tr><td colspan='3'><b><xsl:value-of select="$genre"/></b></td></tr>

  <xsl:variable name="song" select="/plist/dict/dict/dict"/>
  <xsl:for-each select="$song[generate-id(.)=
      generate-id(key('songsByAlbum',string[preceding-sibling::key[1]='Album'])[1])]">
    <xsl:sort select="string[preceding-sibling::key[1]='Album']"/>
    <xsl:for-each select="key('songsByAlbum',string[preceding-sibling::key[1]='Album'])
        [string[preceding-sibling::key[1]='Genre']=$genre][1]">
      <tr>
        <td> </td>
        <td><xsl:call-template name="albumName"/></td>
        <td><xsl:call-template name="artistName"/></td>
      </tr>
    </xsl:for-each>
  </xsl:for-each>
</xsl:template>

Dan daftar album yang dihasilkan terlihat seperti:

Electronica / Dance
  Jalan raya St. Germain
  Dehli9 tosca
  Di darat Brian Eno
Jazz
  Madar Jan Garbarek
  The Fives Hot & Sevens  Louis Armstrong & The Hot Five

Jika Anda ingin hanya album pada iPod, bukan semua album di iTunes library, maka kondisi ekstra harus ditambahkan ke dalam-paling untuk-masing, sehingga menjadi:

<xsl:for-each select="key('songsByAlbum',string[preceding-sibling::key[1]='Album'])
        [string[preceding-sibling::key[1]='Genre']=$genre]
        [not(true[preceding-sibling::key[1]='Disabled'])][1]">

Menambahkan kali track

Setelah mendapat sejauh ini, saya ingin melihat durasi setiap album. Durasi setiap lagu yang disimpan (dalam milidetik) terhadap Total Waktu kunci, sehingga template untuk melakukan ini adalah:

<xsl:template name="iTunesTimeAlbum">
  <xsl:variable name="tracksInAlbum"
      select="key('songsByAlbum',string[preceding-sibling::key[1]='Album'])"/>
  <xsl:variable name="t"
      select="sum($tracksInAlbum/integer[preceding-sibling::key[1]='Total Time'])"/>
  <xsl:call-template name="formatTime">
    <xsl:with-param name="t" select="$t"/>
  </xsl:call-template>
</xsl:template>

<xsl:template name="formatTime">
  <xsl:param name="t"/>
  <xsl:if test="$t != 0">
    <xsl:variable name="h" select="floor(($t div (1000*60*60)))"/>
    <xsl:variable name="m" select="floor(($t div (1000*60)) mod 60)"/>
    <xsl:variable name="s" select="floor(($t div 1000) mod 60)"/>
    <xsl:if test="$h != 0"><xsl:value-of select="$h"/>h</xsl:if>
    <xsl:value-of select="format-number($m,'00')"/>m
    <xsl:value-of select="format-number($s,'00')"/>s
  </xsl:if>
</xsl:template>

Sekali lagi, jika Anda ingin melihat durasi pada iPod bukan di iTunes library (ketika hanya beberapa lagu di album ini dipilih karena transfer ke iPod), kondisi ekstra harus ditambahkan untuk pilih untuk variabel t:

<xsl:variable name="t"
      select="sum($tracksInAlbum/integer[preceding-sibling::key[1]='Total Time']
      [not(../true[preceding-sibling::key[1]='Disabled'])])"/>      

Sebuah template yang sama akan memberikan total waktu musik di iTunes library (atau pada iPod):

<xsl:template name="iTunesTimeTotal">
  <xsl:variable name="t"
      select="sum(dict/integer[preceding-sibling::key[1]='Total Time'])"/>
  <xsl:call-template name="formatTime">
    <xsl:with-param name="t" select="$t"/>
  </xsl:call-template>
</xsl:template>         

Dan jika Anda ingin memiliki ukuran total disk lagu, tersedia terhadap Ukuran kunci:

<xsl:template name="iTunesSizeTotal">
  <xsl:variable name="s" select="sum(dict/integer[preceding-sibling::key[1]='Size'])"/>
  <xsl:value-of select="floor($s div (1000*1000)) div 1000"/>GB
</xsl:template>

Membuka daftar langsung di browser

Daripada menggunakan alat XML / XSLT khusus, saya membuka daftar langsung di browser. Untuk melakukan hal ini, saya membuat 'wrapper' file XML yang menentukan yang stylesheet untuk menerapkan apa file XML, menggunakan tombol Instruksi pengolahan dan elemen:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="albumList.xsl" type="text/xsl"?>
<wrapper>
  <incl file="iTunes Music Library.xml"/>
</wrapper>

Saya kemudian menempatkan template di bagian atas stylesheet XSL yang cocok dengan elemen saya menempatkan sekitar file XML disertakan, dan menerapkan template stylesheet ke file XML disertakan.

<xsl:template match="/wrapper">
    <xsl:apply-templates select="document(incl/@file)"/>
  </xsl:template>   

Ini harus bekerja di browser saat ini; Aku sudah memeriksa di Internet Explorer (6.0+) dan Firefox.

Catatan: pendekatan alternatif akan pra-proses p-daftar XML menjadi struktur yang lebih konvensional, seperti yang dijelaskan di www.xmldatabases.org/WK/blog/1086?t=item , yang kemudian akan lebih mudah untuk memanipulasi dengan XSLT . Saya siap untuk hidup dengan sebelumnya-saudara membangun agar dapat menghasilkan daftar dalam satu langkah.


Semua ini disatukan dalam stylesheet bawah. Itu membuat untuk lebih stylesheet besar, tetapi dengan informasi di atas, Anda harus dapat mengambilnya terpisah dan menggunakan bagian-bagian yang Anda inginkan.

... Jadi sekarang saya memiliki daftar referensi cepat dari album di iPod saya.


Untuk menggunakan script ini, dalam bentuk dasar atau penuh, simpan albumList.xml dan albumList.xsl ditunjukkan di bawah untuk Anda My Music / iTunes folder, dan albumList.xml terbuka di browser Anda (IE6 atau Firefox). Jika Anda ingin menyesuaikan tata letak, dengan pedoman di atas Anda harus dapat mencampur dan mencocokkan komponen yang diperlukan. Anda dipersilakan untuk kembali menggunakan script ini [tanpa jaminan apapun tersurat maupun tersirat] untuk penggunaan pribadi. Jika Anda memiliki pertanyaan atau menemukan masalah, silakan hubungi saya .

 


Berkas albumList.xml (menspesifikasikan menerapkan albumList.xsl ke iTunes Music library.xml ):

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="albumList.xsl" type="text/xsl"?>
<wrapper>
  <incl file="iTunes Music Library.xml"/>
</wrapper>

Berkas albumList.xsl dasar versi (daftar nama & artis dari album di iPod):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <!-- (c) Chris Veness 2005 -->
  <xsl:output method="html" encoding="UTF-8" indent="yes"/>

  <!-- match the wrapper and apply templates to the <incl> xml file -->
  <xsl:template match="/wrapper">
    <xsl:apply-templates select="document(incl/@file)/plist/dict/dict"/>
  </xsl:template>


  <xsl:key name="songsByGenre" match="dict" use="string[preceding-sibling::key[1]='Genre']"/>
  <xsl:key name="songsByAlbum" match="dict" use="string[preceding-sibling::key[1]='Album']"/>


  <xsl:template match="dict">
    <html>
      <head>
        <title>iPod Album Listing</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
      </head>
      <body>
        <table>

          <xsl:for-each select="dict[generate-id(.)=generate-id(key('songsByGenre',string)[1])]">
            <xsl:sort select="string[preceding-sibling::key[1]='Genre']"/>
            <xsl:for-each select="key('songsByGenre',string)[1]">
              <xsl:call-template name="albumsInGenre">
                <xsl:with-param name="genre" select="string[preceding-sibling::key[1]='Genre']"/>
              </xsl:call-template>
            </xsl:for-each>
          </xsl:for-each>

        </table>
      </body>
    </html>
  </xsl:template>


  <xsl:template name="albumsInGenre">
    <xsl:param name="genre"/>

    <!-- genre header -->
    <tr><td colspan='3'><b><xsl:value-of select="$genre"/></b></td></tr>

    <!-- output each album in genre -->
    <xsl:variable name="song" select="/plist/dict/dict/dict"/>
    <xsl:for-each select="$song[generate-id(.)=
        generate-id(key('songsByAlbum',string[preceding-sibling::key[1]='Album'])[1])]">
      <xsl:sort select="string[preceding-sibling::key[1]='Album']"/>
      <xsl:for-each select="key('songsByAlbum',string[preceding-sibling::key[1]='Album'])
          [string[preceding-sibling::key[1]='Genre']=$genre]
          [not(true[preceding-sibling::key[1]='Disabled'])][1]">
        <tr valign='top'>
          <td width='20'> </td>
          <!-- the album name: -->
          <td><xsl:value-of select="string[preceding-sibling::key[1]='Album']"/></td>
          <!-- the artist: -->
          <td>
            <xsl:choose>
              <xsl:when test="true[preceding-sibling::key[1]='Compilation']">
                <i>Compilation</i>
              </xsl:when>
              <xsl:otherwise>
                <xsl:value-of select="string[preceding-sibling::key[1]='Artist']"/>
              </xsl:otherwise>
            </xsl:choose>
          </td>
        </tr>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

File albumList.xsl full version (includes complete iTunes library with times & totals):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <!-- (c) Chris Veness 2005-2006 -->
  <xsl:output method="html" encoding="UTF-8" indent="yes"/>


  <!-- match the wrapper and apply templates to the <incl> xml file -->
  <xsl:template match="/wrapper">
    <xsl:apply-templates select="document(incl/@file)/plist/dict/dict"/>
  </xsl:template>


  <xsl:key name="songsByGenre" match="dict" use="string[preceding-sibling::key[1]='Genre']"/>
  <xsl:key name="songsByAlbum" match="dict" use="string[preceding-sibling::key[1]='Album']"/>


  <xsl:template match="dict">
    <html>
      <head>
        <title>iTunes Album Listing</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <style type='text/css'> td { vertical-align: top; padding-right: 1em; } </style>
      </head>
      <body>
        <table>

          <xsl:for-each select="dict[generate-id(.)=generate-id(key('songsByGenre',string)[1])]">
            <xsl:sort select="string[preceding-sibling::key[1]='Genre']"/>
            <xsl:for-each select="key('songsByGenre',string)[1]">
              <xsl:call-template name="albumsInGenre">
                <xsl:with-param name="genre" select="string[preceding-sibling::key[1]='Genre']"/>
              </xsl:call-template>
            </xsl:for-each>
          </xsl:for-each>

          <!-- totals -->
          <tr>
            <td colspan='4' style='color: gray'><b>Total</b></td>
            <td style='color: gray' align='right'><xsl:call-template name="iPodTimeTotal"/></td>
            <td style='color: gray' align='right'><xsl:call-template name="iTunesTimeTotal"/></td>
          </tr>
          <tr>
            <td colspan='4'> </td>
            <td style='color: gray' align='right'><xsl:call-template name="iPodSizeTotal"/></td>
            <td style='color: gray' align='right'><xsl:call-template name="iTunesSizeTotal"/></td>
          </tr>

        </table>
      </body>
    </html>
  </xsl:template>


  <xsl:template name="albumsInGenre">
    <xsl:param name="genre"/>

    <tr>  <!-- genre header -->
      <td colspan='4'><b><xsl:value-of select="$genre"/></b></td>
      <td align='right' style='color: gray'><i>iPod</i></td>
      <td align='right' style='color: gray'><i>iTunes</i></td>
    </tr>

    <xsl:variable name="song" select="/plist/dict/dict/dict"/>
    <xsl:for-each select="$song[generate-id(.)=
        generate-id(key('songsByAlbum',string[preceding-sibling::key[1]='Album'])[1])]">
      <xsl:sort select="string[preceding-sibling::key[1]='Album']"/>
      <xsl:for-each select="key('songsByAlbum',string[preceding-sibling::key[1]='Album'])
          [string[preceding-sibling::key[1]='Genre']=$genre]
          [1]">
          <!--  for albums on iPod only, add
                [not(true[preceding-sibling::key[1]='Disabled'])] -->
        <tr>
          <td> </td>
          <td><xsl:call-template name="album"/></td>
          <td><xsl:call-template name="artist"/></td>
          <td align='right'><xsl:call-template name="iPodTimeAlbum"/></td>
          <td align='right'><xsl:call-template name="iTunesTimeAlbum"/></td>
        </tr>
      </xsl:for-each>
    </xsl:for-each>
    <tr><td colspan='6'>&#160;</td></tr>  <!-- space between genres -->
  </xsl:template>


  <xsl:template name="album">
    <xsl:value-of select="string[preceding-sibling::key[1]='Album']"/>
  </xsl:template>


  <xsl:template name="artist">
    <xsl:choose>
      <xsl:when test="true[preceding-sibling::key[1]='Compilation']">
        <i>Compilation</i>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="string[preceding-sibling::key[1]='Artist']"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>


  <xsl:template name="iPodTimeAlbum">
    <xsl:variable name="tracksInAlbum"
        select="key('songsByAlbum',string[preceding-sibling::key[1]='Album'])"/>
    <xsl:variable name="t"
        select="sum($tracksInAlbum/integer[preceding-sibling::key[1]='Total Time']
            [not(../true[preceding-sibling::key[1]='Disabled'])])"/>
    <xsl:call-template name="formatTime">
      <xsl:with-param name="t" select="$t"/>
    </xsl:call-template>
  </xsl:template>


  <xsl:template name="iTunesTimeAlbum">
    <xsl:variable name="tracksInAlbum"
        select="key('songsByAlbum',string[preceding-sibling::key[1]='Album'])"/>
    <xsl:variable name="t"
        select="sum($tracksInAlbum/integer[preceding-sibling::key[1]='Total Time'])"/>
    <xsl:call-template name="formatTime">
      <xsl:with-param name="t" select="$t"/>
    </xsl:call-template>
  </xsl:template>


  <xsl:template name="iPodTimeTotal">
    <xsl:variable name="t" select="sum(dict/integer[preceding-sibling::key[1]='Total Time']
        [not(../true[preceding-sibling::key[1]='Disabled'])])"/>
    <xsl:call-template name="formatTime">
      <xsl:with-param name="t" select="$t"/>
    </xsl:call-template>
  </xsl:template>


  <xsl:template name="iTunesTimeTotal">
    <xsl:variable name="t" select="sum(dict/integer[preceding-sibling::key[1]='Total Time'])"/>
    <xsl:call-template name="formatTime">
      <xsl:with-param name="t" select="$t"/>
    </xsl:call-template>
  </xsl:template>


  <xsl:template name="iPodSizeTotal">
    <xsl:variable name="s" select="sum(dict/integer[preceding-sibling::key[1]='Size']
        [not(../true[preceding-sibling::key[1]='Disabled'])])"/>
    <xsl:value-of select="floor($s div (1000000)) div 1000"/>GB
  </xsl:template>


  <xsl:template name="iTunesSizeTotal">
    <xsl:variable name="s" select="sum(dict/integer[preceding-sibling::key[1]='Size'])"/>
    <xsl:value-of select="floor($s div (1000000)) div 1000"/>GB
  </xsl:template>


  <xsl:template name="formatTime">
    <xsl:param name="t"/>
    <xsl:if test="$t != 0">
      <xsl:variable name="h" select="floor(($t div (1000*60*60)))"/>
      <xsl:variable name="m" select="floor(($t div (1000*60)) mod 60)"/>
      <xsl:variable name="s" select="floor(($t div 1000) mod 60)"/>
      <xsl:if test="$h != 0"><xsl:value-of select="$h"/>:</xsl:if>
      <xsl:value-of select="format-number($m,'00')"/>:<xsl:value-of select="format-number($s,'00')"/>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>