diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml index d1fac0cd..2375cff6 100644 --- a/.idea/assetWizardSettings.xml +++ b/.idea/assetWizardSettings.xml @@ -320,7 +320,7 @@ @@ -331,7 +331,7 @@ diff --git a/.idea/misc.xml b/.idea/misc.xml index be2898d8..d739b9ab 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -37,6 +37,7 @@ + @@ -45,6 +46,7 @@ + @@ -70,6 +72,7 @@ + @@ -96,7 +99,7 @@ - + diff --git a/.idea/navEditor.xml b/.idea/navEditor.xml new file mode 100644 index 00000000..8ab47ce5 --- /dev/null +++ b/.idea/navEditor.xml @@ -0,0 +1,61 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 7cea4b05..960b5f4c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -68,6 +68,8 @@ dependencies { implementation 'androidx.core:core:1.8.0' implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.preference:preference:1.2.0' + implementation 'androidx.navigation:navigation-fragment:2.5.2' + implementation 'androidx.navigation:navigation-ui:2.5.2' testImplementation "junit:junit:$junitVer" androidTestImplementation "androidx.test.ext:junit:$androidJunitVer" androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVer" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 44deca19..bc7b1bbb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,6 +49,10 @@ android:name=".DownloaderService" android:enabled="true" android:exported="false" /> + \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.java b/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.java index 7a9622db..e93e94c3 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.java +++ b/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.java @@ -1,10 +1,12 @@ package com.deniscerri.ytdlnis; +import android.app.Activity; import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.SharedPreferences; import android.os.Bundle; import android.os.IBinder; import android.util.Log; @@ -13,7 +15,6 @@ import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; - import com.deniscerri.ytdlnis.database.Video; import com.deniscerri.ytdlnis.databinding.ActivityMainBinding; import com.deniscerri.ytdlnis.page.HistoryFragment; @@ -99,11 +100,11 @@ public class MainActivity extends AppCompatActivity{ this.setTitle(R.string.app_name);; } replaceFragment(homeFragment); - }else if(id == R.id.history){ + }else if(id == R.id.downloads){ if(lastFragment == historyFragment){ historyFragment.scrollToTop(); }else { - this.setTitle(getString(R.string.history)); + this.setTitle(getString(R.string.downloads)); } replaceFragment(historyFragment); }else if(id == R.id.more){ @@ -215,8 +216,11 @@ public class MainActivity extends AppCompatActivity{ } private void checkUpdate(){ - UpdateUtil updateUtil = new UpdateUtil(this); - updateUtil.updateApp(); + SharedPreferences preferences = context.getSharedPreferences("root_preferences", Activity.MODE_PRIVATE); + if(preferences.getBoolean("update_app", false)){ + UpdateUtil updateUtil = new UpdateUtil(this); + updateUtil.updateApp(); + } } public void updateHistoryFragment(){ diff --git a/app/src/main/java/com/deniscerri/ytdlnis/adapter/HistoryRecyclerViewAdapter.java b/app/src/main/java/com/deniscerri/ytdlnis/adapter/HistoryRecyclerViewAdapter.java index 3e9267d6..9ded68fb 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/adapter/HistoryRecyclerViewAdapter.java +++ b/app/src/main/java/com/deniscerri/ytdlnis/adapter/HistoryRecyclerViewAdapter.java @@ -2,6 +2,8 @@ package com.deniscerri.ytdlnis.adapter; import android.content.Context; import android.graphics.Color; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; import android.os.Handler; import android.os.Looper; import android.view.LayoutInflater; @@ -21,6 +23,7 @@ import com.deniscerri.ytdlnis.R; import com.deniscerri.ytdlnis.database.Video; import com.squareup.picasso.Picasso; +import java.io.File; import java.util.ArrayList; public class HistoryRecyclerViewAdapter extends RecyclerView.Adapter { @@ -97,6 +100,12 @@ public class HistoryRecyclerViewAdapter extends RecyclerView.Adapter onItemClickListener.onCardClick(position)); } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/page/HomeFragment.java b/app/src/main/java/com/deniscerri/ytdlnis/page/HomeFragment.java index 4b04d397..f3ac6164 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/page/HomeFragment.java +++ b/app/src/main/java/com/deniscerri/ytdlnis/page/HomeFragment.java @@ -110,10 +110,13 @@ public class HomeFragment extends Fragment implements HomeRecyclerViewAdapter.On public void onDownloadProgress(DownloadInfo info) { activity.runOnUiThread(() -> { try{ + int progress = info.getProgress(); progressBar = fragmentView.findViewWithTag(info.getVideo().getVideoId()+"##progress"); progressBar.setVisibility(View.VISIBLE); - progressBar.setIndeterminate(false); - progressBar.setProgress(info.getProgress()); + if (progress > 1){ + progressBar.setIndeterminate(false); + progressBar.setProgress(info.getProgress()); + } }catch(Exception ignored){} }); } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/page/MoreFragment.java b/app/src/main/java/com/deniscerri/ytdlnis/page/MoreFragment.java index 4a527319..38bf1c6c 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/page/MoreFragment.java +++ b/app/src/main/java/com/deniscerri/ytdlnis/page/MoreFragment.java @@ -1,40 +1,13 @@ package com.deniscerri.ytdlnis.page; import android.os.Bundle; -import android.widget.Toast; - -import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; - import com.deniscerri.ytdlnis.R; -import com.deniscerri.ytdlnis.util.UpdateUtil; public class MoreFragment extends PreferenceFragmentCompat { - Preference updateYTDL; - Preference updateApp; public static final String TAG = "MoreFragment"; - private UpdateUtil updateUtil; - - @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { setPreferencesFromResource(R.xml.more_preferences, rootKey); - - updateYTDL = findPreference("update_ytdl"); - updateApp = findPreference("update_app"); - - updateUtil = new UpdateUtil(getContext()); - - updateYTDL.setOnPreferenceClickListener(preference -> { - updateUtil.updateYoutubeDL(); - return true; - }); - - updateApp.setOnPreferenceClickListener(preference -> { - if(!updateUtil.updateApp()){ - Toast.makeText(getContext(), R.string.you_are_in_latest_version, Toast.LENGTH_SHORT).show(); - } - return true; - }); } } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/page/settings/SettingsFragment.java b/app/src/main/java/com/deniscerri/ytdlnis/page/settings/SettingsFragment.java index 60820d9d..bd9213b0 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/page/settings/SettingsFragment.java +++ b/app/src/main/java/com/deniscerri/ytdlnis/page/settings/SettingsFragment.java @@ -43,6 +43,8 @@ public class SettingsFragment extends PreferenceFragmentCompat { ListPreference videoFormat; SeekBarPreference audioQuality; + Preference updateYTDL; + SwitchPreferenceCompat updateApp; Preference version; private UpdateUtil updateUtil; @@ -58,7 +60,6 @@ public class SettingsFragment extends PreferenceFragmentCompat { setPreferencesFromResource(R.xml.root_preferences, rootKey); updateUtil = new UpdateUtil(requireContext()); - initPreferences(); initListeners(); } @@ -85,6 +86,8 @@ public class SettingsFragment extends PreferenceFragmentCompat { videoFormat = findPreference("video_format"); audioQuality = findPreference("audio_quality"); + updateYTDL = findPreference("update_ytdl"); + updateApp = findPreference("update_app"); version = findPreference("version"); version.setSummary(BuildConfig.VERSION_NAME); @@ -110,6 +113,7 @@ public class SettingsFragment extends PreferenceFragmentCompat { editor.putString("audio_format", audioFormat.getValue()); editor.putString("video_format", videoFormat.getValue()); editor.putInt("audio_quality", audioQuality.getValue()); + editor.putBoolean("update_app", updateApp.isChecked()); editor.apply(); } @@ -222,6 +226,18 @@ public class SettingsFragment extends PreferenceFragmentCompat { return true; }); + updateYTDL.setOnPreferenceClickListener(preference -> { + updateUtil.updateYoutubeDL(); + return true; + }); + + updateApp.setOnPreferenceChangeListener((preference, newValue) -> { + boolean enable = (Boolean) newValue; + editor.putBoolean("update_app", enable); + editor.apply(); + return true; + }); + version.setOnPreferenceClickListener(preference -> { if(!updateUtil.updateApp()){ Toast.makeText(getContext(), R.string.you_are_in_latest_version, Toast.LENGTH_SHORT).show(); diff --git a/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.java b/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.java index c05dec88..e996f5b3 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.java +++ b/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.java @@ -4,6 +4,7 @@ import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.text.Html; import android.util.Log; import android.widget.Toast; @@ -35,6 +36,9 @@ public class InfoUtil { try{ SharedPreferences sharedPreferences = context.getSharedPreferences("root_preferences", Activity.MODE_PRIVATE); key = sharedPreferences.getString("api_key", ""); + applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); + if (key.isEmpty()) key = applicationInfo.metaData.getString("ytAPIkey"); + Log.e(TAG, key); dbManager = new DBManager(context); Thread thread = new Thread(() -> { @@ -60,6 +64,7 @@ public class InfoUtil { videos = new ArrayList<>(); //short data JSONObject res = genericRequest("https://youtube.googleapis.com/youtube/v3/search?part=snippet&q="+query+"&maxResults=25®ionCode="+countryCODE+"&key="+key); + if (!res.has("items")) return getFromYTDL(query); JSONArray dataArray = res.getJSONArray("items"); //extra data @@ -110,6 +115,7 @@ public class InfoUtil { String url = "https://youtube.googleapis.com/youtube/v3/playlistItems?part=snippet&pageToken="+nextPageToken+"&maxResults=50®ionCode="+countryCODE+"&playlistId="+id+"&key="+key; //short data JSONObject res = genericRequest(url); + if (!res.has("items")) return new PlaylistTuple("", getFromYTDL("https://www.youtube.com/playlist?list="+id)); JSONArray dataArray = res.getJSONArray("items"); //extra data @@ -155,6 +161,7 @@ public class InfoUtil { //short data JSONObject res = genericRequest("https://youtube.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id="+id+"&key="+key); + if (!res.has("items")) return getFromYTDL("https://www.youtube.com/watch?v="+id).get(0); String duration = res.getJSONArray("items").getJSONObject(0).getJSONObject("contentDetails").getString("duration"); duration = formatDuration(duration); @@ -264,59 +271,57 @@ public class InfoUtil { try { YoutubeDLRequest request = new YoutubeDLRequest(query); request.addOption("--flat-playlist"); - request.addOption("-J"); + request.addOption("-j"); request.addOption("--skip-download"); + request.addOption("-R", "1"); + request.addOption("--socket-timeout", "5"); if (!query.contains("http")) request.addOption("--default-search", "ytsearch25"); YoutubeDLResponse youtubeDLResponse = YoutubeDL.getInstance().execute(request); - JSONObject jsonObject = new JSONObject(youtubeDLResponse.getOut()); + String[] results; + try { + results = youtubeDLResponse.getOut().split(System.getProperty("line.separator")); + }catch(Exception e){ + results = new String[]{youtubeDLResponse.getOut()}; + } + + int isPlaylist = 0; + JSONObject pl = new JSONObject(results[0]); + if (pl.has("playlist")){ + if (! pl.getString("playlist").equals(query)) isPlaylist = 1; + } + + for (String result : results) { + JSONObject jsonObject = new JSONObject(result); + if (jsonObject.getString("title").equals("[Private video]")) continue; - if(!jsonObject.has("entries")){ String url = jsonObject.getString("webpage_url"); + String thumb = ""; + if (jsonObject.has("thumbnail")){ + thumb = jsonObject.getString("thumbnail"); + }else { + JSONArray thumbs = jsonObject.getJSONArray("thumbnails"); + thumb = thumbs.getJSONObject(thumbs.length()-1).getString("url"); + } + + String website = ""; + if (jsonObject.has("ie_key")) website = jsonObject.getString("ie_key"); + else website = jsonObject.getString("extractor"); + + videos.add(new Video( jsonObject.getString("id"), url, jsonObject.getString("title"), jsonObject.getString("uploader"), formatIntegerDuration(jsonObject.getInt("duration")), - jsonObject.getString("thumbnail"), + thumb, dbManager.checkDownloaded(url, "audio"), dbManager.checkDownloaded(url, "video"), - 0, - jsonObject.getString("extractor")) + isPlaylist, + website) ); - }else{ - JSONArray jsonArray = jsonObject.getJSONArray("entries"); - - for (int i = 0; i < jsonArray.length(); i++){ - jsonObject = jsonArray.getJSONObject(i); - String url = jsonObject.getString("url"); - String thumb = ""; - if (jsonObject.has("thumbnail")){ - thumb = jsonObject.getString("thumbnail"); - }else { - JSONArray thumbs = jsonObject.getJSONArray("thumbnails"); - thumb = thumbs.getJSONObject(thumbs.length()-1).getString("url"); - } - String website = ""; - if (jsonObject.has("ie_key")) website = jsonObject.getString("ie_key"); - else website = jsonObject.getString("extractor"); - - videos.add(new Video( - jsonObject.getString("id"), - url, - jsonObject.getString("title"), - jsonObject.getString("uploader"), - formatIntegerDuration(jsonObject.getInt("duration")), - thumb, - dbManager.checkDownloaded(url, "audio"), - dbManager.checkDownloaded(url, "video"), - 0, - website) - ); - } } - }catch(Exception e){ e.printStackTrace(); } @@ -335,6 +340,8 @@ public class InfoUtil { //extra data from the same videos JSONObject contentDetails = genericRequest("https://www.googleapis.com/youtube/v3/videos?part=contentDetails&chart=mostPopular&videoCategoryId=10®ionCode="+countryCODE+"&maxResults=25&key="+key); + if(!contentDetails.has("items")) return new ArrayList<>(); + JSONArray dataArray = res.getJSONArray("items"); JSONArray extraDataArray = contentDetails.getJSONArray("items"); for(int i = 0; i < dataArray.length(); i++){ @@ -358,55 +365,30 @@ public class InfoUtil { } public String formatDuration(String dur){ - if(dur.equals("P0D")){ return "LIVE"; } + boolean hours = false; String duration = ""; - String tmp = ""; - String hours = ""; - String minutes = ""; - String seconds = ""; - //removing P - dur = dur.substring(1); - for(int i = 0; i < dur.length(); i++){ - char c = dur.charAt(i); - if(!Character.isDigit(c)){ - int nr = Character.getNumericValue(c); - if(nr < 10){ - tmp = "0" + tmp; - } - if (c == 'D'){ - hours = String.valueOf(Integer.parseInt(tmp) * 24); - }else if(c == 'T'){ - continue; - }else if(c == 'H'){ - hours = tmp; - }else if(c == 'M'){ - minutes = tmp; - }else if(c == 'S'){ - seconds = tmp; - } - tmp = ""; - continue; - } - tmp = tmp + c; + dur = dur.substring(2); + if (dur.contains("H")) { + hours = true; + duration += String.format(Locale.getDefault(), "%02d", Integer.parseInt(dur.substring(0, dur.indexOf("H")))) + ":"; + dur = dur.substring(dur.indexOf("H")+1); + } + if (dur.contains("M")) { + duration += String.format(Locale.getDefault(), "%02d", Integer.parseInt(dur.substring(0, dur.indexOf("M")))) + ":"; + dur = dur.substring(dur.indexOf("M")+1); + }else if(hours) duration += "00:"; + if (dur.contains("S")){ + if(duration.isEmpty()) duration = "00:"; + duration += String.format(Locale.getDefault(), "%02d", Integer.parseInt(dur.substring(0, dur.indexOf("S")))); + }else{ + duration += "00"; } - if(seconds.isEmpty()) seconds = "00"; - else if(Integer.parseInt(seconds) < 10) seconds = "0" + seconds; - if(minutes.isEmpty()){ - minutes = "0"; - } - duration = minutes + ":" + seconds; - - if(!hours.isEmpty()){ - if(Integer.parseInt(minutes) < 10) minutes = "0" + minutes; - duration = hours + ":" + minutes + ":" + seconds; - } - - if(duration.equals("0:00:00")){ + if(duration.equals("00:00")){ duration = ""; } diff --git a/app/src/main/res/drawable/ic_app_icon.xml b/app/src/main/res/drawable/ic_app_icon.xml new file mode 100644 index 00000000..d6a5584c --- /dev/null +++ b/app/src/main/res/drawable/ic_app_icon.xml @@ -0,0 +1,15 @@ + + + diff --git a/app/src/main/res/drawable/ic_downloads.xml b/app/src/main/res/drawable/ic_downloads.xml new file mode 100644 index 00000000..ad08ddb0 --- /dev/null +++ b/app/src/main/res/drawable/ic_downloads.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_history.xml b/app/src/main/res/drawable/ic_history.xml deleted file mode 100644 index 98a82168..00000000 --- a/app/src/main/res/drawable/ic_history.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/layout/fragment_more.xml b/app/src/main/res/layout/fragment_more.xml index ebbe9341..469c74d4 100644 --- a/app/src/main/res/layout/fragment_more.xml +++ b/app/src/main/res/layout/fragment_more.xml @@ -1,27 +1,62 @@ - + android:layout_height="wrap_content"> - + android:layout_height="?attr/actionBarSize" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/preference_coordinatorLayout" + xmlns:android="http://schemas.android.com/apk/res/android"> - + android:layout_height="match_parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> - + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/app/src/main/res/layout/result_card.xml b/app/src/main/res/layout/result_card.xml index 43e63b15..74e611a0 100644 --- a/app/src/main/res/layout/result_card.xml +++ b/app/src/main/res/layout/result_card.xml @@ -36,6 +36,17 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> + + - - diff --git a/app/src/main/res/menu/bottom_nav_menu.xml b/app/src/main/res/menu/bottom_nav_menu.xml index 9cd49e2f..73d1eeab 100644 --- a/app/src/main/res/menu/bottom_nav_menu.xml +++ b/app/src/main/res/menu/bottom_nav_menu.xml @@ -4,9 +4,9 @@ - + diff --git a/app/src/main/res/values-sq-rAL/strings.xml b/app/src/main/res/values-sq-rAL/strings.xml index 5de79ea1..42c260ec 100644 --- a/app/src/main/res/values-sq-rAL/strings.xml +++ b/app/src/main/res/values-sq-rAL/strings.xml @@ -45,8 +45,7 @@ Redaktori i Muzikës Komanda Më Shumë - Përditëso Aplikacionin - Shkarko versionin e fundit të aplikacionit + Kontrollo për përditësime Versioni Ekzekuto Komandën Shkruaj komandën yt-dlp @@ -96,4 +95,6 @@ Linku u kopjua Çelësi API i YouTube Përdor një çelës api për të marrë rezultate më të shpejta nga YouTube + Njoftohu kur një përditësim është i vlefshëm + Shkarkimet \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 837f77b6..c22a85b9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -45,8 +45,7 @@ Music Editor Commands More - Update App - Download the latest version of the app + Check for updates Version Run Custom Command Write yt-dlp command @@ -99,4 +98,6 @@ Link copied to clipboard YouTube API Key Use an api key to get faster results from YouTube + Let you know when updates are available right when you open the application + Downloads diff --git a/app/src/main/res/xml/more_preferences.xml b/app/src/main/res/xml/more_preferences.xml index c622d9ca..f383e16c 100644 --- a/app/src/main/res/xml/more_preferences.xml +++ b/app/src/main/res/xml/more_preferences.xml @@ -4,32 +4,15 @@ - - - - - - - - - - - - + + + - + + + + + + + +