From 4534ed11561e7b894afa662e62468ddb9cc309d1 Mon Sep 17 00:00:00 2001 From: gdlbo <41114720+gdlbo@users.noreply.github.com> Date: Mon, 19 Sep 2022 22:14:55 +0300 Subject: [PATCH] caching/downloading own music (without playlists) --- .../lite/downloaders/AudioDownloader.java | 33 ++++++++ .../lite/music/downloader/AudioGet.java | 81 +++++++++++++++++++ .../MusicNotificationBuilder.java | 14 ++++ .../lite/ui/fragments/MediaFragment.java | 30 +++++-- smali/res/xml/preferences_media.xml | 3 + 5 files changed, 156 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/ru/vtosters/lite/music/downloader/AudioGet.java diff --git a/app/src/main/java/ru/vtosters/lite/downloaders/AudioDownloader.java b/app/src/main/java/ru/vtosters/lite/downloaders/AudioDownloader.java index 338c42818..cb072c085 100644 --- a/app/src/main/java/ru/vtosters/lite/downloaders/AudioDownloader.java +++ b/app/src/main/java/ru/vtosters/lite/downloaders/AudioDownloader.java @@ -1,6 +1,7 @@ package ru.vtosters.lite.downloaders; import static ru.vtosters.lite.music.cache.FileCacheImplementation.getTrackFolder; +import static ru.vtosters.lite.utils.AccountManagerUtils.getUserId; import static ru.vtosters.lite.utils.AndroidUtils.getString; import android.os.Environment; @@ -18,6 +19,7 @@ import ru.vtosters.lite.music.cache.CacheDatabaseDelegate; import ru.vtosters.lite.music.cache.FileCacheImplementation; import ru.vtosters.lite.music.callback.MusicCallbackBuilder; import ru.vtosters.lite.music.converter.playlist.PlaylistConverter; +import ru.vtosters.lite.music.downloader.AudioGet; import ru.vtosters.lite.music.downloader.PlaylistDownloader; import ru.vtosters.lite.music.downloader.TrackDownloader; import ru.vtosters.lite.music.notification.MusicNotificationBuilder; @@ -83,6 +85,37 @@ public class AudioDownloader { ); } + public static void cacheAllAudios() { + var tracks = AudioGet.getAudios(); + + var notificationId = getUserId(); + var notification = MusicNotificationBuilder.buildAllAudiosDownloadNotification(notificationId); + + PlaylistDownloader.cachePlaylist( + tracks, + MusicCallbackBuilder.buildPlaylistCallback(tracks.size(), notification, notificationId) + ); + } + + public static void downloadAllAudios() { + var tracks = AudioGet.getAudios(); + + var notificationId = getUserId(); + var notification = MusicNotificationBuilder.buildAllAudiosDownloadNotification(notificationId); + + var playlistName = "Audios of " + getUserId(); + + var musicPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath(); + var downloadPath = musicPath + File.separator + playlistName; + + PlaylistDownloader.downloadPlaylist( + tracks, + playlistName, + downloadPath, + MusicCallbackBuilder.buildPlaylistCallback(tracks.size(), notification, notificationId) + ); + } + private static void downloadM3U8(MusicTrack track, boolean cache) { if (track.D == null) { ToastUtils.a(getString("link_audio_error")); diff --git a/app/src/main/java/ru/vtosters/lite/music/downloader/AudioGet.java b/app/src/main/java/ru/vtosters/lite/music/downloader/AudioGet.java new file mode 100644 index 000000000..e5c2069ae --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/music/downloader/AudioGet.java @@ -0,0 +1,81 @@ +package ru.vtosters.lite.music.downloader; +import static ru.vtosters.lite.hooks.DateHook.getLocale; +import static ru.vtosters.lite.proxy.ProxyUtils.getApi; +import static ru.vtosters.lite.utils.AccountManagerUtils.getUserId; +import static ru.vtosters.lite.utils.AccountManagerUtils.getUserToken; + +import android.util.Log; + +import com.vk.core.network.Network; +import com.vk.core.util.DeviceIdProvider; +import com.vk.dto.music.MusicTrack; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +import java8.util.concurrent.CompletableFuture; +import okhttp3.Headers; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import ru.vtosters.lite.utils.AndroidUtils; + +public class AudioGet{ + public static List getAudios(){ + var tracks = requestAudios(0); + return tracks != null ? tracks.stream().filter(track -> !track.D.isEmpty()).collect(Collectors.toList()) : null; + } + + private static List requestAudios(int offset){ + var audioCount = 1000; + var requestUrl = "https://" + getApi() + "/method/audio.get" + + "?v=5.119" + + "&https=1" + + "&count=" + audioCount + + "&device_id=" + DeviceIdProvider.d(AndroidUtils.getGlobalContext()) + + "&owner_id=" + getUserId() + + "&access_key=" + getUserToken() + + "&lang=" + getLocale() + + "&offset=" + offset + + "&access_token=" + getUserToken(); + + var request = new Request.a() + .b(requestUrl) + .a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a(); + + try { + var response = CompletableFuture.supplyAsync(() -> { + try { + return new OkHttpClient().a(request).execute().a().g(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + ).get(); + var jsonObj = new JSONObject(response); + if (!jsonObj.has("response")) + return null; + Log.d("AudioGet", jsonObj.toString()); + var audios = new JSONObject(response).getJSONObject("response").getJSONArray("items"); + var tracks = new ArrayList(); + for (int i = 0; i < audios.length(); i++) { + var track = new MusicTrack(audios.getJSONObject(i)); + tracks.add(track); + } + + if (tracks.size() == audioCount) { + tracks.addAll(requestAudios(offset + audioCount)); + } + + return tracks; + } catch (JSONException | ExecutionException | InterruptedException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/app/src/main/java/ru/vtosters/lite/music/notification/MusicNotificationBuilder.java b/app/src/main/java/ru/vtosters/lite/music/notification/MusicNotificationBuilder.java index 597db458e..b1bdce16d 100644 --- a/app/src/main/java/ru/vtosters/lite/music/notification/MusicNotificationBuilder.java +++ b/app/src/main/java/ru/vtosters/lite/music/notification/MusicNotificationBuilder.java @@ -11,6 +11,7 @@ import androidx.core.app.NotificationManagerCompat; import com.vk.dto.music.MusicTrack; import ru.vtosters.lite.downloaders.notifications.NotificationChannels; +import ru.vtosters.lite.utils.AccountManagerUtils; import ru.vtosters.lite.utils.AndroidUtils; public class MusicNotificationBuilder { @@ -42,6 +43,19 @@ public class MusicNotificationBuilder { return notificationBuilder; } + public static NotificationCompat.Builder buildAllAudiosDownloadNotification(int id) { + var notificationBuilder = new NotificationCompat.Builder(AndroidUtils.getGlobalContext(), NotificationChannels.MUSIC_PLAYLIST_DOWNLOAD_CHANNEL_ID) + .setSmallIcon(android.R.drawable.stat_sys_download) + .setContentTitle("Скачивание всех аудио") + .setContentText("Все аудио " + AccountManagerUtils.getUsername()) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setCategory(NotificationCompat.CATEGORY_PROGRESS) + .setAutoCancel(true) + .setProgress(100, 0, false); + notificationManager.notify(id, notificationBuilder.build()); + return notificationBuilder; + } + public static void notifySavingToCache(MusicTrack track) { Notification.Builder builder; diff --git a/app/src/main/java/ru/vtosters/lite/ui/fragments/MediaFragment.java b/app/src/main/java/ru/vtosters/lite/ui/fragments/MediaFragment.java index da5d92875..c8c6de939 100644 --- a/app/src/main/java/ru/vtosters/lite/ui/fragments/MediaFragment.java +++ b/app/src/main/java/ru/vtosters/lite/ui/fragments/MediaFragment.java @@ -39,10 +39,13 @@ import org.json.JSONObject; import java.io.IOException; import java.util.Arrays; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import okhttp3.Headers; import okhttp3.OkHttpClient; import okhttp3.Request; +import ru.vtosters.lite.downloaders.AudioDownloader; import ru.vtosters.lite.downloaders.VideoDownloader; import ru.vtosters.lite.music.Scrobbler; import ru.vtosters.lite.music.cache.CacheDatabaseDelegate; @@ -51,11 +54,7 @@ import ru.vtosters.lite.utils.AndroidUtils; import ru.vtosters.lite.utils.LifecycleUtils; public class MediaFragment extends MaterialPreferenceToolbarFragment { - private final int REQUEST_CODE_SET_DOWNLOAD_DIRECTORY = 665; - private final int REQUEST_CODE_SET_MUSIC_DIRECTORY = 666; - private final int REQUEST_CODE_SET_PHOTOS_DIRECTORY = 667; - private final int REQUEST_CODE_SET_VIDEOS_DIRECTORY = 668; - + private static final ExecutorService executor = Executors.newCachedThreadPool(); @Override public void onCreate(Bundle bundle) { @@ -102,6 +101,11 @@ public class MediaFragment extends MaterialPreferenceToolbarFragment { return true; }); + findPreference("audio_download").setOnPreferenceClickListener(preference -> { + dlaudio(requireContext()); + return true; + }); + if (!isVkxInstalled()){ findPreference("vkx_sett").setVisible(false); } @@ -201,6 +205,22 @@ public class MediaFragment extends MaterialPreferenceToolbarFragment { alert.getButton(DialogInterface.BUTTON_NEUTRAL).setTextColor(getResources().getColor(com.vtosters.lite.R.color.red)); } + private void dlaudio(Context ctx) { + VkAlertDialog.Builder alertDialog = new VkAlertDialog.Builder(ctx); + alertDialog.setTitle("Способ скачивания"); + alertDialog.setMessage("Выберите способ скачивания всех своих аудио (без плейлистов)"); + alertDialog.setPositiveButton("В кеш", (dialog, which) -> { + executor.submit(AudioDownloader::cacheAllAudios); + }); + alertDialog.setNegativeButton("Как mp3", (dialog, which) -> { + executor.submit(AudioDownloader::downloadAllAudios); + }); + var alert = alertDialog.create(); + alert.show(); + alert.getButton(DialogInterface.BUTTON_POSITIVE).setTextColor(getAccentColor()); + alert.getButton(DialogInterface.BUTTON_NEGATIVE).setTextColor(getAccentColor());; + } + public static void deleteVideoHistory() { Thread thread = new Thread(() -> { try { diff --git a/smali/res/xml/preferences_media.xml b/smali/res/xml/preferences_media.xml index 8724791b4..2347476ab 100644 --- a/smali/res/xml/preferences_media.xml +++ b/smali/res/xml/preferences_media.xml @@ -51,6 +51,9 @@ +