11use crate :: error:: { Id3v2Error , Id3v2ErrorKind , Result } ;
22use crate :: id3:: v2:: frame:: { FrameFlags , FrameRef } ;
3- use crate :: id3:: v2:: tag:: GenresIter ;
43use crate :: id3:: v2:: util:: synchsafe:: SynchsafeInteger ;
5- use crate :: id3:: v2:: { Frame , FrameId , TextInformationFrame } ;
4+ use crate :: id3:: v2:: { Frame , FrameId , KeyValueFrame , TextInformationFrame } ;
65use crate :: tag:: items:: Timestamp ;
76
87use std:: io:: Write ;
98
9+ use crate :: id3:: v2:: tag:: GenresIter ;
1010use byteorder:: { BigEndian , WriteBytesExt } ;
1111
1212pub ( in crate :: id3:: v2) fn create_items < W > (
@@ -43,12 +43,15 @@ where
4343{
4444 // These are all frames from ID3v2.4
4545 const FRAMES_TO_DISCARD : & [ & str ] = & [
46- "ASPI" , "EQU2" , "RVA2" , "SEEK" , "SIGN" , "TDEN" , "TDRL" , "TDTG" , "TIPL " , "TMCL " , "TMOO " ,
47- "TPRO" , "TSOA" , " TSOP", "TSOT" , "TSST" ,
46+ "ASPI" , "EQU2" , "RVA2" , "SEEK" , "SIGN" , "TDEN" , "TDRL" , "TDTG" , "TMOO " , "TPRO " , "TSOA " ,
47+ "TSOP" , "TSOT" , "TSST" ,
4848 ] ;
4949
50+ const IPLS_ID : & str = "IPLS" ;
51+
5052 let is_id3v23 = true ;
5153
54+ let mut ipls = None ;
5255 for mut frame in frames {
5356 let id = frame. id_str ( ) ;
5457
99102 ) ) ) ;
100103
101104 if let ( Some ( month) , Some ( day) ) = ( timestamp. month , timestamp. day ) {
102- let date = format ! ( "{:02}{:02}" , month , day ) ;
105+ let date = format ! ( "{:02}{:02}" , day , month ) ;
103106 new_frames. push ( Frame :: Text ( TextInformationFrame :: new (
104107 FrameId :: Valid ( "TDAT" . into ( ) ) ,
105108 f. encoding . to_id3v23 ( ) ,
@@ -142,19 +145,63 @@ where
142145 } ;
143146
144147 let mut new_genre_string = String :: new ( ) ;
145- for genre in GenresIter :: new ( & f. value ) {
146- match genre {
148+ let genres = GenresIter :: new ( & f. value , true ) . collect :: < Vec < _ > > ( ) ;
149+ for ( i, genre) in genres. iter ( ) . enumerate ( ) {
150+ match * genre {
147151 "Remix" => new_genre_string. push_str ( "(RX)" ) ,
148152 "Cover" => new_genre_string. push_str ( "(CR)" ) ,
149- _ => {
153+ _ if i == genres . len ( ) - 1 && genre . parse :: < u8 > ( ) . is_err ( ) => {
150154 new_genre_string. push_str ( genre) ;
151155 } ,
156+ _ => {
157+ new_genre_string. push_str ( & format ! ( "({genre})" ) ) ;
158+ } ,
152159 }
153160 }
154161
155162 f. value = new_genre_string;
156163 frame. 0 = value;
157164 } ,
165+ // TIPL (Involved people list) and TMCL (Musician credits list) are
166+ // both key-value pairs. ID3v2.3 does not distinguish between the two,
167+ // so we must merge them into a single IPLS frame.
168+ "TIPL" | "TMCL" => {
169+ let mut value = frame. 0 . clone ( ) ;
170+ let Frame :: KeyValue ( KeyValueFrame {
171+ ref mut key_value_pairs,
172+ encoding,
173+ ..
174+ } ) = value. to_mut ( )
175+ else {
176+ log:: warn!( "Discarding frame: {}, not supported in ID3v2.3" , id) ;
177+ continue ;
178+ } ;
179+
180+ let ipls_frame;
181+ match ipls {
182+ Some ( ref mut frame) => {
183+ ipls_frame = frame;
184+ } ,
185+ None => {
186+ ipls = Some ( TextInformationFrame :: new (
187+ FrameId :: Valid ( "IPLS" . into ( ) ) ,
188+ encoding. to_id3v23 ( ) ,
189+ String :: new ( ) ,
190+ ) ) ;
191+ ipls_frame = ipls. as_mut ( ) . unwrap ( ) ;
192+ } ,
193+ }
194+
195+ for ( key, value) in key_value_pairs. drain ( ..) {
196+ if !ipls_frame. value . is_empty ( ) {
197+ ipls_frame. value . push ( '\0' ) ;
198+ }
199+
200+ ipls_frame. value . push_str ( & format ! ( "{}\0 {}" , key, value) ) ;
201+ }
202+
203+ continue ;
204+ } ,
158205 _ => { } ,
159206 }
160207
@@ -169,6 +216,12 @@ where
169216 ) ?;
170217 }
171218
219+ if let Some ( ipls) = ipls {
220+ let frame = Frame :: Text ( ipls) ;
221+ let value = frame. as_bytes ( is_id3v23) ?;
222+ write_frame ( writer, IPLS_ID , frame. flags ( ) , & value, is_id3v23) ?;
223+ }
224+
172225 Ok ( ( ) )
173226}
174227
@@ -252,10 +305,14 @@ where
252305 ) ;
253306 }
254307
255- if let Some ( len) = flags. data_length_indicator {
308+ if let Some ( mut len) = flags. data_length_indicator {
256309 if len > 0 {
257310 write_frame_header ( writer, name, ( value. len ( ) + 1 ) as u32 , flags, is_id3v23) ?;
258- writer. write_u32 :: < BigEndian > ( len. synch ( ) ?) ?;
311+ if !is_id3v23 {
312+ len = len. synch ( ) ?;
313+ }
314+
315+ writer. write_u32 :: < BigEndian > ( len) ?;
259316 writer. write_u8 ( method_symbol) ?;
260317 writer. write_all ( value) ?;
261318
@@ -282,11 +339,11 @@ where
282339 flags. as_id3v24_bytes ( )
283340 } ;
284341
342+ writer. write_all ( name. as_bytes ( ) ) ?;
285343 if !is_id3v23 {
286344 len = len. synch ( ) ?;
287345 }
288346
289- writer. write_all ( name. as_bytes ( ) ) ?;
290347 writer. write_u32 :: < BigEndian > ( len) ?;
291348 writer. write_u16 :: < BigEndian > ( flags) ?;
292349
0 commit comments