Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Manual add song to Mediastore as a music track

I want to create a Music player which can download a song online and add it to MediaStore. I'm using Download Manager and allow MediaScanner scan this file when download completed.

DownloadManager.Request request ....
request.allowScanningByMediaScanner();
...
downloadManager.enqueue(request);

It's work fine in android 5.0 and above.
But the song was downloaded using codec (opus) which not supported in android below lollipop version, so MediaScanner doesn't add this file to MediaStore.

That's my problem, my app can play opus codec but the song didn't exist in MediaStore after it has downloaded, so my app can't find this song.

How to force MediaScanner add downloaded file to MediaStore.Audio as a Music track. If can not, how can I manual add this song to MediaStore.Audio after download completed:

public class BroadcastDownloadComplete extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("android.intent.action.DOWNLOAD_COMPLETE")) {

            //addSongToMediaStore(intent);
        }
    }
}
like image 936
Khang .NT Avatar asked Apr 13 '16 04:04

Khang .NT


2 Answers

From the source code here, we can see the final implementation of the scanner has two steps to scan an audio file. If either of these two step fail, the audio file will not insert into media provider.

step 1 check the file extension

static bool FileHasAcceptableExtension(const char *extension) {
    static const char *kValidExtensions[] = {
        ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2",
        ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
        ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",
        ".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf",
        ".avi", ".mpeg", ".mpg"
    };
    static const size_t kNumValidExtensions =
         sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
    for (size_t i = 0; i < kNumValidExtensions; ++i) {
        if (!strcasecmp(extension, kValidExtensions[i])) {
            return true;
        }
    }
    return false;
}

More extensions have been added since Android 5.0. The common container for opus codec is ogg, this extension exists before Android 5.0. Assume your audio file extension is ogg, the scanning process is fine at this step.

step2 retrieve metadata

After the first step passed, the scanner need to retrieve media's metadata for later database insertion. I think the scanner do the codec level checking at this step.

sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever);
int fd = open(path, O_RDONLY | O_LARGEFILE);
status_t status;
if (fd < 0) {
    // couldn't open it locally, maybe the media server can?
    status = mRetriever->setDataSource(path);
} else {
    status = mRetriever->setDataSource(fd, 0, 0x7ffffffffffffffL);
    close(fd);
}
if (status) {
    return MEDIA_SCAN_RESULT_ERROR;
}

For Android version before 5.0, the scanner might be failed at this step. Because of lacking of built-in opus codec support, setDataSource will get failed at last. The media file won't be added to media provider finally.

suggested solution

Because we know the audio file will be added to

MediaStore.Audio.Media.EXTERNAL_CONTENT_URI

we can do database operation manually. If you want your audio file keeps consistent with other audio files in the database, you have to retrieve all the metadata by yourself. Since you can play the opus file, I think it's easy to retrieve the metadata.

// retrieve more metadata, duration etc.
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Audio.AudioColumns.DATA, "/mnt/sdcard/Music/example.opus");
contentValues.put(MediaStore.Audio.AudioColumns.TITLE, "Example track");
contentValues.put(MediaStore.Audio.AudioColumns.DISPLAY_NAME, "example");
// more columns should be filled from here
Uri uri = getContentResolver().insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, contentValues);
Log.d(TAG, uri.toString());

After that, you app can find the audio file.

getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI...
like image 135
alijandro Avatar answered Nov 18 '22 07:11

alijandro


You can use MediaScannerConnection to ask Android to scan a file to be included as media. You'll want to use the scanFile() static method.

like image 2
Doug Stevenson Avatar answered Nov 18 '22 05:11

Doug Stevenson