mirror of
https://github.com/playmusicexporter/playmusicexporter
synced 2024-05-14 18:46:42 +00:00
7dafd21843
* Added a refresh button to bin the previous database, copy the new one, and reload it into the UI. Saves having to kill the app and re-open to see the latest changes. Should address "Issue #8 Reload Play Music Files" * Pull Request actions. * Fixed Typo and Translation
851 lines
28 KiB
Java
851 lines
28 KiB
Java
/*
|
|
* Copyright (c) 2015 David Schulte
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
package de.arcus.playmusiclib;
|
|
|
|
import android.content.Context;
|
|
import android.content.pm.ApplicationInfo;
|
|
import android.content.pm.PackageManager;
|
|
import android.database.Cursor;
|
|
import android.database.SQLException;
|
|
import android.database.sqlite.SQLiteDatabase;
|
|
import android.graphics.Bitmap;
|
|
import android.net.Uri;
|
|
import android.os.Build;
|
|
import android.os.Environment;
|
|
import android.os.ParcelFileDescriptor;
|
|
import android.support.v4.content.FileProvider;
|
|
import android.support.v4.provider.DocumentFile;
|
|
import android.text.TextUtils;
|
|
import android.util.Log;
|
|
|
|
import com.mpatric.mp3agic.ID3v1Genres;
|
|
import com.mpatric.mp3agic.ID3v1Tag;
|
|
import com.mpatric.mp3agic.ID3v2;
|
|
import com.mpatric.mp3agic.ID3v22Tag;
|
|
import com.mpatric.mp3agic.ID3v23Tag;
|
|
import com.mpatric.mp3agic.ID3v24Tag;
|
|
import com.mpatric.mp3agic.Mp3File;
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
|
|
import de.arcus.framework.logger.Logger;
|
|
import de.arcus.framework.superuser.SuperUser;
|
|
import de.arcus.framework.superuser.SuperUserTools;
|
|
import de.arcus.framework.utils.FileTools;
|
|
import de.arcus.playmusiclib.enums.ID3v2Version;
|
|
import de.arcus.playmusiclib.exceptions.CouldNotOpenDatabaseException;
|
|
import de.arcus.playmusiclib.exceptions.NoSuperUserException;
|
|
import de.arcus.playmusiclib.exceptions.PlayMusicNotFoundException;
|
|
import de.arcus.playmusiclib.items.MusicTrack;
|
|
|
|
|
|
/**
|
|
* Connects to the PlayMusic data
|
|
*/
|
|
public class PlayMusicManager {
|
|
/**
|
|
* PlayMusic package id
|
|
*/
|
|
public static final String PLAYMUSIC_PACKAGE_ID = "com.google.android.music";
|
|
|
|
/**
|
|
* The last created instance
|
|
*/
|
|
private static PlayMusicManager instance;
|
|
|
|
/**
|
|
* @return Gets the last created instance or returns null if there is no instance
|
|
*/
|
|
public static PlayMusicManager getInstance() {
|
|
return instance;
|
|
}
|
|
|
|
/**
|
|
* Context of the app, needed to access to the package manager
|
|
*/
|
|
private Context mContext;
|
|
|
|
/**
|
|
* @return Gets the app context
|
|
*/
|
|
public Context getContext() {
|
|
return mContext;
|
|
}
|
|
|
|
/**
|
|
* Play Music database
|
|
*/
|
|
private SQLiteDatabase mDatabase;
|
|
|
|
/**
|
|
* @return Gets the database
|
|
*/
|
|
public SQLiteDatabase getDatabase() {
|
|
return mDatabase;
|
|
}
|
|
|
|
/**
|
|
* Path to the private app data
|
|
* Eg.: /data/data/com.google.android.music/
|
|
*/
|
|
private String mPathPrivateData;
|
|
|
|
/**
|
|
* Paths to all possible public app data
|
|
* Eg.: /sdcard/Android/data/com.google.android.music/
|
|
*/
|
|
private String[] mPathPublicData;
|
|
|
|
/**
|
|
* Application info from PlayMusic
|
|
*/
|
|
private ApplicationInfo mPlayMusicApplicationInfo;
|
|
|
|
/**
|
|
* @return Gets the path to the database
|
|
*/
|
|
private String getDatabasePath() {
|
|
return mPathPrivateData + "/databases/music.db";
|
|
}
|
|
|
|
/**
|
|
* The database will be copied to a temp folder to access from the app
|
|
* @return Gets the temp path to the database
|
|
*/
|
|
private String getTempDatabasePath() {
|
|
return getTempPath() + "music.db";
|
|
}
|
|
|
|
/**
|
|
* @return Gets the temp path to the exported music
|
|
*/
|
|
private String getTempPath() {
|
|
// Marshmallow hack
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
// Use the internal storage instead
|
|
String path = Environment.getExternalStorageDirectory() + "/PlayMusicExporter/";
|
|
FileTools.directoryCreate(path);
|
|
return path;
|
|
} else {
|
|
return mContext.getCacheDir().getAbsolutePath();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If this is set the data source will only load offline tracks
|
|
*/
|
|
private boolean mOfflineOnly;
|
|
|
|
/**
|
|
* @return Returns whether the data source should only load offline tracks
|
|
*/
|
|
public boolean getOfflineOnly() {
|
|
return mOfflineOnly;
|
|
}
|
|
|
|
/**
|
|
* @param offlineOnly Sets whether the data source should only load offline tracks
|
|
*/
|
|
public void setOfflineOnly(boolean offlineOnly) {
|
|
mOfflineOnly = offlineOnly;
|
|
}
|
|
|
|
/**
|
|
* If this is set the exporter will add ID3 tag to the mp3 files
|
|
*/
|
|
private boolean mID3Enable = true;
|
|
|
|
/**
|
|
* @return Gets whether the exporter adds ID3 tags to the mp3 files
|
|
*/
|
|
public boolean getID3Enable() {
|
|
return mID3Enable;
|
|
}
|
|
|
|
/**
|
|
* @param id3Enable Sets whether the exporter adds ID3 tags to the mp3 files
|
|
*/
|
|
public void setID3Enable(boolean id3Enable) {
|
|
mID3Enable = id3Enable;
|
|
}
|
|
|
|
/**
|
|
* If this is set the exporter will add the artwork to the ID2v2 tag
|
|
*/
|
|
private boolean mID3EnableArtwork = true;
|
|
|
|
/**
|
|
* @return Gets whether the exporter adds the artwork image
|
|
*/
|
|
public boolean getID3EnableArtwork() {
|
|
return mID3EnableArtwork;
|
|
}
|
|
|
|
/**
|
|
* @param id3EnableArtwork Sets whether the exporter adds the artwork image
|
|
*/
|
|
public void setID3EnableArtwork(boolean id3EnableArtwork) {
|
|
mID3EnableArtwork = id3EnableArtwork;
|
|
}
|
|
|
|
/**
|
|
* The ID3 artwork format (eg. JPEG or PNG)
|
|
*/
|
|
private Bitmap.CompressFormat mID3ArtworkFormat = Bitmap.CompressFormat.JPEG;
|
|
|
|
/**
|
|
* @return Gets the current artwork format
|
|
*/
|
|
public Bitmap.CompressFormat getID3ArtworkFormat() {
|
|
return mID3ArtworkFormat;
|
|
}
|
|
|
|
/**
|
|
* @param id3ArtworkFormat Sets the artwork format for the id3 tag
|
|
*/
|
|
public void setID3ArtworkFormat(Bitmap.CompressFormat id3ArtworkFormat) {
|
|
mID3ArtworkFormat = id3ArtworkFormat;
|
|
}
|
|
|
|
/**
|
|
* The ID3 artwork maximum size (0 = use original size)
|
|
*/
|
|
private int mID3ArtworkMaximumSize = 512;
|
|
|
|
/**
|
|
* @return Gets the current artwork format
|
|
*/
|
|
public int getID3ArtworkMaximumSize() {
|
|
return mID3ArtworkMaximumSize;
|
|
}
|
|
|
|
/**
|
|
* @param id3ArtworkMaximumSize Sets the artwork maximum size of the artwork.
|
|
* If the original artwork is larger than this value the app will
|
|
* sample it down. (0 = use original size)
|
|
*/
|
|
public void setID3ArtworkMaximumSize(int id3ArtworkMaximumSize) {
|
|
mID3ArtworkMaximumSize = id3ArtworkMaximumSize;
|
|
}
|
|
|
|
/**
|
|
* If this is set the exporter will also adds ID3v1 tags
|
|
*/
|
|
private boolean mID3EnableFallback = true;
|
|
|
|
/**
|
|
* @return Gets whether the exporter adds ID3v1 tags as fallback
|
|
*/
|
|
public boolean getID3EnableFallback() {
|
|
return mID3EnableFallback;
|
|
}
|
|
|
|
/**
|
|
* @param id3EnableFallback Sets whether the exporter adds ID3v1 tags as fallback
|
|
*/
|
|
public void setID3EnableFallback(boolean id3EnableFallback) {
|
|
mID3EnableFallback = id3EnableFallback;
|
|
}
|
|
|
|
/**
|
|
* The sub version of ID3v2
|
|
* Use 2.3 for default to fix issues with the Windows Windows Media Player
|
|
*/
|
|
private ID3v2Version mID3v2Version = ID3v2Version.ID3v23;
|
|
|
|
/**
|
|
* @return Gets the sub version of ID3v2
|
|
*/
|
|
public ID3v2Version getID3v2Version() {
|
|
return mID3v2Version;
|
|
}
|
|
|
|
/**
|
|
* @param id3v2Version Sets the sub version of ID3v2
|
|
*/
|
|
public void setID3v2Version(ID3v2Version id3v2Version) {
|
|
mID3v2Version = id3v2Version;
|
|
}
|
|
|
|
/**
|
|
* Creates a new PlayMusic manager
|
|
* @param context App context
|
|
*/
|
|
public PlayMusicManager(Context context) {
|
|
mContext = context;
|
|
instance = this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Loads all needed information and opens the database
|
|
* @throws PlayMusicNotFoundException PlayMusic is not installed
|
|
* @throws NoSuperUserException No super user permissions
|
|
* @throws CouldNotOpenDatabaseException Could not open the database
|
|
*/
|
|
public void startUp() throws PlayMusicNotFoundException, NoSuperUserException, CouldNotOpenDatabaseException {
|
|
// Gets the package manager
|
|
PackageManager packageManager = mContext.getPackageManager();
|
|
|
|
try {
|
|
// Loads the application info
|
|
mPlayMusicApplicationInfo = packageManager.getApplicationInfo(PLAYMUSIC_PACKAGE_ID, 0);
|
|
} catch (PackageManager.NameNotFoundException e) {
|
|
// No PlayMusic
|
|
throw new PlayMusicNotFoundException();
|
|
}
|
|
|
|
|
|
// Path to the private data
|
|
mPathPrivateData = mPlayMusicApplicationInfo.dataDir;
|
|
|
|
List<String> publicDataList = new ArrayList<>();
|
|
// Search on all sdcards
|
|
for (String storage : FileTools.getStorages()) {
|
|
String publicData = storage + "/Android/data/com.google.android.music";
|
|
|
|
// Directory exists
|
|
if (FileTools.directoryExists(publicData))
|
|
publicDataList.add(publicData);
|
|
}
|
|
// Convert to array
|
|
mPathPublicData = publicDataList.toArray(new String[publicDataList.size()]);
|
|
|
|
// Loads the database
|
|
loadDatabase();
|
|
}
|
|
|
|
/**
|
|
* Copies the database to a temp directory and opens it
|
|
* @throws NoSuperUserException No super user permissions
|
|
* @throws de.arcus.playmusiclib.exceptions.CouldNotOpenDatabaseException Could not open the database
|
|
*/
|
|
private void loadDatabase() throws NoSuperUserException, CouldNotOpenDatabaseException {
|
|
// Ask for super user
|
|
if (!SuperUser.askForPermissions())
|
|
throw new NoSuperUserException();
|
|
|
|
// Close the database
|
|
closeDatabase();
|
|
|
|
// Copy the database to the temp folder
|
|
if (!SuperUserTools.fileCopy(getDatabasePath(), getTempDatabasePath()))
|
|
throw new CouldNotOpenDatabaseException();
|
|
|
|
// Opens the database
|
|
try {
|
|
mDatabase = SQLiteDatabase.openDatabase(getTempDatabasePath(), null, SQLiteDatabase.OPEN_READONLY);
|
|
} catch (SQLException e) {
|
|
throw new CouldNotOpenDatabaseException();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reloads the database from PlayMusic
|
|
* @throws NoSuperUserException No super user permissions
|
|
* @throws CouldNotOpenDatabaseException Could not open the database
|
|
*/
|
|
public void reloadDatabase() throws NoSuperUserException, CouldNotOpenDatabaseException {
|
|
// Reload database
|
|
loadDatabase();
|
|
}
|
|
|
|
/**
|
|
* Closes the database if it's open
|
|
*/
|
|
private void closeDatabase() {
|
|
if (mDatabase == null) return;
|
|
|
|
mDatabase.close();
|
|
}
|
|
|
|
/**
|
|
* Debug function to get the database
|
|
*/
|
|
public void copyDatabaseToSdCard() {
|
|
FileTools.fileCopy(getTempDatabasePath(), Environment.getExternalStorageDirectory() + "/music.db");
|
|
}
|
|
|
|
/**
|
|
* @return Gets the path to the private music
|
|
*/
|
|
public String getPrivateMusicPath() {
|
|
return mPathPrivateData + "/files/music";
|
|
}
|
|
|
|
/**
|
|
* Gets the path to the music track
|
|
* @param localCopyPath The local copy path
|
|
* @return The path to the music file
|
|
*/
|
|
public String getMusicFile(String localCopyPath) {
|
|
// LocalCopyPath is empty
|
|
if (TextUtils.isEmpty(localCopyPath)) return null;
|
|
|
|
String path;
|
|
|
|
// Search in the public data
|
|
for (String publicData : mPathPublicData) {
|
|
path = publicData + "/files/music/" + localCopyPath;
|
|
|
|
if (FileTools.fileExists(path)) return path;
|
|
}
|
|
|
|
|
|
// Private music path
|
|
path = getPrivateMusicPath() + "/" + localCopyPath;
|
|
// Don't check if the file exists, this will freeze the UI thread
|
|
//if (SuperUserTools.fileExists(path)) return path;
|
|
|
|
return path;
|
|
}
|
|
|
|
/**
|
|
* @return Gets the path to the private files
|
|
*/
|
|
public String getPrivateFilesPath() {
|
|
return mPathPrivateData + "/files";
|
|
}
|
|
|
|
/**
|
|
* Gets the full path to the artwork
|
|
* @param artworkPath The artwork path
|
|
* @return The full path to the artwork
|
|
*/
|
|
public String getArtworkPath(String artworkPath) {
|
|
// Artwork path is empty
|
|
if (TextUtils.isEmpty(artworkPath)) return null;
|
|
|
|
String path;
|
|
|
|
// Fix the path for Play Music 5.9.1854
|
|
if (!artworkPath.startsWith("artwork/"))
|
|
artworkPath = "artwork/" + artworkPath;
|
|
|
|
// Search in the public data
|
|
for (String publicData : mPathPublicData) {
|
|
path = publicData + "/files/" + artworkPath;
|
|
|
|
if (FileTools.fileExists(path)) return path;
|
|
}
|
|
|
|
// Private artwork path
|
|
path = getPrivateFilesPath() + "/" + artworkPath;
|
|
|
|
// Don't check if the file exists, this will freeze the UI thread
|
|
// if (SuperUserTools.fileExists(path)) return path;
|
|
|
|
return path;
|
|
}
|
|
|
|
|
|
/**
|
|
* Exports a track to the sd card
|
|
* @param musicTrack The music track you want to export
|
|
* @param dest The destination path
|
|
* @param forceOverwrite Forces overwrite of the destination file
|
|
* @return Returns whether the export was successful
|
|
*/
|
|
public boolean exportMusicTrack(MusicTrack musicTrack, String dest, boolean forceOverwrite ) {
|
|
// Creates the destination directory
|
|
File directory = new File(dest).getParentFile();
|
|
|
|
|
|
// Filename
|
|
String filename = new File(dest).getName();
|
|
|
|
return exportMusicTrack(musicTrack, Uri.fromFile(directory), filename, forceOverwrite);
|
|
}
|
|
|
|
/**
|
|
* Exports a track to the sd card
|
|
* @param musicTrack The music track you want to export
|
|
* @param uri The document tree
|
|
* @param forceOverwrite Forces overwrite of the destination file
|
|
* @return Returns whether the export was successful
|
|
*/
|
|
public boolean exportMusicTrack(MusicTrack musicTrack, Uri uri, String path, boolean forceOverwrite) {
|
|
|
|
// Check for null
|
|
if (musicTrack == null) return false;
|
|
|
|
String srcFile = musicTrack.getSourceFile();
|
|
|
|
// Could not find the source file
|
|
if (srcFile == null) return false;
|
|
|
|
String uniqueID = UUID.randomUUID().toString();
|
|
|
|
if ( forceOverwrite || !isAlreadyThere(uri, path) )
|
|
{
|
|
|
|
String fileTmp = getTempPath() + uniqueID +"_tmp.mp3";
|
|
|
|
// Copy to temp path failed
|
|
if (!SuperUserTools.fileCopy(srcFile, fileTmp))
|
|
return false;
|
|
|
|
// Encrypt the file
|
|
if (musicTrack.isEncoded()) {
|
|
String fileTmpCrypt = getTempPath() + uniqueID +"_crypt.mp3";
|
|
|
|
// Encrypts the file
|
|
if (trackEncrypt(musicTrack, fileTmp, fileTmpCrypt)) {
|
|
// Remove the old tmp file
|
|
FileTools.fileDelete(fileTmp);
|
|
|
|
// New tmp file
|
|
fileTmp = fileTmpCrypt;
|
|
} else {
|
|
Logger.getInstance().logWarning("ExportMusicTrack", "Encrypting failed! Continue with decrypted file.");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
String dest;
|
|
Uri copyUri = null;
|
|
if (uri.toString().startsWith("file://")) {
|
|
// Build the full path
|
|
dest = uri.buildUpon().appendPath(path).build().getPath();
|
|
|
|
String parentDirectory = new File(dest).getParent();
|
|
FileTools.directoryCreate(parentDirectory);
|
|
} else {
|
|
// Complex uri (Lollipop)
|
|
dest = getTempPath() + uniqueID +"_final.mp3";
|
|
|
|
// The root
|
|
DocumentFile document = DocumentFile.fromTreeUri(mContext, uri);
|
|
|
|
// Creates the subdirectories
|
|
String[] directories = path.split("\\/");
|
|
for(int i=0; i<directories.length - 1; i++) {
|
|
String directoryName = directories[i];
|
|
boolean found = false;
|
|
|
|
// Search all sub elements
|
|
for (DocumentFile subDocument: document.listFiles()) {
|
|
// Directory exists
|
|
if (subDocument.isDirectory() && subDocument.getName().equalsIgnoreCase(directoryName)) {
|
|
document = subDocument;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
// Create the directory
|
|
document = document.createDirectory(directoryName);
|
|
}
|
|
}
|
|
|
|
// Gets the filename
|
|
String filename = directories[directories.length - 1];
|
|
|
|
for (DocumentFile subDocument: document.listFiles()) {
|
|
// Directory exists
|
|
if (subDocument.isFile() ){
|
|
if ( filename != null && subDocument.getName().equalsIgnoreCase(filename)) {
|
|
// Delete the file
|
|
if ( forceOverwrite ) {
|
|
Logger.getInstance().logWarning("ExportMusicTrack", "(forceOverwrite) Deleting original file: " + filename);
|
|
}
|
|
subDocument.delete();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create the mp3 file
|
|
document = document.createFile("music/mp3", filename);
|
|
|
|
// Create the directories
|
|
copyUri = document.getUri();
|
|
}
|
|
|
|
|
|
// We want to export the ID3 tags
|
|
if (mID3Enable) {
|
|
// Adds the meta data
|
|
if (!trackWriteID3(musicTrack, fileTmp, dest)) {
|
|
Logger.getInstance().logWarning("ExportMusicTrack", "ID3 writer failed! Continue without ID3 tags.");
|
|
|
|
// Failed, moving without meta data
|
|
if (!FileTools.fileMove(fileTmp, dest)) {
|
|
Logger.getInstance().logError("ExportMusicTrack", "Moving the raw file failed!");
|
|
|
|
// Could not copy the file
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
// Moving the file
|
|
if (!FileTools.fileMove(fileTmp, dest)) {
|
|
Logger.getInstance().logError("ExportMusicTrack", "Moving the raw file failed!");
|
|
|
|
// Could not copy the file
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// We need to copy the file to a uri
|
|
if (copyUri != null) {
|
|
// Lollipop only
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
try {
|
|
// Gets the file descriptor
|
|
ParcelFileDescriptor parcelFileDescriptor = mContext.getContentResolver().openFileDescriptor(copyUri, "w");
|
|
|
|
// Gets the output stream
|
|
FileOutputStream fileOutputStream = new FileOutputStream(parcelFileDescriptor.getFileDescriptor());
|
|
|
|
// Gets the input stream
|
|
FileInputStream fileInputStream = new FileInputStream(dest);
|
|
|
|
// Copy the stream
|
|
FileTools.fileCopy(fileInputStream, fileOutputStream);
|
|
|
|
// Close all streams
|
|
fileOutputStream.close();
|
|
fileInputStream.close();
|
|
parcelFileDescriptor.close();
|
|
|
|
} catch (FileNotFoundException e) {
|
|
Logger.getInstance().logError("ExportMusicTrack", "File not found!");
|
|
|
|
// Could not copy the file
|
|
return false;
|
|
} catch (IOException e) {
|
|
Logger.getInstance().logError("ExportMusicTrack", "Failed to write the document: " + e.toString());
|
|
|
|
// Could not copy the file
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Delete temp files
|
|
cleanUp(uniqueID);
|
|
|
|
// Adds the file to the media system
|
|
//new MediaScanner(mContext, dest);
|
|
|
|
} else {
|
|
Logger.getInstance().logInfo("exportMusicTrack", path + " already exists, skipping." );
|
|
}
|
|
|
|
// Done
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Checks if the destination file already exists
|
|
* @param pUri the source file
|
|
* @param pPath The destination
|
|
* return true if the file already exists
|
|
*/
|
|
private boolean isAlreadyThere(Uri pUri, String pPath) {
|
|
if (pUri.toString().startsWith("file://")) {
|
|
//Old sdcard URI
|
|
return FileTools.fileExists(pUri.buildUpon().appendPath(pPath).build().toString());
|
|
} else {
|
|
//Documents Provider URI
|
|
DocumentFile lDocumentFile = DocumentFile.fromTreeUri(mContext, pUri);
|
|
for (String lDisplayName : pPath.split("/")) {
|
|
if (lDocumentFile.findFile(lDisplayName) != null) {
|
|
lDocumentFile = lDocumentFile.findFile(lDisplayName);
|
|
if ( lDocumentFile.length() == 0 ) {
|
|
if ( !lDocumentFile.isDirectory() ) {
|
|
Logger.getInstance().logInfo("isAlreadyThere", pPath + " File exists, but is 0 bytes in size.");
|
|
}
|
|
}
|
|
} else {
|
|
Logger.getInstance().logInfo("isAlreadyThere", pPath + " does not exist yet.");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copies the music file to a new path and adds the mp3 meta data
|
|
* @param musicTrack Track information
|
|
* @param src The source mp3 file
|
|
* @param dest The destination path
|
|
* return Return if the operation was successful
|
|
*/
|
|
private boolean trackWriteID3(MusicTrack musicTrack, String src, String dest) {
|
|
try {
|
|
// Opens the mp3
|
|
Mp3File mp3File = new Mp3File(src);
|
|
|
|
// Removes all existing tags
|
|
mp3File.removeId3v1Tag();
|
|
mp3File.removeId3v2Tag();
|
|
mp3File.removeCustomTag();
|
|
|
|
// We want to add a fallback ID3v1 tag
|
|
if (mID3EnableFallback) {
|
|
// Create a new tag with ID3v1
|
|
ID3v1Tag tagID3v1 = new ID3v1Tag();
|
|
|
|
// Set all tag values
|
|
tagID3v1.setTrack(musicTrack.getTitle());
|
|
tagID3v1.setArtist(musicTrack.getArtist());
|
|
tagID3v1.setAlbum(musicTrack.getAlbum());
|
|
tagID3v1.setYear(musicTrack.getYear());
|
|
|
|
// Search the genre
|
|
for(int n=0; n<ID3v1Genres.GENRES.length; n++) {
|
|
// Genre found
|
|
if (ID3v1Genres.GENRES[n].equals(musicTrack.getGenre())) {
|
|
tagID3v1.setGenre(n);
|
|
break;
|
|
}
|
|
}
|
|
|
|
mp3File.setId3v1Tag(tagID3v1);
|
|
}
|
|
|
|
// It can't be null
|
|
final ID3v2 tagID3v2;
|
|
|
|
// Creates the requested version
|
|
switch(mID3v2Version) {
|
|
case ID3v22:
|
|
tagID3v2 = new ID3v22Tag();
|
|
break;
|
|
case ID3v23:
|
|
tagID3v2 = new ID3v23Tag();
|
|
break;
|
|
case ID3v24:
|
|
tagID3v2 = new ID3v24Tag();
|
|
break;
|
|
default:
|
|
tagID3v2 = null;
|
|
break;
|
|
}
|
|
|
|
|
|
// Set all tag values
|
|
tagID3v2.setTitle(musicTrack.getTitle());
|
|
tagID3v2.setArtist(musicTrack.getArtist());
|
|
tagID3v2.setAlbum(musicTrack.getAlbum());
|
|
tagID3v2.setAlbumArtist(musicTrack.getAlbumArtist());
|
|
tagID3v2.setTrack("" + musicTrack.getTrackNumber());
|
|
tagID3v2.setPartOfSet("" + musicTrack.getDiscNumber());
|
|
tagID3v2.setYear(musicTrack.getYear());
|
|
|
|
if (!TextUtils.isEmpty(musicTrack.getGenre())) {
|
|
try {
|
|
// Maybe the genre is not supported
|
|
tagID3v2.setGenreDescription(musicTrack.getGenre());
|
|
} catch (IllegalArgumentException e) {
|
|
Logger.getInstance().logWarning("TrackWriteID3", e.getMessage());
|
|
}
|
|
}
|
|
|
|
// Add the artwork to the meta data
|
|
if (mID3EnableArtwork) {
|
|
// Load the artwork
|
|
Bitmap bitmap = ArtworkLoader.loadArtwork(musicTrack, mID3ArtworkMaximumSize);
|
|
|
|
if (bitmap != null) {
|
|
// JPEG is default
|
|
String mimeType = "image/jpeg";
|
|
|
|
// Load the bitmap into a byte array
|
|
ByteArrayOutputStream artworkDataStream = new ByteArrayOutputStream();
|
|
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, artworkDataStream);
|
|
|
|
// Adds the artwork to the meta data
|
|
tagID3v2.setAlbumImage(artworkDataStream.toByteArray(), mimeType);
|
|
}
|
|
}
|
|
|
|
mp3File.setId3v2Tag(tagID3v2);
|
|
|
|
|
|
// Save the file
|
|
mp3File.save(dest);
|
|
|
|
|
|
// Done
|
|
return true;
|
|
} catch (Exception e) {
|
|
Logger.getInstance().logError("TrackWriteId3", e.toString());
|
|
}
|
|
|
|
// Failed
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Encrypts a track and save it to a new path
|
|
* @param musicTrack The music track
|
|
* @param src The source mp3 file
|
|
* @param dest The destination path
|
|
* @return Return if the operation was successful
|
|
*/
|
|
private boolean trackEncrypt(MusicTrack musicTrack, String src, String dest) {
|
|
|
|
try {
|
|
AllAccessExporter allAccessExporter = new AllAccessExporter(src, musicTrack.getCpData());
|
|
|
|
// Checks the magic number
|
|
if (!allAccessExporter.hasValidMagicNumber()) {
|
|
Logger.getInstance().logError("TrackEncrypt", "Invalid magic number! This is not an AllAccess file");
|
|
return false;
|
|
}
|
|
|
|
// Saves the file
|
|
return allAccessExporter.save(dest);
|
|
} catch (Exception e) {
|
|
Logger.getInstance().logError("TrackEncrypt", e.toString());
|
|
}
|
|
|
|
// Failed
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Deletes all cache files
|
|
*/
|
|
private void cleanUp(String theUniqueID) {
|
|
FileTools.fileDelete( getTempPath() + theUniqueID +"_final.mp3");
|
|
FileTools.fileDelete( getTempPath() + theUniqueID +"_tmp.mp3");
|
|
FileTools.fileDelete( getTempPath() + theUniqueID +"_crypt.mp3");
|
|
}
|
|
}
|