/** ~~~~~ * Created with the AppyBuilder Code Editor. https://editor.appybuilder.com/ * **** NOTE: DO NOT use a package name. * **** The package name will be created for you automatically. * **** Adding a package name will cause a compile error */ import android.content.Context; import android.util.Log; import com.google.appinventor.components.annotations.*; import com.google.appinventor.components.runtime.*; import com.google.appinventor.components.common.ComponentCategory; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.MediaMetadataRetriever; import com.google.appinventor.components.common.PropertyTypeConstants; import com.google.appinventor.components.runtime.util.MediaUtil; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.FileInputStream; import java.io.ByteArrayOutputStream; import java.io.BufferedOutputStream; import android.widget.Toast; import java.nio.ByteBuffer; @DesignerComponent(version = 5, description = "obtention et edition des tags d'un mp3" + " par jm latour ", category = ComponentCategory.EXTENSION, nonVisible = true, iconName = "https://www.dropbox.com/s/7jim1f21jjxtv4v/logo%20Part-cours.png?dl=0") @SimpleObject(external = true) public class Mp3Tags extends AndroidNonvisibleComponent { private ComponentContainer container; public static String fichier=""; /** * @param container container, component will be placed in */ public Mp3Tags(ComponentContainer container) { super(container.$form()); this.container = container; } MediaMetadataRetriever metaRetriever; //************* @SimpleFunction(description = "return file's artist") public String GetArtist(String filePath){ String song=defineDir(filePath); File file=new File(song); metaRetriever = new MediaMetadataRetriever(); if (file.exists()) { metaRetriever.setDataSource(song); try { song=metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST); if (song == ""|| song == null) { song = ""; } } catch (Exception e) { song=" Exception"; } }else { song="file not found"; } return song; } @SimpleFunction(description = "Edit Artist's Tag") public void EditArtist(String filePath,String NewTag) throws IOException { filePath=defineDir(filePath); if (new File(filePath).exists()) { replaceTag("TPE1",filePath,NewTag); } } //**************** @SimpleFunction(description = "return file's album") public String GetAlbum(String filePath){ String song=defineDir(filePath); File file=new File(song); metaRetriever = new MediaMetadataRetriever(); if (file.exists()) { metaRetriever.setDataSource(song); try { song=metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM); if (song == ""|| song == null) { song = ""; } } catch (Exception e) { song=" Exception"; } }else { song="file not found"; } return song; } @SimpleFunction(description = "Edit Album's Tag") public void EditAlbum(String filePath,String NewTag) throws IOException { filePath=defineDir(filePath); if (new File(filePath).exists()) { replaceTag("TALB",filePath,NewTag); } } //*********** @SimpleFunction(description = "return file's title") public String GetTitle(String filePath){ String song=defineDir(filePath); File file=new File(song); metaRetriever = new MediaMetadataRetriever(); if (file.exists()) { metaRetriever.setDataSource(song); try { song=metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE); if (song == ""|| song == null) { song = ""; } } catch (Exception e) { song=" Exception"; } }else { song="file not found"; } return song; } @SimpleFunction(description = "Edit Title's Tag") public void EditTitle(String filePath,String NewTitle) throws IOException { filePath=defineDir(filePath); if (new File(filePath).exists()) { replaceTag("TIT2",filePath,NewTitle); } } //********* @SimpleFunction(description = "return file's genre") public String GetGenre(String filePath){ String song=defineDir(filePath); File file=new File(song); metaRetriever = new MediaMetadataRetriever(); if (file.exists()) { metaRetriever.setDataSource(song); try { song=metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE); if (song == ""|| song == null) { song = ""; } } catch (Exception e) { song=" Exception"; } }else { song="file not found"; } return song; } @SimpleFunction(description = "Edit Genre's Tag") public void EditGenre(String filePath,String NewTag) throws IOException { filePath=defineDir(filePath); if (new File(filePath).exists()) { replaceTag("TCON",filePath,NewTag); } } //********** @SimpleFunction(description = "Edit Comment's Tag") public void EditComment(String filePath,String NewTag) throws IOException { filePath=defineDir(filePath); if (new File(filePath).exists()) { NewTag="fra "+NewTag; replaceTag("COMM", filePath, NewTag); } } @SimpleFunction(description = "Get Comment's Tag") public String GetComment(String filePath){ filePath=defineDir(filePath); if (new File(filePath).exists()) { String result=GetTextTag(filePath, "COMM"); if (result==""){ return ""; }else{ return result.substring(4); //extrait l'info de langue } }else return ""; } //*********** @SimpleFunction(description = "Edit Group's Tag") public void EditGroup(String filePath,String NewTag) throws IOException { filePath=defineDir(filePath); if (new File(filePath).exists()) { replaceTag("TIT1", filePath, NewTag); } } @SimpleFunction(description = "Get Group's Tag") public String GetGroup(String filePath){ filePath=defineDir(filePath); if (new File(filePath).exists()) { return GetTextTag(filePath, "TIT1"); }else return""; } //************ @SimpleFunction(description = "Get volume adjustment's Tag (return -127 if not found)" ) public byte GetVolumeAdjustment(String filePath){ filePath=defineDir(filePath); if (new File(filePath).exists()) { return GetByteTag(filePath, "RVAD"); }else return -127; } @SimpleFunction(description = "Edit volume adjustment's Tag (must be an integer from 0 to 255)") public void EditVolumeAdjustment(String filePath,byte NewVolAdj) throws IOException { filePath=defineDir(filePath); if (new File(filePath).exists()) { replaceByteTag("RVAD",filePath,NewVolAdj); } } //*********** @SimpleFunction(description = "Edit Rate's Tag") public void EditRate(String filePath,int NewRate) throws IOException { filePath=defineDir(filePath); if (new File(filePath).exists()) { replaceIntTag("POPM",filePath,NewRate); } } @SimpleFunction(description = "Get Rate Tag (return -1 if not found)") public int GetRate(String filePath) throws IOException { filePath=defineDir(filePath); if (new File(filePath).exists()) { return GetIntTag(filePath, "POPM"); } else return -1; } //*********** @SimpleFunction(description = "Edit Last play date's Tag") public void EditLastPlayDate(String filePath,long NewLpd) throws IOException { filePath=defineDir(filePath); if (new File(filePath).exists()) { replaceLongTag("LPDA",filePath,NewLpd); } } @SimpleFunction(description = "Get Last play date Tag (return -1 if not found)") public long GetLastPlayDate(String filePath) throws IOException { filePath=defineDir(filePath); if (new File(filePath).exists()) { return GetLongTag(filePath, "LPDA"); } else return -1; } //************* @SimpleFunction(description = "return file's path") public String GetPath(String filePath){ String path=defineDir(filePath); //path=form.getExternalFilesDir("Mp3Tags").getAbsolutePath()+"/tmp.png"; return path; } String defineDir(String path){ if(path.startsWith("//")){ return "/storage/emulated/0/AppInventor/assets/"+path.substring(2,path.length()); }else{ if(path.startsWith("/")){ return "/mnt/sdcard"+path; }else { if (path.startsWith("file:///") ) { return path.substring(7,path.length()); } else return path; } } } //********** @SimpleFunction(description = "return file's album art") public String GetAlbum_Art(String filePath){ String song=defineDir (filePath); File file=new File(song); byte[] art = new byte[0]; Bitmap songImage=null; metaRetriever = new MediaMetadataRetriever(); if (file.exists()) { metaRetriever.setDataSource(song); try {song="try"; art = metaRetriever.getEmbeddedPicture(); songImage = BitmapFactory .decodeByteArray(art, 0, art.length); } catch (Exception e) {song="exception"; } }else { //song="file not found : "+ file.toString(); } String filename =form.getExternalFilesDir("Mp3Tags").getAbsolutePath()+"/tmp.png"; String reponse=saveBitmap(songImage,filename); return reponse; } //************** @SimpleFunction(description = "Get Lyrics") public String GetLyrics(String filePath) throws IOException { filePath=defineDir(filePath); if (new File(filePath).exists()) { byte[] bytesOfAudio = getByteArrayFromAudio(filePath); int positionTag = chercheTagChaine(bytesOfAudio, "USLT"); if (positionTag == 0) { //Toast.makeText(this, "Tag doesn't exists", Toast.LENGTH_LONG).show(); return ""; } else { int longueurDuTag=(bytesOfAudio[positionTag+7]+256)%256+((bytesOfAudio[positionTag+6]+256)%256)*256; Log.d("*** mylog ****", "longueurDuTag "+longueurDuTag+" "+bytesOfAudio[positionTag+6]+" "+bytesOfAudio[positionTag+7]); byte[] lyricsBytes=new byte[longueurDuTag-5]; for (int f = 0; f < longueurDuTag-5; f++) { //copie des octets jusqu'à l'octet de Rating lyricsBytes[f] = bytesOfAudio[positionTag+15+f]; } return new String(lyricsBytes); } } else return ""; } //*********** //********************************* Get procedures public byte GetTag(String filePath,String Tag){ byte[] bytesOfAudio = getByteArrayFromAudio(filePath); int positionTag = chercheTagChaine(bytesOfAudio, Tag); if (positionTag == 0) { return -127; } else { int longueurDuTag=bytesOfAudio[positionTag+7]; byte val=bytesOfAudio[positionTag+9+longueurDuTag-4]; return val; } } public String GetTextTag(String filePath,String Tag){ byte[] bytesOfAudio = getByteArrayFromAudio(filePath); int positionTag = chercheTagChaine(bytesOfAudio, Tag); if (positionTag == 0) { //Toast.makeText(this, "Tag doesn't exists", Toast.LENGTH_LONG).show(); return ""; } else { int longueurDuTag=(bytesOfAudio[positionTag+7]+256)%256+((bytesOfAudio[positionTag+6]+256)%256)*256; Log.d("*** mylog ****", "longueurDuTag "+longueurDuTag+" "+bytesOfAudio[positionTag+6]+" "+bytesOfAudio[positionTag+7]); byte[] tagBytes=new byte[longueurDuTag-1]; for (int f = 0; f < longueurDuTag-1; f++) { //copie des octets jusqu'à l'octet de Rating tagBytes[f] = bytesOfAudio[positionTag+11+f]; } return new String(tagBytes); } } public int GetIntTag(String filePath,String Tag){ byte[] bytesOfAudio = getByteArrayFromAudio(filePath); int positionTag = chercheTagChaine(bytesOfAudio, Tag); if (positionTag == 0) { return -1; } else { int longueurDuTag=bytesOfAudio[positionTag+7]; int val=(bytesOfAudio[positionTag+9+longueurDuTag-4]+256)%256; return val; } } public long GetLongTag(String filePath,String Tag){ byte[] bytesOfAudio = getByteArrayFromAudio(filePath); int positionTag = chercheTagChaine(bytesOfAudio, Tag); if (positionTag == 0) { return -1; } else { int longueurDuTag=bytesOfAudio[positionTag+7]; byte[] array= new byte[8]; for (int f = 0; f < 7; f++) { //copie des 8 octets du long array[f] = bytesOfAudio[positionTag+11+f]; } ByteBuffer buffer = ByteBuffer.wrap(array); return buffer.getLong(); } } public byte GetByteTag(String filePath,String Tag){ byte[] bytesOfAudio = getByteArrayFromAudio(filePath); int positionTag = chercheTagChaine(bytesOfAudio, Tag); if (positionTag == 0) { return -127; } else { int longueurDuTag=bytesOfAudio[positionTag+7]; byte val=bytesOfAudio[positionTag+9+longueurDuTag]; return val; } } //******************************* sauvegarde image *********************************** private String saveBitmap(Bitmap bitmap, String path) { File file = null; String reponse="vide"; if (bitmap != null) { file = new File(path); try { FileOutputStream outputStream = null; try { outputStream = new FileOutputStream(path); //here is set your file path where you want to save or also here you can set file object directly bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream); // bitmap is your Bitmap instance, if you want to compress it you can compress reduce percentage // PNG is a lossless format, the compression factor (100) is ignored reponse=file.toString(); } catch (Exception e) { e.printStackTrace(); reponse="erreur"; } finally { try { if (outputStream != null) { outputStream.close(); } } catch (IOException e) { //e.printStackTrace(); } } } catch (Exception e) { e.printStackTrace(); reponse="erreur 2"; } }else{reponse="null";} return reponse; } //******************************* sauvegarde image FIN *********************************** //******************************** Edition des Tags ******************************************** void createTag(String Tag ,String filePath) throws IOException { byte[] bytesOfAudioOrigine = getByteArrayFromAudio(filePath); Log.d("*** mylog ***", "longueur Fichier avant dreation structure " + bytesOfAudioOrigine.length); if (bytesOfAudioOrigine[0]!=73 || bytesOfAudioOrigine[1]!=68 || bytesOfAudioOrigine[2]!=51){ //la structure ID3 n'existe pas byte[] bytesModifies = new byte[bytesOfAudioOrigine.length + 10]; bytesModifies[0]=73; bytesModifies[1]=68; bytesModifies[2]=51; bytesModifies[3]=3; //version3 bytesModifies[4]=0; //point0 bytesModifies[5]=0; //flag bytesModifies[6]=0; //taille Tag bytesModifies[7]=0; bytesModifies[8]=0; bytesModifies[9]=0; for (int f = 0; f < bytesOfAudioOrigine.length; f++) { //copie du reste du fichier bytesModifies[10+ f] = bytesOfAudioOrigine[f]; } Log.d("*** mylog ***", "Structure ID3 créé "); bytesToFile(bytesModifies, filePath); Log.d("*** mylog ***", "Structure ID3 Sauvée "); } byte[] bytesOfAudio = getByteArrayFromAudio(filePath); //Recharge le fichier au cas où j'ai créé la structure Tags Log.d("*** mylog ***", "longueur Fichier avant ajout Tag " + bytesOfAudio.length); if (bytesOfAudio[0]==73 && bytesOfAudio[1]==68 && bytesOfAudio[2]==51){ //la structure ID3 existe mais le tag n'existe pas //byte[] longueurActuelleNulle= new byte[]{0,0,0,0}; int longueurTagVierge=11; byte[]longueurTotalTag=new byte[]{bytesOfAudio[6],bytesOfAudio[7],bytesOfAudio[8],bytesOfAudio[9]}; Log.d("*** mylog ***", "longueurTotalTag " + " "+longueurTotalTag[0]+ " "+longueurTotalTag[1]+ " "+longueurTotalTag[2]+ " "+longueurTotalTag[3]); longueurTotalTag=changeLongueurTotalDesTag(longueurTagVierge,longueurTotalTag); Log.d("*** mylog ***", "longueurTotalTag " + " "+longueurTotalTag[0]+ " "+longueurTotalTag[1]+ " "+longueurTotalTag[2]+ " "+longueurTotalTag[3]); byte[] bytesModifies = new byte[bytesOfAudio.length + longueurTagVierge]; for (int f = 0; f < 6; f++) { //copie des octets 6 1ers octects du header soit de 0 à 4 (ID3 et version et flag) bytesModifies[f] = bytesOfAudio[f]; } for (int f = 0; f <4 ; f++) { //copie des octets 4 octects de la nouvelle longueur totale des Tags soit de 6 à 9 bytesModifies[6+f] = longueurTotalTag[f]; } bytesModifies[10]=(byte)Tag.charAt(0); //copie de l'identifiant du Tag sur 4 caractères bytesModifies[11]=(byte)Tag.charAt(1); bytesModifies[12]=(byte)Tag.charAt(2); bytesModifies[13]=(byte)Tag.charAt(3); bytesModifies[14]=(byte)0; bytesModifies[15]=(byte)0; bytesModifies[16]=(byte)0; bytesModifies[17]=(byte)1; //lg du Tag vierge //ajouter 1 car After the header always one Byte with value 00 follows and then begins frame body. Size has to include this Byte. bytesModifies[18]=(byte)0; //8-9 Flags bytesModifies[19]=(byte)0; bytesModifies[20]=(byte)0; //le 10 est nulllongueurTotalTag Log.d("*** mylog ***", "Copie des 20 premiers octets "); for (int f = 10; f < bytesOfAudio.length; f++) { //copie du reste du fichier bytesModifies[longueurTagVierge+ f] = bytesOfAudio[f]; } Log.d("*** mylog ***", "fin de copie octets -> enregistrement"); bytesToFile(bytesModifies, filePath); } } byte[] changeLongueurTotalDesTag(int ecart,byte[] longueurActuelle){ byte[] nouvelleLongueuerTotale=new byte[4]; int lgActuelle=longueurActuelle[0]*128*128*128+longueurActuelle[1]*128*128+longueurActuelle[2]*128+longueurActuelle[3]; Log.d("*** mylog ***", "lgActuelle " + lgActuelle+ " "+longueurActuelle[0]+ " "+longueurActuelle[1]+ " "+longueurActuelle[2]+ " "+longueurActuelle[3]); int nouvelleLongueur=lgActuelle+ecart; byte premierOctet = (byte) (nouvelleLongueur / (128 * 128*128)); //128 car codé sur 7 bits, le 8eme étant à 0 byte deuxiemeOctet = (byte) ((nouvelleLongueur - premierOctet * 128 * 128*128) / (128*128)); byte troisiemeOctet = (byte) ((nouvelleLongueur - premierOctet * 128 * 128*128 - deuxiemeOctet * 128*128)/128); byte quatriemeOctet = (byte) (nouvelleLongueur - premierOctet * 128 * 128*128 - deuxiemeOctet * 128*128 - troisiemeOctet*128); nouvelleLongueuerTotale[0]=premierOctet; nouvelleLongueuerTotale[1]=deuxiemeOctet; nouvelleLongueuerTotale[2]=troisiemeOctet; nouvelleLongueuerTotale[3]=quatriemeOctet; Log.d("*** mylog ***", "nouvelleLongueur " + nouvelleLongueur+ " "+nouvelleLongueuerTotale[0]+ " "+nouvelleLongueuerTotale[1]+ " "+nouvelleLongueuerTotale[2]+ " "+nouvelleLongueuerTotale[3]); return nouvelleLongueuerTotale; } void replaceTag(String Tag,String filePath ,String newTag) throws IOException { byte[] bytesOfAudioOrigine = getByteArrayFromAudio(filePath); Log.d("*** mylog ***", "longueur Fichier Origine " + bytesOfAudioOrigine.length); int positionTagOrigine = chercheTagChaine(bytesOfAudioOrigine, Tag); if (positionTagOrigine == 0) { createTag(Tag,filePath); } byte[] bytesOfAudio = getByteArrayFromAudio(filePath); //recharge le fichier au cas où j'ai créée le Tag dans le if précédent sinon pb de longueur de Array Log.d("*** mylog ***", "longueur Fichier avant remplacement Tag " + bytesOfAudio.length); int positionTag = chercheTagChaine(bytesOfAudio, Tag); int longueurTagActuel = ((bytesOfAudio[positionTag + 7]+256)%256) + ((bytesOfAudio[positionTag + 6]+256)%256) * 256 + ((bytesOfAudio[positionTag + 5]+256)%256) * 256 * 256; //longueur codée sur 3 octets Log.d("*** mylog ***", "longueurTagActuel " + longueurTagActuel); int ecartLongueurTag = newTag.length()+1 - longueurTagActuel; byte[]longueurTotalTag=new byte[]{bytesOfAudio[6],bytesOfAudio[7],bytesOfAudio[8],bytesOfAudio[9]}; longueurTotalTag=changeLongueurTotalDesTag(ecartLongueurTag,longueurTotalTag); byte[] bytesModifies = new byte[bytesOfAudio.length + ecartLongueurTag]; for (int f = 0; f < 6; f++) { //copie des octets 6 1ers octects du header soit de 0 à 4 (ID3 et version et flag) bytesModifies[f] = bytesOfAudio[f]; } for (int f = 0; f <4 ; f++) { //copie des octets 4 octects de la nouvelle longueur totale des Tags soit de 6 à 9 bytesModifies[6+f] = longueurTotalTag[f]; } for (int f = 10; f < positionTag + 4; f++) { //copie des octets jusqu'aux 1er octets de longueur compris (le 4 ) //0-3 Frame identifier bytesModifies[f] = bytesOfAudio[f]; } byte premierOctet = (byte) ((newTag.length()+1) / (256 * 256)); //ajouter 1 car After the header always one Byte with value 00 follows and then begins frame body. Size has to include this Byte. byte deuxiemeOctet = (byte) (((newTag.length()+1) - premierOctet * 256 * 256) / 256); byte troisiemeOctet = (byte) ((newTag.length()+1) - premierOctet * 256 * 256 - deuxiemeOctet * 256); bytesModifies[positionTag + 5] = premierOctet; //4-7 Size le 4 est copié plus haut bytesModifies[positionTag + 6] = deuxiemeOctet; bytesModifies[positionTag + 7] = troisiemeOctet; bytesModifies[positionTag + 8] = bytesOfAudio[positionTag + 8]; //8-9 Flags bytesModifies[positionTag + 9] = bytesOfAudio[positionTag + 9]; if (Tag=="COMM"){ //specifique au commentaire bytesModifies[positionTag + 11]=(byte)"fra".charAt(0); bytesModifies[positionTag + 12]=(byte)"fra".charAt(1); bytesModifies[positionTag + 13]=(byte)"fra".charAt(2); bytesModifies[positionTag + 14]=0; for (int f = 0; f < newTag.length()-4; f++) { //copie du nouveau tag bytesModifies[positionTag + 15 + f] = (byte) newTag.charAt(f+4); } }else{ for (int f = 0; f < newTag.length(); f++) { //copie du nouveau tag bytesModifies[positionTag + 11 + f] = (byte) newTag.charAt(f); //11 car le 10 est null } } for (int f = 0; f < bytesOfAudio.length - (positionTag + 10 + longueurTagActuel); f++) { //copie du reste du fichier bytesModifies[positionTag + 11 + newTag.length() + f] = bytesOfAudio[positionTag + 10 + longueurTagActuel + f]; } bytesToFile(bytesModifies, filePath); } int chercheTagChaine(byte[] bytes,String Tag){ int pos=0; Log.d("*** mylog ***", "recherche Tag "+ pos); while (pos