id3 outputs id3v2-info objects or f now. id3v1 info is turned into id3v2. you can access the title, album, etc fields by calling id3-title, etc, but also other frames are saved and can be accessed

db4
Doug Coleman 2009-02-26 21:39:53 -06:00
parent 1bd35e6f62
commit e1b4e8c66f
4 changed files with 246 additions and 222 deletions

View File

@ -1,2 +1,2 @@
Tim Wawrzynczak Tim Wawrzynczak
Doug Coleman

View File

@ -6,7 +6,7 @@ IN: id3
HELP: file-id3-tags HELP: file-id3-tags
{ $values { $values
{ "path" "a path string" } { "path" "a path string" }
{ "object/f" "a tuple storing ID3 metadata or f" } } { "id3v2-info/f" "a tuple storing ID3v2 metadata or f" } }
{ $description "Return a tuple containing the ID3 information parsed out of the MP3 file, or " { $link f } " if no metadata is present. Currently, the parser supports the following tags: " { $description "Return a tuple containing the ID3 information parsed out of the MP3 file, or " { $link f } " if no metadata is present. Currently, the parser supports the following tags: "
$nl { $link title>> } $nl { $link title>> }
$nl { $link artist>> } $nl { $link artist>> }

View File

@ -1,35 +1,42 @@
! Copyright (C) 2009 Tim Wawrzynczak ! Copyright (C) 2009 Tim Wawrzynczak
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: tools.test id3 id3.private ; USING: tools.test id3 combinators ;
IN: id3.tests IN: id3.tests
[ : id3-params ( id3 -- title artist album year comment genre )
T{ id3-info {
{ title "BLAH" } [ id3-title ]
{ artist "ARTIST" } [ id3-artist ]
{ album "ALBUM" } [ id3-album ]
{ year "2009" } [ id3-year ]
{ comment "COMMENT" } [ id3-comment ]
{ genre "Bluegrass" } [ id3-genre ]
} } cleave ;
] [ "resource:extra/id3/tests/blah.mp3" file-id3-tags ] unit-test
[ [
T{ id3-info "BLAH"
{ title "Anthem of the Trinity" } "ARTIST"
{ artist "Terry Riley" } "ALBUM"
{ album "Shri Camel" } "2009"
{ genre "Classical" } "COMMENT"
} "Bluegrass"
] [ "resource:extra/id3/tests/blah2.mp3" file-id3-tags ] unit-test ] [ "resource:extra/id3/tests/blah.mp3" file-id3-tags id3-params ] unit-test
[ [
T{ id3-info "Anthem of the Trinity"
{ title "Stormy Weather" } "Terry Riley"
{ artist "Frank Sinatra" } "Shri Camel"
{ album "Night and Day Frank Sinatra" } f
{ comment "eng, AG# 08E1C12E" } f
{ genre "Big Band" } "Classical"
} ] [ "resource:extra/id3/tests/blah2.mp3" file-id3-tags id3-params ] unit-test
] [ "resource:extra/id3/tests/blah3.mp3" file-id3-tags ] unit-test
[
"Stormy Weather"
"Frank Sinatra"
"Night and Day Frank Sinatra"
f
"eng, AG# 08E1C12E"
"Big Band"
] [ "resource:extra/id3/tests/blah3.mp3" file-id3-tags id3-params ] unit-test

View File

@ -1,145 +1,144 @@
! Copyright (C) 2009 Tim Wawrzynczak ! Copyright (C) 2009 Tim Wawrzynczak, Doug Coleman.
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: sequences io io.encodings.binary io.files io.pathnames USING: sequences io io.encodings.binary io.files io.pathnames
strings kernel math io.mmap io.mmap.uchar accessors syntax strings kernel math io.mmap io.mmap.uchar accessors syntax
combinators math.ranges unicode.categories byte-arrays combinators math.ranges unicode.categories byte-arrays
io.encodings.string io.encodings.utf8 assocs math.parser io.encodings.string io.encodings.utf8 assocs math.parser
combinators.short-circuit fry ; combinators.short-circuit fry namespaces multiline
combinators.smart splitting ;
IN: id3 IN: id3
<PRIVATE <PRIVATE
CONSTANT: genres CONSTANT: genres
H{ {
{ 0 "Blues" } "Blues"
{ 1 "Classic Rock" } "Classic Rock"
{ 2 "Country" } "Country"
{ 3 "Dance" } "Dance"
{ 4 "Disco" } "Disco"
{ 5 "Funk" } "Funk"
{ 6 "Grunge" } "Grunge"
{ 7 "Hip-Hop" } "Hip-Hop"
{ 8 "Jazz" } "Jazz"
{ 9 "Metal" } "Metal"
{ 10 "New Age" } "New Age"
{ 11 "Oldies" } "Oldies"
{ 12 "Other" } "Other"
{ 13 "Pop" } "Pop"
{ 14 "R&B" } "R&B"
{ 15 "Rap" } "Rap"
{ 16 "Reggae" } "Reggae"
{ 17 "Rock" } "Rock"
{ 18 "Techno" } "Techno"
{ 19 "Industrial" } "Industrial"
{ 20 "Alternative" } "Alternative"
{ 21 "Ska" } "Ska"
{ 22 "Death Metal" } "Death Metal"
{ 23 "Pranks" } "Pranks"
{ 24 "Soundtrack" } "Soundtrack"
{ 25 "Euro-Techno" } "Euro-Techno"
{ 26 "Ambient" } "Ambient"
{ 27 "Trip-Hop" } "Trip-Hop"
{ 28 "Vocal" } "Vocal"
{ 29 "Jazz+Funk" } "Jazz+Funk"
{ 30 "Fusion" } "Fusion"
{ 31 "Trance" } "Trance"
{ 32 "Classical" } "Classical"
{ 33 "Instrumental" } "Instrumental"
{ 34 "Acid" } "Acid"
{ 35 "House" } "House"
{ 36 "Game" } "Game"
{ 37 "Sound Clip" } "Sound Clip"
{ 38 "Gospel" } "Gospel"
{ 39 "Noise" } "Noise"
{ 40 "AlternRock" } "AlternRock"
{ 41 "Bass" } "Bass"
{ 42 "Soul" } "Soul"
{ 43 "Punk" } "Punk"
{ 44 "Space" } "Space"
{ 45 "Meditative" } "Meditative"
{ 46 "Instrumental Pop" } "Instrumental Pop"
{ 47 "Instrumental Rock" } "Instrumental Rock"
{ 48 "Ethnic" } "Ethnic"
{ 49 "Gothic" } "Gothic"
{ 50 "Darkwave" } "Darkwave"
{ 51 "Techno-Industrial" } "Techno-Industrial"
{ 52 "Electronic" } "Electronic"
{ 53 "Pop-Folk" } "Pop-Folk"
{ 54 "Eurodance" } "Eurodance"
{ 55 "Dream" } "Dream"
{ 56 "Southern Rock" } "Southern Rock"
{ 57 "Comedy" } "Comedy"
{ 58 "Cult" } "Cult"
{ 59 "Gangsta" } "Gangsta"
{ 60 "Top 40" } "Top 40"
{ 61 "Christian Rap" } "Christian Rap"
{ 62 "Pop/Funk" } "Pop/Funk"
{ 63 "Jungle" } "Jungle"
{ 64 "Native American" } "Native American"
{ 65 "Cabaret" } "Cabaret"
{ 66 "New Wave" } "New Wave"
{ 67 "Psychedelic" } "Psychedelic"
{ 68 "Rave" } "Rave"
{ 69 "Showtunes" } "Showtunes"
{ 70 "Trailer" } "Trailer"
{ 71 "Lo-Fi" } "Lo-Fi"
{ 72 "Tribal" } "Tribal"
{ 73 "Acid Punk" } "Acid Punk"
{ 74 "Acid Jazz" } "Acid Jazz"
{ 75 "Polka" } "Polka"
{ 76 "Retro" } "Retro"
{ 77 "Musical" } "Musical"
{ 78 "Rock & Roll" } "Rock & Roll"
{ 79 "Hard Rock" } "Hard Rock"
{ 80 "Folk" } "Folk"
{ 81 "Folk-Rock" } "Folk-Rock"
{ 82 "National Folk" } "National Folk"
{ 83 "Swing" } "Swing"
{ 84 "Fast Fusion" } "Fast Fusion"
{ 85 "Bebop" } "Bebop"
{ 86 "Latin" } "Latin"
{ 87 "Revival" } "Revival"
{ 88 "Celtic" } "Celtic"
{ 89 "Bluegrass" } "Bluegrass"
{ 90 "Avantgarde" } "Avantgarde"
{ 91 "Gothic Rock" } "Gothic Rock"
{ 92 "Progressive Rock" } "Progressive Rock"
{ 93 "Psychedelic Rock" } "Psychedelic Rock"
{ 94 "Symphonic Rock" } "Symphonic Rock"
{ 95 "Slow Rock" } "Slow Rock"
{ 96 "Big Band" } "Big Band"
{ 97 "Chorus" } "Chorus"
{ 98 "Easy Listening" } "Easy Listening"
{ 99 "Acoustic" } "Acoustic"
{ 100 "Humour" } "Humour"
{ 101 "Speech" } "Speech"
{ 102 "Chanson" } "Chanson"
{ 103 "Opera" } "Opera"
{ 104 "Chamber Music" } "Chamber Music"
{ 105 "Sonata" } "Sonata"
{ 106 "Symphony" } "Symphony"
{ 107 "Booty Bass" } "Booty Bass"
{ 108 "Primus" } "Primus"
{ 109 "Porn Groove" } "Porn Groove"
{ 110 "Satire" } "Satire"
{ 111 "Slow Jam" } "Slow Jam"
{ 112 "Club" } "Club"
{ 113 "Tango" } "Tango"
{ 114 "Samba" } "Samba"
{ 115 "Folklore" } "Folklore"
{ 116 "Ballad" } "Ballad"
{ 117 "Power Ballad" } "Power Ballad"
{ 118 "Rhythmic Soul" } "Rhythmic Soul"
{ 119 "Freestyle" } "Freestyle"
{ 120 "Duet" } "Duet"
{ 121 "Punk Rock" } "Punk Rock"
{ 122 "Drum Solo" } "Drum Solo"
{ 123 "A capella" } "A capella"
{ 124 "Euro-House" } "Euro-House"
{ 125 "Dance Hall" } "Dance Hall"
} ! end genre hashtable }
! tuples
TUPLE: header version flags size ; TUPLE: header version flags size ;
@ -151,42 +150,58 @@ TUPLE: id3-info title artist album year comment genre ;
: <id3-info> ( -- object ) id3-info new ; : <id3-info> ( -- object ) id3-info new ;
: <id3v2-info> ( header frames -- object ) id3v2-info boa ; : <id3v2-info> ( header frames -- object )
[ [ frame-id>> ] keep ] H{ } map>assoc
id3v2-info boa ;
: <header> ( -- object ) header new ; : <header> ( -- object ) header new ;
: <frame> ( -- object ) frame new ; : <frame> ( -- object ) frame new ;
! utility words : id3v2? ( mmap -- ? ) "ID3" head? ; inline
: id3v2? ( mmap -- ? )
"ID3" head? ;
: id3v1? ( mmap -- ? ) : id3v1? ( mmap -- ? )
{ [ length 128 >= ] [ 128 tail-slice* "TAG" head? ] } 1&& ; { [ length 128 >= ] [ 128 tail-slice* "TAG" head? ] } 1&& ; inline
: id3v1-frame ( string key -- frame )
<frame>
swap >>frame-id
swap >>data ;
: id3v1>id3v2 ( id3v1 -- id3v2 )
[
{
[ title>> "TIT2" id3v1-frame ]
[ artist>> "TPE1" id3v1-frame ]
[ album>> "TALB" id3v1-frame ]
[ year>> "TYER" id3v1-frame ]
[ comment>> "COMM" id3v1-frame ]
[ genre>> "TCON" id3v1-frame ]
} cleave
] output>array f swap <id3v2-info> ;
: >28bitword ( seq -- int ) : >28bitword ( seq -- int )
0 [ swap 7 shift bitor ] reduce ; 0 [ [ 7 shift ] dip bitor ] reduce ; inline
: filter-text-data ( data -- filtered ) : filter-text-data ( data -- filtered )
[ printable? ] filter ; [ printable? ] filter ; inline
! frame details stuff ! frame details stuff
: valid-frame-id? ( id -- ? ) : valid-frame-id? ( id -- ? )
[ [ digit? ] [ LETTER? ] bi or ] all? ; [ { [ digit? ] [ LETTER? ] } 1|| ] all? ; inline
: read-frame-id ( mmap -- id ) : read-frame-id ( mmap -- id )
4 head-slice ; 4 head-slice ; inline
: read-frame-size ( mmap -- size ) : read-frame-size ( mmap -- size )
[ 4 8 ] dip subseq ; [ 4 8 ] dip subseq ; inline
: read-frame-flags ( mmap -- flags ) : read-frame-flags ( mmap -- flags )
[ 8 10 ] dip subseq ; [ 8 10 ] dip subseq ; inline
: read-frame-data ( frame mmap -- frame data ) : read-frame-data ( frame mmap -- frame data )
[ 10 over size>> 10 + ] dip <slice> filter-text-data ; [ 10 over size>> 10 + ] dip <slice> filter-text-data ; inline
! read whole frames ! read whole frames
@ -200,10 +215,11 @@ TUPLE: id3-info title artist album year comment genre ;
} cleave ; } cleave ;
: read-frame ( mmap -- frame/f ) : read-frame ( mmap -- frame/f )
dup read-frame-id valid-frame-id? [ (read-frame) ] [ drop f ] if ; dup read-frame-id valid-frame-id?
[ (read-frame) ] [ drop f ] if ;
: remove-frame ( mmap frame -- mmap ) : remove-frame ( mmap frame -- mmap )
size>> 10 + tail-slice ; size>> 10 + tail-slice ; inline
: read-frames ( mmap -- frames ) : read-frames ( mmap -- frames )
[ dup read-frame dup ] [ dup read-frame dup ]
@ -213,13 +229,12 @@ TUPLE: id3-info title artist album year comment genre ;
! header stuff ! header stuff
: read-header-supported-version? ( mmap -- ? ) : read-header-supported-version? ( mmap -- ? )
3 tail-slice [ { 4 } head? ] [ { 3 } head? ] bi or ; 3 tail-slice first { 3 4 } member? ; inline
: read-header-flags ( mmap -- flags ) : read-header-flags ( mmap -- flags ) 5 swap nth ; inline
5 swap nth ;
: read-header-size ( mmap -- size ) : read-header-size ( mmap -- size )
[ 6 10 ] dip <slice> >28bitword ; [ 6 10 ] dip <slice> >28bitword ; inline
: read-v2-header ( mmap -- id3header ) : read-v2-header ( mmap -- id3header )
[ <header> ] dip [ <header> ] dip
@ -227,51 +242,30 @@ TUPLE: id3-info title artist album year comment genre ;
[ read-header-supported-version? >>version ] [ read-header-supported-version? >>version ]
[ read-header-flags >>flags ] [ read-header-flags >>flags ]
[ read-header-size >>size ] [ read-header-size >>size ]
} cleave ; } cleave ; inline
: drop-header ( mmap -- seq1 seq2 ) : drop-header ( mmap -- seq1 seq2 )
dup 10 tail-slice swap ; [ 10 tail-slice ] [ ] bi ; inline
: frame-tag ( frame string -- tag/f ) : read-v2-tag-data ( seq -- id3v2-info )
'[ frame-id>> _ = ] find nip ; inline drop-header read-v2-header
swap read-frames <id3v2-info> ; inline
: parse-frames ( id3v2-info -- id3-info )
[ <id3-info> ] dip frames>>
{
[ "TIT2" frame-tag [ data>> >>title ] when* ]
[ "TALB" frame-tag [ data>> >>album ] when* ]
[ "TPE1" frame-tag [ data>> >>artist ] when* ]
[ "TCON" frame-tag [ data>> [ [ digit? ] filter string>number ] keep swap [ genres at nip ] when*
>>genre ] when* ]
[ "COMM" frame-tag [ data>> >>comment ] when* ]
[ "TYER" frame-tag [ data>> >>year ] when* ]
} cleave ;
: read-v2-tag-data ( seq -- id3-info )
drop-header read-v2-header swap read-frames <id3v2-info> parse-frames ;
! v1 information ! v1 information
: skip-to-v1-data ( seq -- seq ) : skip-to-v1-data ( seq -- seq ) 125 tail-slice* ; inline
125 tail-slice* ;
: read-title ( seq -- title ) : read-title ( seq -- title ) 30 head-slice ; inline
30 head-slice ;
: read-artist ( seq -- title ) : read-artist ( seq -- title ) [ 30 60 ] dip subseq ; inline
[ 30 60 ] dip subseq ;
: read-album ( seq -- album ) : read-album ( seq -- album ) [ 60 90 ] dip subseq ; inline
[ 60 90 ] dip subseq ;
: read-year ( seq -- year ) : read-year ( seq -- year ) [ 90 94 ] dip subseq ; inline
[ 90 94 ] dip subseq ;
: read-comment ( seq -- comment ) : read-comment ( seq -- comment ) [ 94 124 ] dip subseq ; inline
[ 94 124 ] dip subseq ;
: read-genre ( seq -- genre ) : read-genre ( seq -- genre ) [ 124 ] dip nth ; inline
[ 124 ] dip nth ;
: (read-v1-tag-data) ( seq -- mp3-file ) : (read-v1-tag-data) ( seq -- mp3-file )
[ <id3-info> ] dip [ <id3-info> ] dip
@ -281,23 +275,46 @@ TUPLE: id3-info title artist album year comment genre ;
[ read-album utf8 decode filter-text-data >>album ] [ read-album utf8 decode filter-text-data >>album ]
[ read-year utf8 decode filter-text-data >>year ] [ read-year utf8 decode filter-text-data >>year ]
[ read-comment utf8 decode filter-text-data >>comment ] [ read-comment utf8 decode filter-text-data >>comment ]
[ read-genre >fixnum genres at >>genre ] [ read-genre number>string >>genre ]
} cleave ; } cleave ; inline
: read-v1-tag-data ( seq -- mp3-file ) : read-v1-tag-data ( seq -- mp3-file )
skip-to-v1-data (read-v1-tag-data) ; skip-to-v1-data (read-v1-tag-data) ; inline
: parse-genre ( string -- n/f )
dup "(" ?head-slice drop ")" ?tail-slice drop
string>number dup number? [
genres ?nth swap or
] [
drop
] if ; inline
PRIVATE> PRIVATE>
! public interface : frame-named ( id3 name quot -- obj )
[ swap frames>> at* ] dip
[ data>> ] prepose [ drop f ] if ; inline
: file-id3-tags ( path -- object/f ) : id3-title ( id3 -- title/f ) "TIT2" [ ] frame-named ; inline
: id3-artist ( id3 -- artist/f ) "TPE1" [ ] frame-named ; inline
: id3-album ( id3 -- album/f ) "TALB" [ ] frame-named ; inline
: id3-year ( id3 -- year/f ) "TYER" [ ] frame-named ; inline
: id3-comment ( id3 -- comment/f ) "COMM" [ ] frame-named ; inline
: id3-genre ( id3 -- genre/f )
"TCON" [ parse-genre ] frame-named ; inline
: id3-frame ( id3 key -- value/f ) [ ] frame-named ; inline
: file-id3-tags ( path -- id3v2-info/f )
[ [
{ {
{ [ dup id3v2? ] [ read-v2-tag-data ] } ! ( ? -- id3v2 ) { [ dup id3v2? ] [ read-v2-tag-data ] }
{ [ dup id3v1? ] [ read-v1-tag-data ] } ! ( ? -- id3-info ) { [ dup id3v1? ] [ read-v1-tag-data id3v1>id3v2 ] }
[ drop f ] ! ( mmap -- f ) [ drop f ]
} cond } cond
] with-mapped-uchar-file ; ] with-mapped-uchar-file ;
! end