added thumbnails support for music cache
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
+12
-4
@@ -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)
|
||||
|
||||
+35
-1
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user