Best Way to Retrieve MetaData from Mp3s In Android - android

I've been working on a small media player app for Android. I'm having some trouble retrieving the meta data from the music files. I've been using the MediaMetadataRetriever, but it has proved to be quite troublesome. Does anyone know of a better way to go about this? If so how would one implement such method?

I've used JAudioTagger, which you can find here. It supports (basically) every version of ID3, so you're covered even for old files with outdated metadata. Pretty good (and easy) solution. Other options include mp3agic and entagged.

I was able to read the metadata from the Android database, using the code here. Just change the managedquery to something like:
String selection = MediaStore.Audio.Media.DATA + " == ?";
cursor = this.managedQuery(media,
projection,
selection,
new String[] {file.getCanonicalPath().toString()},
null);
This returns a cursor to the metadata of file. (Author, Title, duration etc.)

Related

File path from Uri in Kotlin 2022? [duplicate]

This question already has answers here:
Get path from recorded video
(1 answer)
Android Kotlin: Getting a FileNotFoundException with filename chosen from file picker?
(5 answers)
Closed 10 months ago.
I need to get the file path from my onActivityResult Uri in Kotlin.
When I google for that I get a ton of different ways on how to do it, most of them are not working and a lot seem uneccessary complex, some of them solutions are even 8 years old.
I also found some librarys like PickIt and Simplestorage.
What is currently the best practice (or one of the best practices) to get a file path from Uri?
I'm building a Sound Editor and I need the real file path of the selected Sound File for that.
Storing the sound file as a temp file in my local storage would also be a solution for me, whats the best practice there?
P.S I'm also using Jetpack Compose if that matters
Use MediaStore for the latest versions of Android, that's the recommended practice.
val projection = arrayOf(media-database-columns-to-retrieve)
val selection = sql-where-clause-with-placeholder-variables
val selectionArgs = values-of-placeholder-variables
val sortOrder = sql-order-by-clause
applicationContext.contentResolver.query(
MediaStore.media-type.Media.EXTERNAL_CONTENT_URI,
projection,
selection,
selectionArgs,
sortOrder
)?.use { cursor ->
while (cursor.moveToNext()) {
// Use an ID column from the projection to get
// a URI representing the media item itself.
}
}
This is the basic structure of the API usage, you an read full details here.
Basically, you create a Directory in one of the default Android folders (in your case, it may be the 'Music' Directory), and then you read and write to that specific directory which is designated to your app.
Columns have default values, like name, thumbnail etc. You can get full URIs as well, there are a couple of methods available for that within the API, I think. You can read files using FileDescriptors like so
// Open a specific media item using ParcelFileDescriptor.
val resolver = applicationContext.contentResolver
// "rw" for read-and-write;
// "rwt" for truncating or overwriting existing file contents.
val readOnlyMode = "r"
resolver.openFileDescriptor(content-uri, readOnlyMode).use { pfd ->
// Perform operations on "pfd".
}
Maybe have a look here - How to get the Uri from MediaStore via file path?

How to filter out only relevant Media files in Android?

I'm trying to fetch all the music files in my phone:
For this I'm using:
String[] STAR = {"*"};
Uri allExternalSongUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0";
Cursor cursor = getContentResolver().query(allExternalSongUri, STAR, selection, null, null);
if(cursor != null){
if(cursor.moveToFirst()){
do {
String songName = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME));
Log.i("name", songName);
} while (cursor.moveToNext());
}
cursor.close();
}
But above code, apart from getting music files, is also fetching some additional unnecessary files like *sound_screen_on.mp3* (which is installed & used by some other app).
Issue is my native android music player does not list & plays these unnecessary files.
How can I filter files like these.
You can filter using MediaStore.Audio.AudioColumns class.That will return lots of value of Audio file and you can use it.
musiccursor = managedQuery(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
proj, MediaStore.Audio.Media.DURATION + ">= 60000", null, null);
Filter by Duration - The duration of the audio file, in ms
Filter by Type-Music -Non-zero if the audio file is music
Filter by Type-RingTone - Non-zero if the audio file may be a ringtone
Filter by Type-Alarm - Non-zero if the audio file may be an alarm
Check for more reference
Aside from the above methods suggested by others, in the case where column is not reliable from the db, you can always use MediaMetadataRetriever to get the information you want after the first filtering from the db.
http://developer.android.com/reference/android/media/MediaMetadataRetriever.html
This is of course slower than getting existing information from the media db, but it will give you author/album/duration etc other information and you can do as custom filter as you can get with the file list that you get from the ContentProvider.
Using your same code, i dont get any undesired file.
Usually that files are marked to be ignored, so probably the media scanner did something wrong or the index is not updated in your system.
Sounds, images and other media files that are meant to be used by one app or by the system, are marked to be ignored simply by adding a file called ".nomedia" in the same directory. As you can read in the docs of the MediaStore provider
public static final String MEDIA_IGNORE_FILENAME
Added in API level 9 Name of the file signaling the media scanner to
ignore media in the containing directory and its subdirectories.
Developers should use this to avoid application graphics showing up in
the Gallery and likewise prevent application sounds and music from
showing up in the Music app.
Constant Value: ".nomedia"
So, besides of the advices from chintan khetiya, that are great, probably you can consider that there are some files marked to be ignored, that are not actually being ignored, and check it by yourself.
Simply, when you iterating the cursor, for every file, just check if in the same directory there is a file called like the value of MediaStore.MEDIA_IGNORE_FILENAME or the hardcoded .nomedia
only get the music which the _data is more than a certain value, for example 300kb.

How can I play sounds that are stored in assets or raw resource folder if the specific file to play depends on an SQLite Query?

I am finding a way how to play sounds in my resource folder (res or assets, in whichever appropriate way to store) but the file that will be loaded to the soundpool or mediaplayer depends on an SQLite query.
I am currently making a translator app that displays word or sentence translations via search engine (just noting that this app is just an search engine, not really a translator like Google Translate). and I need a 'pronounce' button that will play how to pronounce the searched word to its dialect, and this file (it was pre-recorded) will be stored in a resource folder. It's like, when the user searched the word "apple", and the button was clicked, it will play the "apple.mp3" in the resource folder.
I'm just new to Android programming and I search from the web and found answers about soundpools and mediaplayer but I think I hadn't seen any like this before.
According to my comment:
Create one more column in your database table
Put your references into this column (i.e. R.raw.my_sound.mp3) (!remember reference is always int!)
When you query get value from this column and use it like this ( I didn't test it. It's more like pseudo code ;) ):
Cursor cursor = database.query(DATABASE_TABLE,
new String[] { KEY_REFERENCES }, WORD + " = " + insertedWord, null, null, null, null);
if (cursor.moveToFirst()){
int reference = cursor.getInt(referenceColumnIndex);
MediaPlayer mp = MediaPlayer.create(YourActivityClass.this, reference);
if(mp != null) {
mp.start();
}
}
I think it should work.

Android - Query MediaStore with known (potentially incorrect) filepath?

I'm trying to include a folder browser element in my app, which upon clicking an audio file, locates that file in the MediaStore and returns it's _ID.
One of the issues I'm having is that for some devices, it seems the absolute file path works fine, but for other devices, I need the canonical paths. In some cases, it doesn't work at all!
String canonicalPath = file.getCanonicalPath();
String absolutePath = file.getAbsolutePath();
For what it's worth, the 'file' is created from the root "/", not from getExternalStorageDirectory() (because some Audio files may be located on the internal sd or elsewhere).
So the query looks like this:
Cursor cursor = getActivity().getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, cols, MediaStore.MediaColumns.DATA + "= ? ", new String[]{absolutePath}, null);
If it fails, I do the same query on the canonicalPath, and see if that works. The issue is that it fails in both cases on a lot of different devices, depending on the file structure.
On my particular device (Galaxy S2), the difference between the absolute and canonical paths is usually as follows:
/extSdCard/Music/myFile.mp3
/storage/sdcard1/Music/myFile.mp3
Am I wrong to assume the file will always have an ID in the MediaStore? Is there a better way to find the 'actual' path to the file, or a better way to construct the query to get the id of the file? It's crashing all over the place on various devices, due to the query not returning and cursor items (in other words, it can't find anywhere where the MediaColumns.DATA is equal to the absolute or canoncial path passed in.
I guess I'm just looking for a way to have a more failproof query.. A better way to find the path or the id of the audio file. Any ideas would be appreciated.

Passing Parameters via URI in Android

Is it possible (or recommended) to pass parameters to content providers via URIs in Android, similar to how web addresses use them? That is to say, can I use name/value pairs in content:// URIs?
For example, I have a search provider that can search based on names. I pass it a URI like this:
content://com.example.app/name/john
That would return anyone with "john" in their names, including John, Johnathon, Johnson, etc.
I want to have the option (but not requirement) to search by exact names and not find partial matches. I was thinking of doing something like this:
content://com.example.app/name/john?exact=true
That would tell the search provider to only return names that exactly match "John." But I haven't seen any other examples of parameters used like this within Android. Is there a better way? What am I missing here?
Thanks!
if you want to pass query parameters you can use the appendQueryParameter() method when constructing your URI
LoaderFactory.createCursorLoader(this,
MyProvider.MY_CONTENT_URI.buildUpon().appendQueryParameter(
MyProvider.MY_PARAM_NAME,
myParamValue).build(), projection, null,
null, null);
And then access the param value using the getQueryParameter()
String paramValue = uri.getQueryParameter(MyProvider.MY_PARAM_NAME);
No, as far as I have seen, any parameters get stripped from content provider urls (although I don't know why), I worked around this by adding parameters using a "/" and a certain prefix and parse them out manually, something like this:
content://com.example.app/name/john/paramexact/true

Categories

Resources