mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-29 16:30:31 +05:30
import subscription
This commit is contained in:
parent
347179b59a
commit
6688e7a5de
@ -10,8 +10,8 @@ android {
|
|||||||
applicationId 'com.github.libretube'
|
applicationId 'com.github.libretube'
|
||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 31
|
targetSdk 31
|
||||||
versionCode 6
|
versionCode 7
|
||||||
versionName '0.2.4'
|
versionName '0.2.5'
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||||
resValue "string", "app_name", "LibreTube"
|
resValue "string", "app_name", "LibreTube"
|
||||||
|
@ -16,23 +16,10 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"attributes": [],
|
"attributes": [],
|
||||||
"versionCode": 6,
|
"versionCode": 7,
|
||||||
"versionName": "0.2.4",
|
"versionName": "0.2.5",
|
||||||
"outputFile": "app-x86_64-release.apk"
|
"outputFile": "app-x86_64-release.apk"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "ONE_OF_MANY",
|
|
||||||
"filters": [
|
|
||||||
{
|
|
||||||
"filterType": "ABI",
|
|
||||||
"value": "arm64-v8a"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"attributes": [],
|
|
||||||
"versionCode": 6,
|
|
||||||
"versionName": "0.2.4",
|
|
||||||
"outputFile": "app-arm64-v8a-release.apk"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "ONE_OF_MANY",
|
"type": "ONE_OF_MANY",
|
||||||
"filters": [
|
"filters": [
|
||||||
@ -42,10 +29,23 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"attributes": [],
|
"attributes": [],
|
||||||
"versionCode": 6,
|
"versionCode": 7,
|
||||||
"versionName": "0.2.4",
|
"versionName": "0.2.5",
|
||||||
"outputFile": "app-x86-release.apk"
|
"outputFile": "app-x86-release.apk"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "ONE_OF_MANY",
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"filterType": "ABI",
|
||||||
|
"value": "arm64-v8a"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attributes": [],
|
||||||
|
"versionCode": 7,
|
||||||
|
"versionName": "0.2.5",
|
||||||
|
"outputFile": "app-arm64-v8a-release.apk"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "ONE_OF_MANY",
|
"type": "ONE_OF_MANY",
|
||||||
"filters": [
|
"filters": [
|
||||||
@ -55,8 +55,8 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"attributes": [],
|
"attributes": [],
|
||||||
"versionCode": 6,
|
"versionCode": 7,
|
||||||
"versionName": "0.2.4",
|
"versionName": "0.2.5",
|
||||||
"outputFile": "app-armeabi-v7a-release.apk"
|
"outputFile": "app-armeabi-v7a-release.apk"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
@ -15,6 +15,7 @@ import android.view.*
|
|||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
@ -49,6 +50,11 @@ class MainActivity : AppCompatActivity() {
|
|||||||
RetrofitInstance.url=sharedPreferences.getString("instance", "https://pipedapi.kavin.rocks/")!!
|
RetrofitInstance.url=sharedPreferences.getString("instance", "https://pipedapi.kavin.rocks/")!!
|
||||||
DynamicColors.applyToActivitiesIfAvailable(application)
|
DynamicColors.applyToActivitiesIfAvailable(application)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
|
when (sharedPreferences.getString("theme_togglee", "A")!!) {
|
||||||
|
"A" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||||
|
"L" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
|
||||||
|
"D" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
|
||||||
|
}
|
||||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||||
|
|
||||||
bottomNavigationView = findViewById(R.id.bottomNav)
|
bottomNavigationView = findViewById(R.id.bottomNav)
|
||||||
|
@ -52,8 +52,13 @@ interface PipedApi {
|
|||||||
@POST("unsubscribe")
|
@POST("unsubscribe")
|
||||||
suspend fun unsubscribe(@Header("Authorization") token: String, @Body subscribe: Subscribe): Message
|
suspend fun unsubscribe(@Header("Authorization") token: String, @Body subscribe: Subscribe): Message
|
||||||
|
|
||||||
|
@POST("import")
|
||||||
|
suspend fun importSubscriptions(@Header("Authorization") token: String, @Body channels: List<String>): Message
|
||||||
|
|
||||||
//only for fetching servers list
|
//only for fetching servers list
|
||||||
@GET
|
@GET
|
||||||
suspend fun getInstances(@Url url: String): List<Instances>
|
suspend fun getInstances(@Url url: String): List<Instances>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -125,4 +125,8 @@ class SearchFragment : Fragment() {
|
|||||||
requireActivity().window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
|
requireActivity().window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
hideKeyboard()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
package com.github.libretube
|
package com.github.libretube
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.ContentValues.TAG
|
import android.content.ContentResolver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@ -15,67 +14,83 @@ import androidx.activity.result.ActivityResultLauncher
|
|||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
|
||||||
import com.blankj.utilcode.util.UriUtils
|
import com.blankj.utilcode.util.UriUtils
|
||||||
import com.github.libretube.obj.Subscribe
|
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.*
|
||||||
import java.io.IOException
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
|
|
||||||
class Settings : PreferenceFragmentCompat() {
|
class Settings : PreferenceFragmentCompat() {
|
||||||
val TAG = "Settings"
|
val TAG = "Settings"
|
||||||
companion object {
|
companion object {
|
||||||
lateinit var getContent: ActivityResultLauncher<String>
|
lateinit var getContent: ActivityResultLauncher<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
|
getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
|
||||||
|
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
try{
|
try{
|
||||||
Log.d(TAG,UriUtils.uri2File(uri).toString())
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
val file = UriUtils.uri2File(uri)
|
|
||||||
var inputStream: InputStream? = null
|
|
||||||
if (file.extension == "zip") {
|
|
||||||
var zipfile = ZipFile(file)
|
|
||||||
|
|
||||||
var zipentry =
|
// Open a specific media item using ParcelFileDescriptor.
|
||||||
zipfile.getEntry("Takeout/YouTube and YouTube Music/subscriptions/subscriptions.csv")
|
val resolver: ContentResolver =
|
||||||
|
requireActivity()
|
||||||
|
.contentResolver
|
||||||
|
|
||||||
inputStream = zipfile.getInputStream(zipentry)
|
// "rw" for read-and-write;
|
||||||
}else if(file.extension == "csv"){
|
// "rwt" for truncating or overwriting existing file contents.
|
||||||
inputStream = file.inputStream()
|
val readOnlyMode = "r"
|
||||||
}
|
// uri - I have got from onActivityResult
|
||||||
val baos = ByteArrayOutputStream()
|
//uri = data.getData();
|
||||||
|
val parcelFile = resolver.openFileDescriptor(uri, readOnlyMode)
|
||||||
inputStream?.use { it.copyTo(baos) }
|
val fileReader = FileReader(parcelFile!!.fileDescriptor)
|
||||||
|
val reader = BufferedReader(fileReader)
|
||||||
var subscriptions = baos.toByteArray().decodeToString()
|
var line: String?
|
||||||
|
while (reader.readLine().also { line = it } != null) {
|
||||||
var subscribedCount = 0
|
Log.d(TAG,reader.readLine())
|
||||||
|
|
||||||
for (text in subscriptions.lines()) {
|
|
||||||
if (text.take(24) != "Channel Id,Channel Url,C" && text.take(24).isNotEmpty()) {
|
|
||||||
subscribe(text.take(24))
|
|
||||||
subscribedCount++
|
|
||||||
Log.d(TAG, "subscribed: " + text + " total: " + subscribedCount)
|
|
||||||
}
|
}
|
||||||
}
|
reader.close()
|
||||||
|
fileReader.close()
|
||||||
|
}else{
|
||||||
|
Log.d(TAG,UriUtils.uri2File(uri).toString())
|
||||||
|
val file = UriUtils.uri2File(uri)
|
||||||
|
var inputStream: InputStream? = null
|
||||||
|
if (file.extension == "zip") {
|
||||||
|
var zipfile = ZipFile(file)
|
||||||
|
|
||||||
Toast.makeText(
|
var zipentry =
|
||||||
context,
|
zipfile.getEntry("Takeout/YouTube and YouTube Music/subscriptions/subscriptions.csv")
|
||||||
"Subscribed to " + subscribedCount + " channels.",
|
|
||||||
Toast.LENGTH_SHORT
|
inputStream = zipfile.getInputStream(zipentry)
|
||||||
).show()
|
}else if(file.extension == "csv"){
|
||||||
|
inputStream = file.inputStream()
|
||||||
|
}
|
||||||
|
val baos = ByteArrayOutputStream()
|
||||||
|
|
||||||
|
inputStream?.use { it.copyTo(baos) }
|
||||||
|
|
||||||
|
var subscriptions = baos.toByteArray().decodeToString()
|
||||||
|
var channels: MutableList<String> = emptyList<String>().toMutableList()
|
||||||
|
var subscribedCount = 0
|
||||||
|
for (text in subscriptions.lines().subList(1,subscriptions.lines().size)) {
|
||||||
|
if (text.replace(" ","") != "") {
|
||||||
|
val channel = text.split(",")[0]
|
||||||
|
channels.add(channel)
|
||||||
|
subscribedCount++
|
||||||
|
Log.d(TAG, "subscribed: " + text + " total: " + subscribedCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subscribe(channels)
|
||||||
|
}
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
|
Log.e(TAG,e.toString())
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
context,
|
context,
|
||||||
R.string.error,
|
R.string.error,
|
||||||
@ -121,7 +136,9 @@ class Settings : PreferenceFragmentCompat() {
|
|||||||
//check StorageAccess
|
//check StorageAccess
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
Log.d("myz", "" + Build.VERSION.SDK_INT)
|
Log.d("myz", "" + Build.VERSION.SDK_INT)
|
||||||
if (!Environment.isExternalStorageManager()) {
|
if (ContextCompat.checkSelfPermission(this.requireContext(), Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
|
!= PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
ActivityCompat.requestPermissions(
|
ActivityCompat.requestPermissions(
|
||||||
this.requireActivity(), arrayOf(
|
this.requireActivity(), arrayOf(
|
||||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||||
@ -218,15 +235,12 @@ class Settings : PreferenceFragmentCompat() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun subscribe(channel_id: String) {
|
private fun subscribe(channels: List<String>) {
|
||||||
fun run() {
|
fun run() {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
|
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
|
||||||
RetrofitInstance.api.subscribe(
|
RetrofitInstance.api.importSubscriptions(sharedPref?.getString("token", "")!!,channels)
|
||||||
sharedPref?.getString("token", "")!!,
|
|
||||||
Subscribe(channel_id)
|
|
||||||
)
|
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.e(TAG, "IOException, you might not have internet connection")
|
Log.e(TAG, "IOException, you might not have internet connection")
|
||||||
return@launchWhenCreated
|
return@launchWhenCreated
|
||||||
@ -234,9 +248,17 @@ class Settings : PreferenceFragmentCompat() {
|
|||||||
Log.e(TAG, "HttpException, unexpected response$e")
|
Log.e(TAG, "HttpException, unexpected response$e")
|
||||||
return@launchWhenCreated
|
return@launchWhenCreated
|
||||||
}
|
}
|
||||||
|
if(response.message == "ok"){
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
R.string.importsuccess,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
run()
|
run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
<string name="region">Choose a region</string>
|
<string name="region">Choose a region</string>
|
||||||
<string name="login_register">Login/Register</string>
|
<string name="login_register">Login/Register</string>
|
||||||
<string name="please_login">Please Login or Register from the settings to show your Subscriptions!</string>
|
<string name="please_login">Please Login or Register from the settings to show your Subscriptions!</string>
|
||||||
|
<string name="importsuccess">Subscribed successfully!</string>
|
||||||
<string name="subscribeIsEmpty">Subscribe to some channels first!</string>
|
<string name="subscribeIsEmpty">Subscribe to some channels first!</string>
|
||||||
<string name="cannotDownload">Can\'t Download this stream!</string>
|
<string name="cannotDownload">Can\'t Download this stream!</string>
|
||||||
<string name="dlcomplete">Download is completed!</string>
|
<string name="dlcomplete">Download is completed!</string>
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
app:key="grid"
|
app:key="grid"
|
||||||
app:entries="@array/grid"
|
app:entries="@array/grid"
|
||||||
app:entryValues="@array/grid"
|
app:entryValues="@array/grid"
|
||||||
app:defaultValue=""
|
app:defaultValue="@integer/grid_items"
|
||||||
android:icon="@drawable/ic_column"
|
android:icon="@drawable/ic_column"
|
||||||
app:useSimpleSummaryProvider="true"
|
app:useSimpleSummaryProvider="true"
|
||||||
/>
|
/>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user