Merge pull request #997 from Bnyro/master

fix import from csv and other things
This commit is contained in:
Bnyro 2022-08-08 15:23:00 +02:00 committed by GitHub
commit 33e090d6d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 88 additions and 65 deletions

View File

@ -67,7 +67,7 @@ class PlaylistsAdapter(
builder.show() builder.show()
} }
root.setOnClickListener { root.setOnClickListener {
NavigationHelper.navigatePlaylist(root.context, playlist.id) NavigationHelper.navigatePlaylist(root.context, playlist.id, true)
} }
root.setOnLongClickListener { root.setOnLongClickListener {

View File

@ -171,7 +171,7 @@ class SearchAdapter(
root.context.getString(R.string.videoCount, item.videos.toString()) root.context.getString(R.string.videoCount, item.videos.toString())
} }
root.setOnClickListener { root.setOnClickListener {
NavigationHelper.navigatePlaylist(root.context, item.url) NavigationHelper.navigatePlaylist(root.context, item.url, false)
} }
root.setOnLongClickListener { root.setOnLongClickListener {
val playlistId = item.url!!.toID() val playlistId = item.url!!.toID()

View File

@ -9,6 +9,7 @@ import android.widget.Toast
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.Globals import com.github.libretube.Globals
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.adapters.PlaylistsAdapter import com.github.libretube.adapters.PlaylistsAdapter
@ -44,7 +45,7 @@ class LibraryFragment : BaseFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.playlistRecView.layoutManager = LinearLayoutManager(view.context) binding.playlistRecView.layoutManager = LinearLayoutManager(requireContext())
token = PreferenceHelper.getToken() token = PreferenceHelper.getToken()
// hide watch history button of history disabled // hide watch history button of history disabled
@ -59,8 +60,7 @@ class LibraryFragment : BaseFragment() {
} }
if (token != "") { if (token != "") {
binding.boogh.visibility = View.GONE binding.loginOrRegister.visibility = View.GONE
binding.textLike.visibility = View.GONE
fetchPlaylists() fetchPlaylists()
binding.playlistRefresh.isEnabled = true binding.playlistRefresh.isEnabled = true
binding.playlistRefresh.setOnRefreshListener { binding.playlistRefresh.setOnRefreshListener {
@ -103,26 +103,32 @@ class LibraryFragment : BaseFragment() {
binding.playlistRefresh.isRefreshing = false binding.playlistRefresh.isRefreshing = false
} }
if (response.isNotEmpty()) { if (response.isNotEmpty()) {
runOnUiThread { binding.loginOrRegister.visibility = View.GONE
binding.boogh.visibility = View.GONE
binding.textLike.visibility = View.GONE
}
val playlistsAdapter = PlaylistsAdapter( val playlistsAdapter = PlaylistsAdapter(
response.toMutableList(), response.toMutableList(),
childFragmentManager, childFragmentManager,
requireActivity() requireActivity()
) )
// listen for playlists to become deleted
playlistsAdapter.registerAdapterDataObserver(object :
RecyclerView.AdapterDataObserver() {
override fun onChanged() {
Log.e(TAG, playlistsAdapter.itemCount.toString())
if (playlistsAdapter.itemCount == 0) {
binding.loginOrRegister.visibility = View.VISIBLE
}
super.onChanged()
}
})
binding.playlistRecView.adapter = playlistsAdapter binding.playlistRecView.adapter = playlistsAdapter
} else { } else {
runOnUiThread { runOnUiThread {
binding.boogh.apply { binding.loginOrRegister.visibility = View.VISIBLE
visibility = View.VISIBLE binding.boogh.setImageResource(R.drawable.ic_list)
setImageResource(R.drawable.ic_list) binding.textLike.text = getString(R.string.emptyList)
}
binding.textLike.apply {
visibility = View.VISIBLE
text = getString(R.string.emptyList)
}
} }
} }
} }

View File

@ -14,7 +14,6 @@ import com.github.libretube.adapters.PlaylistAdapter
import com.github.libretube.databinding.FragmentPlaylistBinding import com.github.libretube.databinding.FragmentPlaylistBinding
import com.github.libretube.dialogs.PlaylistOptionsDialog import com.github.libretube.dialogs.PlaylistOptionsDialog
import com.github.libretube.extensions.BaseFragment import com.github.libretube.extensions.BaseFragment
import com.github.libretube.preferences.PreferenceHelper
import com.github.libretube.util.RetrofitInstance import com.github.libretube.util.RetrofitInstance
import com.github.libretube.util.toID import com.github.libretube.util.toID
import retrofit2.HttpException import retrofit2.HttpException
@ -26,7 +25,7 @@ class PlaylistFragment : BaseFragment() {
private var playlistId: String? = null private var playlistId: String? = null
private var isOwner: Boolean = false private var isOwner: Boolean = false
var nextPage: String? = null private var nextPage: String? = null
private var playlistAdapter: PlaylistAdapter? = null private var playlistAdapter: PlaylistAdapter? = null
private var isLoading = true private var isLoading = true
@ -34,6 +33,7 @@ class PlaylistFragment : BaseFragment() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
arguments?.let { arguments?.let {
playlistId = it.getString("playlist_id") playlistId = it.getString("playlist_id")
isOwner = it.getBoolean("isOwner")
} }
} }
@ -61,7 +61,8 @@ class PlaylistFragment : BaseFragment() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { val response = try {
// load locally stored playlists with the auth api // load locally stored playlists with the auth api
RetrofitInstance.authApi.getPlaylist(playlistId!!) if (isOwner) RetrofitInstance.authApi.getPlaylist(playlistId!!)
else RetrofitInstance.api.getPlaylist(playlistId!!)
} catch (e: IOException) { } catch (e: IOException) {
println(e) println(e)
Log.e(TAG, "IOException, you might not have internet connection") Log.e(TAG, "IOException, you might not have internet connection")
@ -75,14 +76,10 @@ class PlaylistFragment : BaseFragment() {
runOnUiThread { runOnUiThread {
binding.playlistProgress.visibility = View.GONE binding.playlistProgress.visibility = View.GONE
binding.playlistName.text = response.name binding.playlistName.text = response.name
binding.playlistUploader.text = response.uploader binding.uploader.text = response.uploader
binding.playlistTotVideos.text = binding.videoCount.text =
getString(R.string.videoCount, response.videos.toString()) getString(R.string.videoCount, response.videos.toString())
val user = PreferenceHelper.getUsername()
// check whether the user owns the playlist
isOwner = response.uploaderUrl == null && response.uploader.equals(user, true)
// show playlist options // show playlist options
binding.optionsMenu.setOnClickListener { binding.optionsMenu.setOnClickListener {
val optionsDialog = val optionsDialog =
@ -100,6 +97,16 @@ class PlaylistFragment : BaseFragment() {
requireActivity(), requireActivity(),
childFragmentManager childFragmentManager
) )
// listen for playlist items to become deleted
playlistAdapter!!.registerAdapterDataObserver(object :
RecyclerView.AdapterDataObserver() {
override fun onChanged() {
binding.videoCount.text =
getString(R.string.videoCount, playlistAdapter!!.itemCount.toString())
}
})
binding.playlistRecView.adapter = playlistAdapter binding.playlistRecView.adapter = playlistAdapter
binding.playlistScrollview.viewTreeObserver binding.playlistScrollview.viewTreeObserver
.addOnScrollChangedListener { .addOnScrollChangedListener {
@ -111,8 +118,6 @@ class PlaylistFragment : BaseFragment() {
isLoading = true isLoading = true
fetchNextPage() fetchNextPage()
} }
} else {
// scroll view is not at bottom
} }
} }
@ -155,7 +160,10 @@ class PlaylistFragment : BaseFragment() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { val response = try {
// load locally stored playlists with the auth api // load locally stored playlists with the auth api
RetrofitInstance.authApi.getPlaylistNextPage( if (isOwner) RetrofitInstance.authApi.getPlaylistNextPage(
playlistId!!,
nextPage!!
) else RetrofitInstance.api.getPlaylistNextPage(
playlistId!!, playlistId!!,
nextPage!! nextPage!!
) )

View File

@ -15,52 +15,48 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import java.io.BufferedReader import java.io.BufferedReader
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.InputStream
import java.io.InputStreamReader import java.io.InputStreamReader
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
class ImportHelper( class ImportHelper(
private val activity: Activity private val activity: Activity
) { ) {
private val TAG = "ImportHelper" private val TAG = "ImportHelper"
/**
* Import subscriptions by a file uri
*/
fun importSubscriptions(uri: Uri?) { fun importSubscriptions(uri: Uri?) {
if (uri == null) return if (uri == null) return
try { try {
val type = activity.contentResolver.getType(uri)
var inputStream: InputStream? = activity.contentResolver.openInputStream(uri)
var channels = ArrayList<String>() var channels = ArrayList<String>()
if (type == "application/json") { val fileType = activity.contentResolver.getType(uri)
if (fileType == "application/json") {
// NewPipe subscriptions format
val mapper = ObjectMapper() val mapper = ObjectMapper()
val json = readTextFromUri(uri) val json = readRawTextFromUri(uri)
val subscriptions = mapper.readValue(json, NewPipeSubscriptions::class.java) val subscriptions = mapper.readValue(json, NewPipeSubscriptions::class.java)
channels = subscriptions.subscriptions?.map { channels = subscriptions.subscriptions?.map {
it.url?.replace("https://www.youtube.com/channel/", "")!! it.url?.replace("https://www.youtube.com/channel/", "")!!
} as ArrayList<String> } as ArrayList<String>
} else if (type == "application/zip") { } else if (
val zis = ZipInputStream(inputStream) fileType == "text/csv" ||
var entry: ZipEntry? = zis.nextEntry fileType == "text/comma-separated-values"
) {
while (entry != null) { // import subscriptions from Google/YouTube Takeout
if (entry.name.endsWith(".csv")) { val inputStream = activity.contentResolver.openInputStream(uri)
inputStream = zis BufferedReader(InputStreamReader(inputStream)).use { reader ->
break var line: String? = reader.readLine()
} while (line != null) {
entry = zis.nextEntry val channelId = line.substringBefore(",")
inputStream?.bufferedReader()?.readLines()?.forEach { if (channelId.length == 24) channels.add(channelId)
if (it.isNotBlank()) { line = reader.readLine()
val channelId = it.substringBefore(",")
if (channelId.length == 24) {
channels.add(channelId)
}
} }
} }
inputStream?.close() inputStream?.close()
}
} else { } else {
throw IllegalArgumentException("unsupported type") throw IllegalArgumentException("Unsupported file type")
} }
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
@ -78,7 +74,7 @@ class ImportHelper(
} }
} }
private fun readTextFromUri(uri: Uri): String { private fun readRawTextFromUri(uri: Uri): String {
val stringBuilder = StringBuilder() val stringBuilder = StringBuilder()
activity.contentResolver.openInputStream(uri)?.use { inputStream -> activity.contentResolver.openInputStream(uri)?.use { inputStream ->
BufferedReader(InputStreamReader(inputStream)).use { reader -> BufferedReader(InputStreamReader(inputStream)).use { reader ->

View File

@ -10,7 +10,10 @@ import com.github.libretube.activities.MainActivity
import com.github.libretube.fragments.PlayerFragment import com.github.libretube.fragments.PlayerFragment
object NavigationHelper { object NavigationHelper {
fun navigateChannel(context: Context, channelId: String?) { fun navigateChannel(
context: Context,
channelId: String?
) {
if (channelId != null) { if (channelId != null) {
val activity = context as MainActivity val activity = context as MainActivity
val bundle = bundleOf("channel_id" to channelId) val bundle = bundleOf("channel_id" to channelId)
@ -28,7 +31,11 @@ object NavigationHelper {
} }
} }
fun navigateVideo(context: Context, videoId: String?, playlistId: String? = null) { fun navigateVideo(
context: Context,
videoId: String?,
playlistId: String? = null
) {
if (videoId != null) { if (videoId != null) {
val bundle = Bundle() val bundle = Bundle()
bundle.putString("videoId", videoId.toID()) bundle.putString("videoId", videoId.toID())
@ -45,10 +52,16 @@ object NavigationHelper {
} }
} }
fun navigatePlaylist(context: Context, playlistId: String?) { fun navigatePlaylist(
context: Context,
playlistId: String?,
isOwner: Boolean
) {
if (playlistId != null) { if (playlistId != null) {
val activity = context as MainActivity val activity = context as MainActivity
val bundle = bundleOf("playlist_id" to playlistId) val bundle = Bundle()
bundle.putString("playlist_id", playlistId)
bundle.putBoolean("isOwner", isOwner)
activity.navController.navigate(R.id.playlistFragment, bundle) activity.navController.navigate(R.id.playlistFragment, bundle)
} }
} }

View File

@ -7,7 +7,7 @@
tools:context=".fragments.LibraryFragment"> tools:context=".fragments.LibraryFragment">
<RelativeLayout <RelativeLayout
android:id="@+id/loginOrRegister2" android:id="@+id/loginOrRegister"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">

View File

@ -20,7 +20,7 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <LinearLayout
@ -48,14 +48,14 @@
</LinearLayout> </LinearLayout>
<TextView <TextView
android:id="@+id/playlist_uploader" android:id="@+id/uploader"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="8dp" android:padding="8dp"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView <TextView
android:id="@+id/playlist_totVideos" android:id="@+id/video_count"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="8dp" /> android:padding="8dp" />