added thumbnails support for music cache

This commit is contained in:
Vologhat
2022-09-18 00:35:49 +03:00
parent 593086d3b1
commit 415d3fb3a7
9 changed files with 119 additions and 91 deletions
@@ -1,6 +1,6 @@
package ru.vtosters.lite.downloaders;
import static ru.vtosters.lite.music.cache.FileCacheImplementation.getCacheDir;
import static ru.vtosters.lite.music.cache.FileCacheImplementation.getTrackFolder;
import android.app.Notification;
import android.os.Build;
@@ -15,7 +15,6 @@ import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Playlist;
import java.io.File;
import java.util.ArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
@@ -73,7 +72,9 @@ public class AudioDownloader {
return;
}
var musicPath = cache ? getCacheDir().getAbsolutePath() : Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath();
var musicPath = cache
? getTrackFolder(LibVKXClient.asId(track)).getAbsolutePath()
: Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath();
var tempId = track.d;
var downloadPath = musicPath + File.separator;
var notification = buildDownloadNotification(track, tempId);
@@ -1,61 +0,0 @@
package ru.vtosters.lite.music;
import android.app.Notification;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import com.vk.dto.music.MusicTrack;
import org.json.JSONObject;
import java.io.IOException;
import java.net.URL;
import ru.vtosters.lite.downloaders.notifications.NotificationChannels;
import ru.vtosters.lite.music.cache.FileCacheImplementation;
import ru.vtosters.lite.utils.IOUtils;
public class ArtworkDownloader {
public static void downloadArtworks(MusicTrack track) throws IOException {
var obj = track.J();
if (!obj.has("album")) return;
JSONObject album = obj.optJSONObject("album");
if (!album.has("thumb")) return;
JSONObject thumb = album.optJSONObject("thumb");
var trackId = track.y1();
if (thumb.has("photo_300"))
downloadArtwork(thumb.optString("photo_300"), 300, trackId.hashCode(), trackId);
if (thumb.has("photo_600"))
downloadArtwork(thumb.optString("photo_600"), 600, trackId.hashCode(), trackId);
if (thumb.has("photo_1200"))
downloadArtwork(thumb.optString("photo_1200"), 1200, trackId.hashCode(), trackId);
}
private static void downloadArtwork(String url, int res, int hash, String trackId) throws IOException {
var content = IOUtils.readAllBytes(new URL(url).openStream());
IOUtils.writeToFile(FileCacheImplementation.getTrackArtwork(trackId, res), content);
Log.d("ArtworkDownloader", "Downloading " + url);
//notifyArtwork(AndroidUtils.getGlobalContext(), hash, res);
}
private static void notifyArtwork(Context ctx, int hash, int resolution) {
Notification.Builder builder;
if (Build.VERSION.SDK_INT >= 26) {
builder = new Notification.Builder(ctx, NotificationChannels.MUSIC_DOWNLOAD_CHANNEL_ID);
builder.setVisibility(Notification.VISIBILITY_PUBLIC);
} else builder = new Notification.Builder(ctx);
builder.setSmallIcon(com.vtosters.lite.R.drawable.ic_download_outline_28)
.setShowWhen(false)
.setContentTitle(NotificationChannels.MUSIC_CACHING_NAME)
.setContentText("Разрешение обложки: " + resolution + "x" + resolution);
NotificationChannels.getNotificationManager().notify(hash, builder.build());
}
}
@@ -1,7 +1,5 @@
package ru.vtosters.lite.music;
import android.util.Log;
import com.vk.dto.music.MusicTrack;
import java.io.File;
@@ -16,7 +14,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import javax.crypto.NoSuchPaddingException;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import java8.util.concurrent.CompletableFuture;
import okhttp3.Call;
import okhttp3.OkHttpClient;
@@ -24,6 +21,7 @@ import okhttp3.Request;
import okhttp3.Response;
import ru.vtosters.lite.music.converter.ts.FFMpeg;
import ru.vtosters.lite.music.converter.ts.TSMerger;
import ru.vtosters.lite.music.downloader.ThumbnailDownloader;
import ru.vtosters.lite.music.interfaces.ITrackDownloader;
import ru.vtosters.lite.utils.IOUtils;
@@ -52,6 +50,13 @@ public class M3UDownloader implements ITrackDownloader {
}
private void parse(String payload, File outDir, Callback callback, MusicTrack track, boolean cache) {
if (cache)
try {
ThumbnailDownloader.downloadThumbnails(track);
} catch (IOException e) {
e.printStackTrace();
}
VKM3UParser parser = new VKM3UParser(payload);
List<TransportStream> tses = parser.getTransportStreams();
AtomicInteger progress = new AtomicInteger(0);
@@ -60,8 +65,7 @@ public class M3UDownloader implements ITrackDownloader {
var tsesDir = new File(outDir, String.valueOf(payload.hashCode()));
tsesDir.mkdirs();
var resultTs = new File(tsesDir, "result.ts");
var fileName = IOUtils.getValidFileName((cache ? LibVKXClient.asId(track) : track.toString()) + ".mp3");
var resultMp3 = new File(outDir, fileName);
var resultMp3 = new File(outDir, "track.mp3");
callback.onProgress(5);
for (TransportStream ts : tses) {
@@ -69,7 +73,6 @@ public class M3UDownloader implements ITrackDownloader {
futures.add(CompletableFuture.runAsync(() -> {
try {
// Log.d("M3UDownloader", "Downloading " + tsURL);
InputStream is = IOUtils.openStream(tsURL);
byte[] content;
if (TransportStream.METHOD_AES128.equals(ts.getMethod())) {
@@ -17,7 +17,6 @@ import androidx.annotation.Nullable;
import com.vk.core.util.ToastUtils;
import com.vk.dto.music.MusicTrack;
import com.vk.dto.music.Playlist;
import org.json.JSONObject;
@@ -25,15 +24,10 @@ import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import bruhcollective.itaysonlab.libvkx.ILibVkxService;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClientImpl;
import java8.util.concurrent.CompletableFuture;
import ru.vtosters.lite.music.converter.playlist.PlaylistConverter;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.IOUtils;
public class CacheDatabaseDelegate {
private static final int DB_VERSION = 0x1;
@@ -127,9 +121,9 @@ public class CacheDatabaseDelegate {
@Nullable
private static JSONObject reconstructAlbumFromDb(Cursor cursor, String trackId) {
try {
if (!FileCacheImplementation.getTrackArtwork(trackId, 300).exists()
&& !FileCacheImplementation.getTrackArtwork(trackId, 600).exists()
&& !FileCacheImplementation.getTrackArtwork(trackId, 1200).exists()
if (!FileCacheImplementation.getTrackThumbnail(trackId, 300).exists()
&& !FileCacheImplementation.getTrackThumbnail(trackId, 600).exists()
&& !FileCacheImplementation.getTrackThumbnail(trackId, 1200).exists()
) {
var ownerId = Integer.parseInt(trackId.split("_")[1]);
return new JSONObject()
@@ -151,9 +145,9 @@ public class CacheDatabaseDelegate {
new JSONObject()
.put("height", 600)
.put("width", 600)
.putOpt("photo_300", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackArtwork(trackId, 300)))
.putOpt("photo_600", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackArtwork(trackId, 600)))
.putOpt("photo_1200", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackArtwork(trackId, 1200)));
.putOpt("photo_300", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackThumbnail(trackId, 300)))
.putOpt("photo_600", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackThumbnail(trackId, 600)))
.putOpt("photo_1200", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackThumbnail(trackId, 1200)));
} catch (Exception e) {
e.printStackTrace();
}
@@ -22,11 +22,19 @@ public class FileCacheImplementation {
}
public static File getTrackFolder(String trackId) {
return new File(getCacheDir(), trackId);
var dir = new File(getCacheDir(), trackId);
dir.mkdirs();
return dir;
}
public static File getThumbnailsFolder(String trackId) {
var dir = new File(getTrackFolder(trackId), "thumbs");
dir.mkdirs();
return dir;
}
public static File getTrackFile(String trackId) {
return new File(getCacheDir(), trackId + ".mp3");
return new File(getTrackFolder(trackId), "track.mp3");
}
public static boolean isTrackExist(String trackId) {
@@ -37,8 +45,8 @@ public class FileCacheImplementation {
return getTrackFile(track.y1());
}
public static File getTrackArtwork(String trackId, int factor) {
return new File(getTrackFolder(trackId), "artwork_" + factor + ".png");
public static File getTrackThumbnail(String trackId, int factor) {
return new File(getThumbnailsFolder(trackId), "thumb_" + factor + ".png");
}
public static String getFileUri(File file) {
@@ -52,8 +52,8 @@ public class PlaylistHelper {
if (!photo.isEmpty())
jPhoto.put("height", 600)
.put("width", 600)
.putOpt("photo_300", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackArtwork(trackId, 300)))
.putOpt("photo_600", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackArtwork(trackId, 600)));
.putOpt("photo_300", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackThumbnail(trackId, 300)))
.putOpt("photo_600", FileCacheImplementation.getFileUri(FileCacheImplementation.getTrackThumbnail(trackId, 600)));
var obj = new JSONObject()
.put("id", -2)
.put("owner_id", albumId)
@@ -1,12 +1,20 @@
package ru.vtosters.lite.music.cache.helpers;
import android.util.Log;
import com.vk.dto.music.MusicTrack;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.net.MalformedURLException;
import java.util.List;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.cache.FileCacheImplementation;
public class TracklistHelper {
@@ -33,8 +41,34 @@ public class TracklistHelper {
public static JSONArray tracksToJsons(List<MusicTrack> tracks) {
var arr = new JSONArray();
for (MusicTrack track : tracks) {
arr.put(track.J());
var json = track.J();
var folder = FileCacheImplementation.getThumbnailsFolder(LibVKXClient.asId(track));
try {
addCachedThumbnails(json, folder);
} catch (JSONException | MalformedURLException e) {
Log.d("TracklistHelper", e+"");
}
arr.put(json);
}
return arr;
}
private static void addCachedThumbnails(JSONObject target, File thumbnailsDir)
throws JSONException, MalformedURLException {
var sizes = new JSONArray();
var files = thumbnailsDir.listFiles();
if (files == null || files.length == 0)
return;
for (var file : files) {
var filename = file.getName();
var width = Integer.parseInt(filename.substring(6, filename.length() - 4));
var src = file.toURI().toURL().toString();
Log.d("tracksToJsons", src);
var size = new JSONObject()
.put("width", width)
.put("src", src);
sizes.put(size);
}
target.put("album", new JSONObject().put("thumb", new JSONObject().put("sizes", sizes)));
}
}
@@ -19,7 +19,6 @@ import java.util.UUID;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import io.reactivex.Observable;
import io.reactivex.ObservableOnSubscribe;
import ru.vtosters.lite.music.cache.CacheDatabaseDelegate;
import ru.vtosters.lite.music.cache.helpers.PlaylistHelper;
import ru.vtosters.lite.music.cache.helpers.TracklistHelper;
import ru.vtosters.lite.utils.AccountManagerUtils;
@@ -148,7 +147,8 @@ public class TracklistInjector {
}
}
public static JSONObject createVirtualCatalog(List<MusicTrack> tracks) throws JSONException {
public static JSONObject createVirtualCatalog(List<MusicTrack> tracks)
throws JSONException {
Log.d("TracklistInjector", "createVirtualCatalog");
var clickAction = new JSONObject()
@@ -0,0 +1,49 @@
package ru.vtosters.lite.music.downloader;
import android.util.Log;
import com.vk.dto.music.MusicTrack;
import java.io.IOException;
import java.net.URL;
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
import ru.vtosters.lite.music.cache.FileCacheImplementation;
import ru.vtosters.lite.utils.IOUtils;
public class ThumbnailDownloader {
public static void downloadThumbnails(MusicTrack track)
throws IOException {
var obj = track.J();
var trackId = LibVKXClient.asId(track);
if (!obj.has("album")) return;
var album = obj.optJSONObject("album");
if (!album.has("thumb")) return;
var thumb = album.optJSONObject("thumb");
if (thumb.has("sizes")) {
var sizes = thumb.optJSONArray("sizes");
for (int i = 0; i < sizes.length(); i++) {
var size = sizes.optJSONObject(i);
var width = size.optInt("width");
var src = size.optString("src");
downloadThumbnail(src, width, trackId);
}
} else {
var names = thumb.names();
for (int i = 0; i < names.length(); i++) {
var name = names.optString(i);
downloadThumbnail(thumb.optString(name), Integer.parseInt(name.substring(6)), trackId);
}
}
}
private static void downloadThumbnail(String url, int res, String trackId)
throws IOException {
var data = IOUtils.readAllBytes(new URL(url).openStream());
IOUtils.writeToFile(FileCacheImplementation.getTrackThumbnail(trackId, res), data);
}
}