mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-28 16:00:31 +05:30
Merge pull request #997 from Bnyro/master
fix import from csv and other things
This commit is contained in:
commit
33e090d6d4
@ -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 {
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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!!
|
||||||
)
|
)
|
||||||
|
@ -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 ->
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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">
|
||||||
|
|
||||||
|
@ -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" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user