From d55361fd72ca81392b0bb33168bf553a3d223c08 Mon Sep 17 00:00:00 2001 From: Vologhat <47892605+Vologhat@users.noreply.github.com> Date: Sun, 24 Jul 2022 02:17:20 +0300 Subject: [PATCH] added new way of music downloading --- .../lite/downloaders/AudioDownloader.java | 34 +++++--- .../ru/vtosters/lite/music/MP3Downloader.java | 86 +++++++++++++++++++ .../ru/vtosters/lite/music/VKM3UParser.java | 6 +- .../ui/activities/VKAdminTokenActivity.java | 61 +++++++++++++ .../lite/ui/fragments/OtherFragment.java | 81 +++++++++++------ smali/res/xml/preferences_debug.xml | 4 + smali/res/xml/preferences_other.xml | 3 + 7 files changed, 239 insertions(+), 36 deletions(-) create mode 100644 app/src/main/java/ru/vtosters/lite/music/MP3Downloader.java create mode 100644 app/src/main/java/ru/vtosters/lite/ui/activities/VKAdminTokenActivity.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 0f49ed0bd..237f51c20 100644 --- a/app/src/main/java/ru/vtosters/lite/downloaders/AudioDownloader.java +++ b/app/src/main/java/ru/vtosters/lite/downloaders/AudioDownloader.java @@ -15,30 +15,40 @@ import ru.vtosters.lite.downloaders.notifications.NotificationChannels; import ru.vtosters.lite.music.Callback; import ru.vtosters.lite.music.FFMpeg; import ru.vtosters.lite.music.M3UDownloader; +import ru.vtosters.lite.music.MP3Downloader; import ru.vtosters.lite.utils.Globals; -public class AudioDownloader{ +public class AudioDownloader { private static NotificationManagerCompat notificationManager = NotificationManagerCompat.from(Globals.getContext()); - public static void downloadAudio(MusicTrack track){ + public static void downloadAudio(MusicTrack track) { + if (Globals.getDefprefs().getBoolean("new_music_downloading_way", false)) { + downloadMP3(track); + } else { + downloadM3U8(track); + } + } + + public static void downloadM3U8(MusicTrack track) { if (track.D == null) { ToastUtils.a("Не удалось найти ссылку на аудиозапись"); return; } + var musicPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath(); var tempId = track.d; var downloadPath = musicPath + File.separator + tempId; - var notification = buildDownloadNotification(track, tempId); - downloadM3U8(track, downloadPath, new Callback(){ + var notification = buildDownloadNotification(track, tempId); + downloadM3U8(track, downloadPath, new Callback() { @Override - public void onProgress(int progress){ + public void onProgress(int progress) { notification.setProgress(100, progress, false); notificationManager.notify(tempId, notification.build()); } @Override - public void onSuccess(){ + public void onSuccess() { try { var fileName = track.toString(); var success = FFMpeg.convert(downloadPath, musicPath + File.separator + fileName + ".mp3"); @@ -59,19 +69,19 @@ public class AudioDownloader{ } @Override - public void onFailure(){ + public void onFailure() { notification.setContentText("Не удалось загрузить аудиозапись").setProgress(0, 0, false); notificationManager.notify(tempId, notification.build()); } @Override - public void onSizeReceived(long size, long header){ + public void onSizeReceived(long size, long header) { } }); } - private static void downloadM3U8(MusicTrack track, String path, Callback callback){ + private static void downloadM3U8(MusicTrack track, String path, Callback callback) { File outDir = new File(path); if (!outDir.exists()) if (outDir.mkdir()) @@ -82,7 +92,11 @@ public class AudioDownloader{ new M3UDownloader(track.D, outDir, callback).execute(); } - private static NotificationCompat.Builder buildDownloadNotification(MusicTrack track, int id){ + private static void downloadMP3(MusicTrack track) { + new MP3Downloader().execute(track); + } + + private static NotificationCompat.Builder buildDownloadNotification(MusicTrack track, int id) { var notificationBuilder = new NotificationCompat.Builder(Globals.getContext(), NotificationChannels.MUSIC_DOWNLOAD_CHANNEL_ID) .setSmallIcon(android.R.drawable.stat_sys_download) .setContentTitle("Загрузка аудиозаписи") diff --git a/app/src/main/java/ru/vtosters/lite/music/MP3Downloader.java b/app/src/main/java/ru/vtosters/lite/music/MP3Downloader.java new file mode 100644 index 000000000..2cf709251 --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/music/MP3Downloader.java @@ -0,0 +1,86 @@ +package ru.vtosters.lite.music; + +import android.app.DownloadManager; +import android.net.Uri; +import android.os.Environment; +import android.util.Log; +import android.widget.Toast; + +import com.vk.dto.music.MusicTrack; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import ru.vtosters.lite.downloaders.AudioDownloader; +import ru.vtosters.lite.utils.Globals; + +public class MP3Downloader { + + private static final String URL_FORMAT = "https://api.vk.com/method/audio.getById?v=5.131&access_token=%s&audios=%s©right=https://vtosters.app/"; + private static final OkHttpClient client = new OkHttpClient(); + + public MP3Downloader() { + } + + public void execute(MusicTrack track) { + var token = Globals.getPreferences().getString("vk_admin_token", ""); + if (token.isEmpty()) { + Toast.makeText(Globals.getContext(), "Обновите токен VK Admin в настройках", Toast.LENGTH_SHORT).show(); + return; + } + var request = new Request.a() + .b(String.format(URL_FORMAT, token, track.y1())) + .a(); + client.a(request).a(new Callback() { + @Override + public void a(Call call, IOException e) { + Log.d("MP3Downloader", e+""); + } + + @Override + public void a(Call call, Response response) throws IOException { + var payload = response.a().g(); + Log.d("MP3Downloader", payload); + try { + JSONObject jsonObj = new JSONObject(payload); + if (jsonObj.has("response")) { + JSONObject responseNode = new JSONObject(payload).getJSONArray("response") + .getJSONObject(0); + if (responseNode.has("url")) { + var url = responseNode.optString("url"); + Log.d("MP3Downloader", url); + if (url != null) { + if (url.endsWith(".mp3")) { + downloadMP3(track.toString(), url); + } else if (VKM3UParser.isM3U8(url)) { + AudioDownloader.downloadM3U8(track); + } + } + } + } + } catch (JSONException e) { + Log.d("MP3Downloader", e+""); + } + } + }); + } + + private void downloadMP3(String title, String url) { + var downloadManager = Globals.getContext().getSystemService(DownloadManager.class); + var request = new DownloadManager.Request(Uri.parse(url)) + .setAllowedOverRoaming(true) + .setTitle("Downloading " + title) + .setDestinationInExternalFilesDir(Globals.getContext(), + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(), + title + ".mp3") + .setVisibleInDownloadsUi(true); + downloadManager.enqueue(request); + } +} diff --git a/app/src/main/java/ru/vtosters/lite/music/VKM3UParser.java b/app/src/main/java/ru/vtosters/lite/music/VKM3UParser.java index b9e8c9247..9a6d4e93d 100644 --- a/app/src/main/java/ru/vtosters/lite/music/VKM3UParser.java +++ b/app/src/main/java/ru/vtosters/lite/music/VKM3UParser.java @@ -40,7 +40,7 @@ public class VKM3UParser{ } else if (line.startsWith("#EXTINF")) { if (mTransportStreams.size() == i) mTransportStreams.add(new TransportStream()); - } else if (line.endsWith(".ts") || line.endsWith(".tp") || line.endsWith(".mpeg-ts") || line.endsWith(".m2ts")) { + } else if (isM3U8(line)) { mTransportStreams.get(i).setName(line); ++i; } @@ -51,6 +51,10 @@ public class VKM3UParser{ return directive.substring(directive.indexOf(":") + 1); } + public static boolean isM3U8(String str) { + return str.endsWith(".ts") || str.endsWith(".tp") || str.endsWith(".mpeg-ts") || str.endsWith(".m2ts"); + } + public int getHeapSize(){ return mHeapSize; } diff --git a/app/src/main/java/ru/vtosters/lite/ui/activities/VKAdminTokenActivity.java b/app/src/main/java/ru/vtosters/lite/ui/activities/VKAdminTokenActivity.java new file mode 100644 index 000000000..084f5416d --- /dev/null +++ b/app/src/main/java/ru/vtosters/lite/ui/activities/VKAdminTokenActivity.java @@ -0,0 +1,61 @@ +package ru.vtosters.lite.ui.activities; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.webkit.WebResourceRequest; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.FrameLayout; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.Map; + +import ru.vtosters.lite.utils.LayoutHelper; + +public class VKAdminTokenActivity extends AppCompatActivity { + + private static final String URL = "https://oauth.vk.com/authorize?client_id=6121396&scope=1073737727&redirect_uri=https://oauth.vk.com/blank.html&display=page&response_type=token&revoke=1"; + + @Override + protected void onCreate(@Nullable Bundle bundle) { + super.onCreate(bundle); + + var container = new FrameLayout(this); + container.setLayoutParams(LayoutHelper.createFrame(-1, -1)); + setContentView(container); + + var webView = new WebView(this); + container.addView(webView, LayoutHelper.createFrame(-1, -1)); + + webView.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + var url = request.getUrl().toString(); + if (url.startsWith("https://oauth.vk.com/auth_redirect?")) { + var redirectUri = Uri.parse(URLDecoder.decode(url)); + var authorizeUrl = redirectUri.getQueryParameter("authorize_url"); + var accessToken = authorizeUrl.substring(authorizeUrl.indexOf("=") + 1, authorizeUrl.indexOf("&")); + + var data = new Intent(); + data.putExtra("token", accessToken); + setResult(Activity.RESULT_OK, data); + finish(); + + return false; + } + return super.shouldOverrideUrlLoading(view, request); + } + }); + + Map headers = new HashMap<>(); + headers.put("User-Agent", System.getProperty("http.agent")); + + webView.loadUrl(URL, headers); + } +} diff --git a/app/src/main/java/ru/vtosters/lite/ui/fragments/OtherFragment.java b/app/src/main/java/ru/vtosters/lite/ui/fragments/OtherFragment.java index 821bdd728..4e769e7ad 100644 --- a/app/src/main/java/ru/vtosters/lite/ui/fragments/OtherFragment.java +++ b/app/src/main/java/ru/vtosters/lite/ui/fragments/OtherFragment.java @@ -13,11 +13,14 @@ import static ru.vtosters.lite.utils.SettBackup.deletePrefs; import static ru.vtosters.lite.utils.SettBackup.restoreBackup; import static ru.vtosters.lite.utils.Themes.getAlertStyle; +import android.app.Activity; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; +import android.util.Log; import android.webkit.WebView; import android.widget.ArrayAdapter; import android.widget.Toast; @@ -38,19 +41,36 @@ import com.vtosters.lite.im.ImEngineProvider; import java.io.IOException; import b.h.g.m.FileUtils; +import ru.vtosters.lite.ui.activities.VKAdminTokenActivity; import ru.vtosters.lite.utils.Globals; import ru.vtosters.lite.utils.SettBackup; -public class OtherFragment extends MaterialPreferenceToolbarFragment{ +public class OtherFragment extends MaterialPreferenceToolbarFragment { + + private static final int VK_ADMIN_TOKEN_REQUEST_CODE = 1; @Override - public void onCreate(Bundle bundle){ + public void onCreate(Bundle bundle) { super.onCreate(bundle); addPreferencesFromResource(getIdentifier("preferences_other", "xml")); prefs(); } - private void prefs(){ + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == VK_ADMIN_TOKEN_REQUEST_CODE) { + if (resultCode == Activity.RESULT_OK) { + String token = data.getStringExtra("token"); + Globals.getPreferences().edit().putString("vk_admin_token", token).apply(); + Log.d("VkAdminToken", token); + Toast.makeText(getContext(), "Токен успешно сохранён", LENGTH_SHORT).show(); + } + } + } + + private void prefs() { findPreference("firebasefix").setOnPreferenceClickListener(new a()); findPreference("applicationstop").setOnPreferenceClickListener(new b()); findPreference("tokencopy").setOnPreferenceClickListener(new c()); @@ -62,19 +82,30 @@ public class OtherFragment extends MaterialPreferenceToolbarFragment{ findPreference("saveprefs").setOnPreferenceClickListener(new saveprefs()); findPreference("restoreprefs").setOnPreferenceClickListener(new restoreprefs()); + + var vkAdminTokenPref = findPreference("vk_admin_token"); + vkAdminTokenPref.setVisible(Globals.getDefprefs().getBoolean("new_music_downloading_way", false)); + if (vkAdminTokenPref.isVisible()) { + vkAdminTokenPref.setOnPreferenceClickListener(preference -> { + + startActivityForResult(new Intent(getContext(), VKAdminTokenActivity.class), VK_ADMIN_TOKEN_REQUEST_CODE); + + return true; + }); + } } - public static class d implements Preference.OnPreferenceClickListener{ + public static class d implements Preference.OnPreferenceClickListener { @Override // android.support.v7.preference.Preference.c - public boolean onPreferenceClick(Preference preference){ + public boolean onPreferenceClick(Preference preference) { restartApplication(); return true; } } - public class onlines implements Preference.OnPreferenceClickListener{ + public class onlines implements Preference.OnPreferenceClickListener { @Override // android.support.v7.preference.Preference.c - public boolean onPreferenceClick(Preference preference){ + public boolean onPreferenceClick(Preference preference) { try { backupOnlines(); } catch (IOException e) { @@ -84,40 +115,40 @@ public class OtherFragment extends MaterialPreferenceToolbarFragment{ } } - public static class b implements Preference.OnPreferenceClickListener{ + public static class b implements Preference.OnPreferenceClickListener { @Override // android.support.v7.preference.Preference.c - public boolean onPreferenceClick(Preference preference){ + public boolean onPreferenceClick(Preference preference) { System.exit(0); return true; } } - public static class deleteprefs implements Preference.OnPreferenceClickListener{ + public static class deleteprefs implements Preference.OnPreferenceClickListener { @Override // android.support.v7.preference.Preference.c - public boolean onPreferenceClick(Preference preference){ + public boolean onPreferenceClick(Preference preference) { deletePrefs(); restartApplication(); return true; } } - class c implements Preference.OnPreferenceClickListener{ - public void copy(Context context, String str){ + class c implements Preference.OnPreferenceClickListener { + public void copy(Context context, String str) { ((ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("MBH-ST", str)); makeText(context, Globals.getString("copybtn"), LENGTH_SHORT).show(); ToastUtils.a(Globals.getString("tokenwarning")); } @Override // android.support.v7.preference.Preference.c - public boolean onPreferenceClick(Preference preference){ + public boolean onPreferenceClick(Preference preference) { copy(getContext(), getUserToken()); return true; } } - public class e implements Preference.OnPreferenceClickListener{ + public class e implements Preference.OnPreferenceClickListener { @Override // android.support.v7.preference.Preference.c - public boolean onPreferenceClick(Preference preference){ + public boolean onPreferenceClick(Preference preference) { SharedPreferences prefs2 = getContext().getSharedPreferences("stickers_storage", Context.MODE_PRIVATE); prefs2.edit().clear().commit(); new WebView(getActivity()).clearCache(true); @@ -132,9 +163,9 @@ public class OtherFragment extends MaterialPreferenceToolbarFragment{ } } - public class a implements Preference.OnPreferenceClickListener{ + public class a implements Preference.OnPreferenceClickListener { @Override // android.support.v7.preference.Preference.c - public boolean onPreferenceClick(Preference preference){ + public boolean onPreferenceClick(Preference preference) { VKAccount b = VKAccountManager.d(); PushSubscriber.e.a(); ToastUtils.a(Globals.getString("fcmtokenrem")); @@ -148,9 +179,9 @@ public class OtherFragment extends MaterialPreferenceToolbarFragment{ } } - public class f implements Preference.OnPreferenceClickListener{ + public class f implements Preference.OnPreferenceClickListener { @Override // android.support.v7.preference.Preference.c - public boolean onPreferenceClick(Preference preference){ + public boolean onPreferenceClick(Preference preference) { SharedPreferences prefs2 = getContext().getSharedPreferences("stickers_storage", Context.MODE_PRIVATE); prefs2.edit().clear().commit(); deleteCache(); @@ -159,20 +190,20 @@ public class OtherFragment extends MaterialPreferenceToolbarFragment{ } } - public class saveprefs implements Preference.OnPreferenceClickListener{ - saveprefs(){ + public class saveprefs implements Preference.OnPreferenceClickListener { + saveprefs() { } @Override // android.support.v7.preference.Preference.c - public boolean onPreferenceClick(Preference preference){ + public boolean onPreferenceClick(Preference preference) { backupSettings(); return true; } } - public class restoreprefs implements Preference.OnPreferenceClickListener{ + public class restoreprefs implements Preference.OnPreferenceClickListener { @Override // android.support.v7.preference.Preference.c - public boolean onPreferenceClick(Preference preference){ + public boolean onPreferenceClick(Preference preference) { var arr = SettBackup.getBackupsNames(); var adapter = new ArrayAdapter(getContext(), android.R.layout.simple_list_item_1, arr); new AlertDialog.Builder(getContext(), getAlertStyle()) diff --git a/smali/res/xml/preferences_debug.xml b/smali/res/xml/preferences_debug.xml index 1b1c6befa..698427e59 100644 --- a/smali/res/xml/preferences_debug.xml +++ b/smali/res/xml/preferences_debug.xml @@ -60,6 +60,10 @@ android:defaultValue="true" android:key="musicFixNew" android:title="Фикс музыки v2" /> + +