added livedata to results

This commit is contained in:
Denis Çerri 2023-01-18 22:51:58 +01:00
parent 335be8299c
commit 09bddccb61
No known key found for this signature in database
GPG key ID: 96B3554AF5B193EE
29 changed files with 1901 additions and 2014 deletions

View file

@ -82,6 +82,7 @@ android {
composeOptions {
kotlinCompilerExtensionVersion composeVer
}
namespace 'com.deniscerri.ytdlnis'
}

View file

@ -0,0 +1,312 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "1391e7ec82bbacc310cf6f23e3d1f85a",
"entities": [
{
"tableName": "results",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `website` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"fields": [
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "author",
"columnName": "author",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "duration",
"columnName": "duration",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "thumb",
"columnName": "thumb",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "website",
"columnName": "website",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "playlistTitle",
"columnName": "playlistTitle",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "history",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `type` TEXT NOT NULL, `time` INTEGER NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"fields": [
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "author",
"columnName": "author",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "duration",
"columnName": "duration",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "thumb",
"columnName": "thumb",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "time",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "downloadPath",
"columnName": "downloadPath",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "website",
"columnName": "website",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "downloads",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `thumb` TEXT NOT NULL, `duration` TEXT NOT NULL, `type` TEXT NOT NULL, `audioQualityId` TEXT NOT NULL, `videoQualityId` TEXT NOT NULL, `customTemplateId` INTEGER NOT NULL, `audioFormat` TEXT NOT NULL, `videoFormat` TEXT NOT NULL, `formatDesc` TEXT NOT NULL, `removeAudio` INTEGER NOT NULL DEFAULT 0, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `downloadSize` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `status` TEXT NOT NULL DEFAULT 'Queued', `id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"fields": [
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "author",
"columnName": "author",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "thumb",
"columnName": "thumb",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "duration",
"columnName": "duration",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "audioQualityId",
"columnName": "audioQualityId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoQualityId",
"columnName": "videoQualityId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "customTemplateId",
"columnName": "customTemplateId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "audioFormat",
"columnName": "audioFormat",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoFormat",
"columnName": "videoFormat",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "formatDesc",
"columnName": "formatDesc",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "removeAudio",
"columnName": "removeAudio",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "downloadPath",
"columnName": "downloadPath",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "website",
"columnName": "website",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "downloadSize",
"columnName": "downloadSize",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "playlistTitle",
"columnName": "playlistTitle",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'Queued'"
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "commandTemplates",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`title` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"fields": [
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "content",
"columnName": "content",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1391e7ec82bbacc310cf6f23e3d1f85a')"
]
}
}

View file

@ -1,26 +0,0 @@
package com.deniscerri.ytdlnis;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.deniscerri.ytdl", appContext.getPackageName());
}
}

View file

@ -0,0 +1,22 @@
package com.deniscerri.ytdlnis
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
/**
* Instrumented test, which will execute on an Android device.
*
* @see [Testing documentation](http://d.android.com/tools/testing)
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
Assert.assertEquals("com.deniscerri.ytdl", appContext.packageName)
}
}

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.deniscerri.ytdlnis" >
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

View file

@ -15,7 +15,7 @@ import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.workDataOf
import com.deniscerri.ytdlnis.database.Video
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.service.DownloadInfo
import com.deniscerri.ytdlnis.service.IDownloaderListener
import com.deniscerri.ytdlnis.service.IDownloaderService
@ -46,7 +46,7 @@ class DownloaderService : Service() {
ConcurrentHashMap()
private val fileUtil = FileUtil()
private val downloadInfo = DownloadInfo()
private var downloadQueue = LinkedList<Video>()
private var downloadQueue = LinkedList<ResultItem>()
private val compositeDisposable = CompositeDisposable()
private val notificationUtil = App.notificationUtil
private var context: Context? = null
@ -91,44 +91,45 @@ class DownloaderService : Service() {
}
override fun onBind(intent: Intent): IBinder? {
val theIntent: Intent
val pendingIntent: PendingIntent
if (intent.getBooleanExtra("rebind", false)) {
return binder
}
when (val id = intent.getIntExtra("id", 1)) {
NotificationUtil.DOWNLOAD_NOTIFICATION_ID -> {
notificationChannelID = NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
theIntent = Intent(this, MainActivity::class.java)
pendingIntent =
PendingIntent.getActivity(this, 0, theIntent, PendingIntent.FLAG_IMMUTABLE)
downloadNotificationID = id
val queue: ArrayList<out Video>? = intent.getParcelableArrayListExtra("queue")
downloadQueue = LinkedList()
downloadQueue.addAll(queue!!)
downloadInfo.downloadQueue = downloadQueue
val title = downloadInfo.video.title
val notification =
App.notificationUtil.createDownloadServiceNotification(pendingIntent, title, NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID)
startForeground(downloadNotificationID, notification)
startDownload(downloadQueue)
}
NotificationUtil.COMMAND_DOWNLOAD_NOTIFICATION_ID -> {
notificationChannelID = NotificationUtil.COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID
theIntent = Intent(this, CustomCommandActivity::class.java)
pendingIntent =
PendingIntent.getActivity(this, 0, theIntent, PendingIntent.FLAG_IMMUTABLE)
downloadNotificationID = id
val command = intent.getStringExtra("command")
val commandNotification = App.notificationUtil.createDownloadServiceNotification(
pendingIntent,
getString(R.string.running_ytdlp_command),
NotificationUtil.COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID
)
startForeground(downloadNotificationID, commandNotification)
startCommandDownload(command)
}
}
// val theIntent: Intent
// val pendingIntent: PendingIntent
// if (intent.getBooleanExtra("rebind", false)) {
// return binder
// }
// when (val id = intent.getIntExtra("id", 1)) {
// NotificationUtil.DOWNLOAD_NOTIFICATION_ID -> {
// notificationChannelID = NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
// theIntent = Intent(this, MainActivity::class.java)
// pendingIntent =
// PendingIntent.getActivity(this, 0, theIntent, PendingIntent.FLAG_IMMUTABLE)
// downloadNotificationID = id
// val queue: ArrayList<out ResultItem>? = intent.getParcelableArrayListExtra("queue")
// downloadQueue = LinkedList()
// downloadQueue.addAll(queue!!)
// downloadInfo.downloadQueue = downloadQueue
// val title = downloadInfo.video.title
// val notification =
// App.notificationUtil.createDownloadServiceNotification(pendingIntent, title, 1, NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID)
// startForeground(downloadNotificationID, notification)
// startDownload(downloadQueue)
// }
// NotificationUtil.COMMAND_DOWNLOAD_NOTIFICATION_ID -> {
// notificationChannelID = NotificationUtil.COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID
// theIntent = Intent(this, CustomCommandActivity::class.java)
// pendingIntent =
// PendingIntent.getActivity(this, 0, theIntent, PendingIntent.FLAG_IMMUTABLE)
// downloadNotificationID = id
// val command = intent.getStringExtra("command")
// val commandNotification = App.notificationUtil.createDownloadServiceNotification(
// pendingIntent,
// getString(R.string.running_ytdlp_command),
// 1,
// NotificationUtil.COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID
// )
// startForeground(downloadNotificationID, commandNotification)
// startCommandDownload(command)
// }
// }
return binder
}
@ -160,23 +161,24 @@ class DownloaderService : Service() {
activities.remove(activity)
}
override fun updateQueue(queue: ArrayList<Video>) {
try {
for (i in queue.indices) {
downloadQueue.add(queue[i].clone() as Video)
}
if (downloadQueue.size == queue.size) {
downloadInfo.downloadQueue = downloadQueue
startDownload(downloadQueue)
} else {
downloadInfo.downloadQueue = downloadQueue
Toast.makeText(context, getString(R.string.added_to_queue), Toast.LENGTH_SHORT)
.show()
}
} catch (e: Exception) {
Toast.makeText(context, "Couldn't update download queue! :(", Toast.LENGTH_SHORT)
.show()
}
override fun updateQueue(queue: ArrayList<ResultItem>) {
// try {
// for (i in queue.indices) {
// downloadQueue.add(queue[i].clone() as ResultItem)
// }
// if (downloadQueue.size == queue.size) {
// downloadInfo.downloadQueue = downloadQueue
// startDownload(downloadQueue)
// } else {
// downloadInfo.downloadQueue = downloadQueue
// Toast.makeText(context, getString(R.string.added_to_queue), Toast.LENGTH_SHORT)
// .show()
// }
// } catch (e: Exception) {
// Toast.makeText(context, "Couldn't update download queue! :(", Toast.LENGTH_SHORT)
// .show()
// }
}
override fun cancelDownload(cancelAll: Boolean) {
@ -192,30 +194,31 @@ class DownloaderService : Service() {
}
}
override fun removeItemFromDownloadQueue(video: Video, type: String) {
//if its the same video with the same download type as the current downloading one
if (downloadInfo.video.getURL() == video.getURL() && downloadInfo.video.downloadedType == video.downloadedType) {
cancelDownload(false)
downloadInfo.downloadType = type
onDownloadCancel(downloadInfo)
downloadQueue.pop()
downloadInfo.downloadQueue = downloadQueue
startDownload(downloadQueue)
} else {
downloadQueue.remove(video)
try {
val info = DownloadInfo()
info.video = video
info.downloadType = type
onDownloadCancel(info)
} catch (ignored: Exception) {
}
}
Toast.makeText(
context,
video.title + " " + getString(R.string.removed_from_queue),
Toast.LENGTH_SHORT
).show()
override fun removeItemFromDownloadQueue(video: ResultItem?, type: String?) {
// //if its the same video with the same download type as the current downloading one
// if (downloadInfo.video.url == video.url && downloadInfo.video.type == video.downloadedType) {
// cancelDownload(false)
// downloadInfo.downloadType = type
// onDownloadCancel(downloadInfo)
// downloadQueue.pop()
// downloadInfo.downloadQueue = downloadQueue
// startDownload(downloadQueue)
// } else {
// downloadQueue.remove(video)
// try {
// val info = DownloadInfo()
// info.video = video
// info.downloadType = type
// onDownloadCancel(info)
// } catch (ignored: Exception) {
// }
// }
// Toast.makeText(
// context,
// video.title + " " + getString(R.string.removed_from_queue),
// Toast.LENGTH_SHORT
// ).show()
}
}
@ -277,212 +280,212 @@ class DownloaderService : Service() {
}
}
private fun startDownload(videos: LinkedList<Video>) {
if (videos.size == 0) {
finishService()
return
}
val video: Video = try {
videos.peek() as Video
} catch (e: Exception) {
finishService()
return
}
try {
for (activity in activities.keys) {
activity.runOnUiThread {
for (i in activities[activity]!!.indices) {
val callback = activities[activity]!![i]
callback.onDownloadStart(downloadInfo)
}
}
}
} catch (err: Exception) {
err.printStackTrace()
}
val url = video.getURL()
val request = YoutubeDLRequest(url)
val type = video.downloadedType
val downloadLocation = getDownloadLocation(type)
var tempFileDir = File(cacheDir.absolutePath + """/${video.title}##${video.downloadedType}""")
tempFileDir.delete()
tempFileDir.mkdir()
val sharedPreferences = context!!.getSharedPreferences("root_preferences", MODE_PRIVATE)
val aria2 = sharedPreferences.getBoolean("aria2", false)
if (aria2) {
request.addOption("--downloader", "libaria2c.so")
request.addOption("--external-downloader-args", "aria2c:\"--summary-interval=1\"")
} else {
val concurrentFragments = sharedPreferences.getInt("concurrent_fragments", 1)
if (concurrentFragments > 1) request.addOption("-N", concurrentFragments)
}
val limitRate = sharedPreferences.getString("limit_rate", "")
if (limitRate != "") request.addOption("-r", limitRate!!)
val writeThumbnail = sharedPreferences.getBoolean("write_thumbnail", false)
if (writeThumbnail) {
request.addOption("--write-thumbnail")
request.addOption("--convert-thumbnails", "png")
}
request.addOption("--no-mtime")
val sponsorBlockFilters = sharedPreferences.getStringSet("sponsorblock_filters", emptySet())
if (sponsorBlockFilters!!.isNotEmpty()) {
val filters = java.lang.String.join(",", sponsorBlockFilters)
request.addOption("--sponsorblock-remove", filters)
}
if (type == "audio") {
if (downloadLocation.equals(getString(R.string.music_path))){
tempFileDir = File(getString(R.string.music_path))
}
request.addOption("-x")
var format = video.audioFormat
if (format == null) format = sharedPreferences.getString("audio_format", "")
request.addOption("--audio-format", format!!)
request.addOption("--embed-metadata")
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
if (embedThumb) {
request.addOption("--embed-thumbnail")
request.addOption("--convert-thumbnails", "png")
try {
val config = File(cacheDir, "config" + video.videoId + ".txt")
val config_data =
"--ppa \"ffmpeg: -c:v png -vf crop=\\\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\\\"\""
val stream = FileOutputStream(config)
stream.write(config_data.toByteArray())
stream.close()
request.addOption("--config", config.absolutePath)
} catch (ignored: java.lang.Exception) {
}
}
request.addOption("--parse-metadata", "%(release_year,upload_date)s:%(meta_date)s")
request.addCommands(Arrays.asList("--replace-in-metadata", "title", ".*.", video.title))
request.addCommands(Arrays.asList("--replace-in-metadata", "uploader", ".*.", video.author))
if (!video.playlistTitle.isEmpty()) {
request.addOption("--parse-metadata", "%(album,playlist,title)s:%(meta_album)s")
request.addOption("--parse-metadata", "%(track_number,playlist_index)d:%(meta_track)s")
} else {
request.addOption("--parse-metadata", "%(album,title)s:%(meta_album)s")
}
request.addOption("-o", tempFileDir.absolutePath + "/%(uploader)s - %(title)s.%(ext)s")
} else if (type == "video") {
if (downloadLocation.equals(getString(R.string.video_path))){
tempFileDir = File(getString(R.string.video_path))
}
val addChapters = sharedPreferences.getBoolean("add_chapters", false)
if (addChapters) {
request.addOption("--sponsorblock-mark", "all")
}
val embedSubs = sharedPreferences.getBoolean("embed_subtitles", false)
if (embedSubs) {
request.addOption("--embed-subs", "")
}
var videoQuality = video.videoQuality
if (videoQuality == null) videoQuality =
sharedPreferences.getString("video_quality", "")
var formatArgument = "bestvideo+bestaudio/best"
if (videoQuality == "Worst Quality") {
formatArgument = "worst"
} else if (!videoQuality!!.isEmpty() && videoQuality != "Best Quality") {
formatArgument = "bestvideo[height<=" + videoQuality.substring(
0,
videoQuality.length - 1
) + "]+bestaudio/best"
}
request.addOption("-f", formatArgument)
var format = video.videoFormat
if (format == null) format = sharedPreferences.getString("video_format", "")
if (format != "DEFAULT") request.addOption("--merge-output-format", format!!)
if (format != "webm") {
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
if (embedThumb) {
request.addOption("--embed-thumbnail")
}
}
request.addOption("-o", tempFileDir.absolutePath + "/%(uploader)s - %(title)s.%(ext)s")
}
val disposable = Observable.fromCallable {
YoutubeDL.getInstance().execute(request, downloadProcessID, callback)
}
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
val workTag = video.getURL()
val workData = workDataOf(
originDir to tempFileDir.absolutePath,
downLocation to downloadLocation,
title to video.title
)
val fileTransferWorkRequest = OneTimeWorkRequestBuilder<FileTransferWorker>()
.addTag(workTag)
.setInputData(workData)
.build()
workManager.enqueueUniqueWork(
workTag,
ExistingWorkPolicy.KEEP,
fileTransferWorkRequest
)
downloadInfo.downloadPath = fileUtil.formatPath(getDownloadLocation(type)!!)
Log.e(TAG, downloadInfo.downloadPath)
downloadInfo.downloadType = type
try {
for (activity in activities.keys) {
activity.runOnUiThread {
for (i in activities[activity]!!.indices) {
val callback = activities[activity]!![i]
callback.onDownloadEnd(downloadInfo)
}
}
}
} catch (e: Exception) {
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
e.printStackTrace()
}
// SCAN NEXT IN QUEUE
videos.remove()
downloadInfo.downloadQueue = videos
startDownload(videos)
}) { e: Throwable? ->
tempFileDir.delete()
if (BuildConfig.DEBUG) {
Toast.makeText(context, e!!.message, Toast.LENGTH_LONG).show()
Log.e(TAG, getString(R.string.failed_download), e)
}
notificationUtil.updateDownloadNotification(
NotificationUtil.DOWNLOAD_NOTIFICATION_ID,
getString(R.string.failed_download), 0, 0, downloadQueue.peek()?.title,
NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
)
downloadInfo.downloadType = type
try {
for (activity in activities.keys) {
activity.runOnUiThread {
for (i in activities[activity]!!.indices) {
val callback = activities[activity]!![i]
callback.onDownloadError(downloadInfo)
}
}
}
} catch (err: Exception) {
err.printStackTrace()
}
// SCAN NEXT IN QUEUE
videos.remove()
downloadInfo.downloadQueue = videos
startDownload(videos)
}
compositeDisposable.add(disposable)
private fun startDownload(videos: LinkedList<ResultItem>) {
// if (videos.size == 0) {
// finishService()
// return
// }
// val video: ResultItem = try {
// videos.peek() as ResultItem
// } catch (e: Exception) {
// finishService()
// return
// }
// try {
// for (activity in activities.keys) {
// activity.runOnUiThread {
// for (i in activities[activity]!!.indices) {
// val callback = activities[activity]!![i]
// callback.onDownloadStart(downloadInfo)
// }
// }
// }
// } catch (err: Exception) {
// err.printStackTrace()
// }
// val url = video.getURL()
// val request = YoutubeDLRequest(url)
// val type = video.downloadedType
// val downloadLocation = getDownloadLocation(type)
//
// var tempFileDir = File(cacheDir.absolutePath + """/${video.title}##${video.downloadedType}""")
// tempFileDir.delete()
// tempFileDir.mkdir()
//
// val sharedPreferences = context!!.getSharedPreferences("root_preferences", MODE_PRIVATE)
// val aria2 = sharedPreferences.getBoolean("aria2", false)
// if (aria2) {
// request.addOption("--downloader", "libaria2c.so")
// request.addOption("--external-downloader-args", "aria2c:\"--summary-interval=1\"")
// } else {
// val concurrentFragments = sharedPreferences.getInt("concurrent_fragments", 1)
// if (concurrentFragments > 1) request.addOption("-N", concurrentFragments)
// }
// val limitRate = sharedPreferences.getString("limit_rate", "")
// if (limitRate != "") request.addOption("-r", limitRate!!)
// val writeThumbnail = sharedPreferences.getBoolean("write_thumbnail", false)
// if (writeThumbnail) {
// request.addOption("--write-thumbnail")
// request.addOption("--convert-thumbnails", "png")
// }
// request.addOption("--no-mtime")
// val sponsorBlockFilters = sharedPreferences.getStringSet("sponsorblock_filters", emptySet())
// if (sponsorBlockFilters!!.isNotEmpty()) {
// val filters = java.lang.String.join(",", sponsorBlockFilters)
// request.addOption("--sponsorblock-remove", filters)
// }
// if (type == "audio") {
// if (downloadLocation.equals(getString(R.string.music_path))){
// tempFileDir = File(getString(R.string.music_path))
// }
//
// request.addOption("-x")
// var format = video.audioFormat
// if (format == null) format = sharedPreferences.getString("audio_format", "")
// request.addOption("--audio-format", format!!)
// request.addOption("--embed-metadata")
//
// val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
// if (embedThumb) {
// request.addOption("--embed-thumbnail")
// request.addOption("--convert-thumbnails", "png")
// try {
// val config = File(cacheDir, "config" + video.videoId + ".txt")
// val config_data =
// "--ppa \"ffmpeg: -c:v png -vf crop=\\\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\\\"\""
// val stream = FileOutputStream(config)
// stream.write(config_data.toByteArray())
// stream.close()
// request.addOption("--config", config.absolutePath)
// } catch (ignored: java.lang.Exception) {
// }
// }
// request.addOption("--parse-metadata", "%(release_year,upload_date)s:%(meta_date)s")
// request.addCommands(Arrays.asList("--replace-in-metadata", "title", ".*.", video.title))
// request.addCommands(Arrays.asList("--replace-in-metadata", "uploader", ".*.", video.author))
//
// if (!video.playlistTitle.isEmpty()) {
// request.addOption("--parse-metadata", "%(album,playlist,title)s:%(meta_album)s")
// request.addOption("--parse-metadata", "%(track_number,playlist_index)d:%(meta_track)s")
// } else {
// request.addOption("--parse-metadata", "%(album,title)s:%(meta_album)s")
// }
// request.addOption("-o", tempFileDir.absolutePath + "/%(uploader)s - %(title)s.%(ext)s")
// } else if (type == "video") {
// if (downloadLocation.equals(getString(R.string.video_path))){
// tempFileDir = File(getString(R.string.video_path))
// }
//
// val addChapters = sharedPreferences.getBoolean("add_chapters", false)
// if (addChapters) {
// request.addOption("--sponsorblock-mark", "all")
// }
// val embedSubs = sharedPreferences.getBoolean("embed_subtitles", false)
// if (embedSubs) {
// request.addOption("--embed-subs", "")
// }
// var videoQuality = video.videoQuality
// if (videoQuality == null) videoQuality =
// sharedPreferences.getString("video_quality", "")
// var formatArgument = "bestvideo+bestaudio/best"
// if (videoQuality == "Worst Quality") {
// formatArgument = "worst"
// } else if (!videoQuality!!.isEmpty() && videoQuality != "Best Quality") {
// formatArgument = "bestvideo[height<=" + videoQuality.substring(
// 0,
// videoQuality.length - 1
// ) + "]+bestaudio/best"
// }
// request.addOption("-f", formatArgument)
// var format = video.videoFormat
// if (format == null) format = sharedPreferences.getString("video_format", "")
// if (format != "DEFAULT") request.addOption("--merge-output-format", format!!)
// if (format != "webm") {
// val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
// if (embedThumb) {
// request.addOption("--embed-thumbnail")
// }
// }
// request.addOption("-o", tempFileDir.absolutePath + "/%(uploader)s - %(title)s.%(ext)s")
// }
//
// val disposable = Observable.fromCallable {
// YoutubeDL.getInstance().execute(request, downloadProcessID, callback)
// }
// .subscribeOn(Schedulers.newThread())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe({
// val workTag = video.getURL()
// val workData = workDataOf(
// originDir to tempFileDir.absolutePath,
// downLocation to downloadLocation,
// title to video.title
// )
// val fileTransferWorkRequest = OneTimeWorkRequestBuilder<FileTransferWorker>()
// .addTag(workTag)
// .setInputData(workData)
// .build()
// workManager.enqueueUniqueWork(
// workTag,
// ExistingWorkPolicy.KEEP,
// fileTransferWorkRequest
// )
//
// downloadInfo.downloadPath = fileUtil.formatPath(getDownloadLocation(type)!!)
// Log.e(TAG, downloadInfo.downloadPath)
// downloadInfo.downloadType = type
//
//
//
// try {
// for (activity in activities.keys) {
// activity.runOnUiThread {
// for (i in activities[activity]!!.indices) {
// val callback = activities[activity]!![i]
// callback.onDownloadEnd(downloadInfo)
// }
// }
// }
// } catch (e: Exception) {
// Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
// e.printStackTrace()
// }
//
// // SCAN NEXT IN QUEUE
// videos.remove()
// downloadInfo.downloadQueue = videos
// startDownload(videos)
// }) { e: Throwable? ->
// tempFileDir.delete()
// if (BuildConfig.DEBUG) {
// Toast.makeText(context, e!!.message, Toast.LENGTH_LONG).show()
// Log.e(TAG, getString(R.string.failed_download), e)
// }
// notificationUtil.updateDownloadNotification(
// NotificationUtil.DOWNLOAD_NOTIFICATION_ID,
// getString(R.string.failed_download), 0, 0, downloadQueue.peek()?.title,
// NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
// )
// downloadInfo.downloadType = type
// try {
// for (activity in activities.keys) {
// activity.runOnUiThread {
// for (i in activities[activity]!!.indices) {
// val callback = activities[activity]!![i]
// callback.onDownloadError(downloadInfo)
// }
// }
// }
// } catch (err: Exception) {
// err.printStackTrace()
// }
//
// // SCAN NEXT IN QUEUE
// videos.remove()
// downloadInfo.downloadQueue = videos
// startDownload(videos)
// }
// compositeDisposable.add(disposable)
}
private fun startCommandDownload(text: String?) {

View file

@ -19,7 +19,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import com.deniscerri.ytdlnis.DownloaderService.LocalBinder
import com.deniscerri.ytdlnis.database.DatabaseManager
import com.deniscerri.ytdlnis.database.Video
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.databinding.ActivityMainBinding
import com.deniscerri.ytdlnis.ui.DownloadsFragment
import com.deniscerri.ytdlnis.ui.HomeFragment
@ -193,7 +193,6 @@ class MainActivity : AppCompatActivity() {
.commit()
lastFragment = homeFragment
listeners = ArrayList()
listeners.add(homeFragment.listener)
}
private fun replaceFragment(f: Fragment) {
@ -202,37 +201,38 @@ class MainActivity : AppCompatActivity() {
}
fun startDownloadService(
downloadQueue: ArrayList<Video>,
downloadQueue: ArrayList<ResultItem>,
awaitingListener: IDownloaderListener
) {
addQueueToDownloads(downloadQueue)
if (isDownloadServiceRunning) {
iDownloaderService?.updateQueue(downloadQueue)
return
}
if (!listeners.contains(awaitingListener)) listeners.add(awaitingListener)
val serviceIntent = Intent(context, DownloaderService::class.java)
serviceIntent.putParcelableArrayListExtra("queue", downloadQueue)
context.applicationContext.startService(serviceIntent)
context.applicationContext.bindService(serviceIntent, serviceConnection, BIND_AUTO_CREATE)
// addQueueToDownloads(downloadQueue)
// if (isDownloadServiceRunning) {
// iDownloaderService?.updateQueue(downloadQueue)
// return
// }
// if (!listeners.contains(awaitingListener)) listeners.add(awaitingListener)
// val serviceIntent = Intent(context, DownloaderService::class.java)
// serviceIntent.putParcelableArrayListExtra("queue", downloadQueue)
// context.applicationContext.startService(serviceIntent)
// context.applicationContext.bindService(serviceIntent, serviceConnection, BIND_AUTO_CREATE)
}
private fun addQueueToDownloads(downloadQueue: ArrayList<Video>) {
try {
val sharedPreferences = context.getSharedPreferences("root_preferences", MODE_PRIVATE)
if (!sharedPreferences.getBoolean("incognito", false)) {
val databaseManager = DatabaseManager(context)
for (i in downloadQueue.indices.reversed()) {
val v = downloadQueue[i]
v.isQueuedDownload = true
databaseManager.addToHistory(v)
}
databaseManager.close()
//downloadsFragment.initCards()
}
} catch (e: Exception) {
e.printStackTrace()
}
private fun addQueueToDownloads(downloadQueue: ArrayList<ResultItem>) {
// try {
// val sharedPreferences = context.getSharedPreferences("root_preferences", MODE_PRIVATE)
// if (!sharedPreferences.getBoolean("incognito", false)) {
// val databaseManager = DatabaseManager(context)
// for (i in downloadQueue.indices.reversed()) {
// val v = downloadQueue[i]
// v.isQueuedDownload = true
// databaseManager.addToHistory(v)
// }
// databaseManager.close()
// //downloadsFragment.initCards()
// }
// } catch (e: Exception) {
// e.printStackTrace()
// }
}
fun stopDownloadService() {
@ -257,7 +257,7 @@ class MainActivity : AppCompatActivity() {
stopDownloadService()
}
fun removeItemFromDownloadQueue(video: Video?, type: String?) {
fun removeItemFromDownloadQueue(video: ResultItem?, type: String?) {
iDownloaderService!!.removeItemFromDownloadQueue(video, type)
}

View file

@ -84,7 +84,6 @@ public class HistoryAdapter extends ListAdapter<HistoryItem, HistoryAdapter.View
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
HistoryItem video = getItem(position);
Log.e("AAA", String.valueOf(position));
MaterialCardView card = holder.cardView;
// THUMBNAIL ----------------------------------
ImageView thumbnail = card.findViewById(R.id.downloads_image_view);

View file

@ -4,6 +4,7 @@ import android.app.Activity;
import android.graphics.Color;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -12,10 +13,12 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import com.deniscerri.ytdlnis.R;
import com.deniscerri.ytdlnis.database.Video;
import com.deniscerri.ytdlnis.database.models.ResultItem;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.card.MaterialCardView;
import com.google.android.material.progressindicator.LinearProgressIndicator;
@ -23,19 +26,33 @@ import com.squareup.picasso.Picasso;
import java.util.ArrayList;
public class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.ViewHolder> {
private ArrayList<Video> videoList;
private ArrayList<Integer> checkedVideos;
public class HomeAdapter extends ListAdapter<ResultItem, HomeAdapter.ViewHolder> {
private final ArrayList<Integer> checkedVideos;
private final OnItemClickListener onItemClickListener;
private Activity activity;
public HomeAdapter(ArrayList<Video> videos, OnItemClickListener onItemClickListener, Activity activity){
this.videoList = videos;
public HomeAdapter(HomeAdapter.OnItemClickListener onItemClickListener, Activity activity){
super(DIFF_CALLBACK);
this.checkedVideos = new ArrayList<>();
this.onItemClickListener = onItemClickListener;
this.activity = activity;
Log.e("TAG", "adapter");
}
private static final DiffUtil.ItemCallback<ResultItem> DIFF_CALLBACK = new DiffUtil.ItemCallback<ResultItem>() {
@Override
public boolean areItemsTheSame(@NonNull ResultItem oldItem, @NonNull ResultItem newItem) {
Log.e("TAG", "adapter2");
return oldItem.getId() == newItem.getId();
}
@Override
public boolean areContentsTheSame(@NonNull ResultItem oldItem, @NonNull ResultItem newItem) {
Log.e("TAG", "adapter3");
return oldItem.getUrl().equals(newItem.getUrl());
}
};
@Override
public int getItemViewType(int position) {
return super.getItemViewType(position);
@ -61,8 +78,8 @@ public class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.ViewHolder> {
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Video video = videoList.get(position);
ResultItem video = getItem(position);
Log.e("TAG", "adapter4");
MaterialCardView card = holder.cardView;
// THUMBNAIL ----------------------------------
ImageView thumbnail = card.findViewById(R.id.result_image_view);
@ -97,7 +114,7 @@ public class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.ViewHolder> {
}
// BUTTONS ----------------------------------
String videoID = video.getVideoId();
int videoID = video.getId();
LinearLayout buttonLayout = card.findViewById(R.id.download_button_layout);
@ -116,39 +133,42 @@ public class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.ViewHolder> {
LinearProgressIndicator progressBar = card.findViewById(R.id.download_progress);
progressBar.setTag(videoID + "##progress");
progressBar.setProgress(0);
progressBar.setIndeterminate(true);
progressBar.setVisibility(View.GONE);
if (video.isDownloading()){
progressBar.setVisibility(View.VISIBLE);
}else {
progressBar.setProgress(0);
progressBar.setIndeterminate(true);
progressBar.setVisibility(View.GONE);
}
if (video.isDownloadingAudio()) {
musicBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_cancel));
musicBtn.setTag(R.id.cancelDownload, "true");
}else{
if(video.isAudioDownloaded() == 1){
musicBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_music_downloaded));
}else{
musicBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_music));
}
}
if (video.isDownloadingVideo()){
videoBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_cancel));
videoBtn.setTag(R.id.cancelDownload, "true");
}else{
if(video.isVideoDownloaded() == 1){
videoBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video_downloaded));
}else{
videoBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video));
}
}
// if (video.isDownloading()){
// progressBar.setVisibility(View.VISIBLE);
// }else {
// progressBar.setProgress(0);
// progressBar.setIndeterminate(true);
// progressBar.setVisibility(View.GONE);
// }
//
// if (video.isDownloadingAudio()) {
// musicBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_cancel));
// musicBtn.setTag(R.id.cancelDownload, "true");
// }else{
// if(video.isAudioDownloaded() == 1){
// musicBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_music_downloaded));
// }else{
// musicBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_music));
// }
// }
//
// if (video.isDownloadingVideo()){
// videoBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_cancel));
// videoBtn.setTag(R.id.cancelDownload, "true");
// }else{
// if(video.isVideoDownloaded() == 1){
// videoBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video_downloaded));
// }else{
// videoBtn.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_video));
// }
// }
if(checkedVideos.contains(position)){
if(checkedVideos.contains(videoID)){
card.setChecked(true);
card.setStrokeWidth(5);
}else{
@ -158,41 +178,32 @@ public class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.ViewHolder> {
card.setTag(videoID + "##card");
card.setOnLongClickListener(view -> {
checkCard(card, position);
checkCard(card, videoID);
return true;
});
card.setOnClickListener(view -> {
if(checkedVideos.size() > 0){
checkCard(card, position);
checkCard(card, videoID);
}
});
}
private void checkCard(MaterialCardView card, int position){
private void checkCard(MaterialCardView card, int videoID){
if(card.isChecked()){
card.setStrokeWidth(0);
checkedVideos.remove(Integer.valueOf(position));
checkedVideos.remove(Integer.valueOf(videoID));
}else{
card.setStrokeWidth(5);
checkedVideos.add(position);
checkedVideos.add(videoID);
}
card.setChecked(!card.isChecked());
onItemClickListener.onCardClick(position, card.isChecked());
onItemClickListener.onCardClick(videoID, card.isChecked());
}
@Override
public int getItemCount() {
return videoList.size();
}
public interface OnItemClickListener {
void onButtonClick(int position, String type);
void onCardClick(int position, boolean add);
}
public void updateVideoListItem(Video v, int position){
videoList.set(position, v);
notifyItemChanged(position);
void onButtonClick(int videoID, String type);
void onCardClick(int videoID, boolean add);
}
public void clearCheckedVideos(){
@ -203,16 +214,4 @@ public class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.ViewHolder> {
}
checkedVideos.clear();
}
public void clear(){
int size = videoList.size();
videoList = new ArrayList<>();
notifyItemRangeRemoved(0, size);
}
public void add(ArrayList<Video> vids){
int position = videoList.size() + 1;
videoList.addAll(vids);
notifyItemRangeInserted(position, vids.size());
}
}

View file

@ -9,6 +9,8 @@ import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;
import com.deniscerri.ytdlnis.R;
import com.deniscerri.ytdlnis.database.models.HistoryItem;
import com.deniscerri.ytdlnis.database.models.ResultItem;
import java.io.File;
import java.util.ArrayList;
@ -90,63 +92,63 @@ public class DatabaseManager extends SQLiteOpenHelper {
}
@SuppressLint("Range")
public void clearHistoryItem(Video video, boolean delete_file){
SQLiteDatabase db = this.getWritableDatabase();
if (delete_file){
try{
Cursor cursor = db.rawQuery("SELECT * FROM " + history_table_name + " WHERE url='" +
video.getURL() + "' AND type='"+video.getDownloadedType() + "' LIMIT 1", null);
if(cursor.moveToFirst()){
String path = cursor.getString(cursor.getColumnIndex(downloadPath));
File file = new File(path);
if(file.exists()){
file.delete();
}
}
}catch (Exception e){
Toast.makeText(context, R.string.error_deleting_file, Toast.LENGTH_SHORT).show();
}
}
db.execSQL("DELETE FROM " + history_table_name + " WHERE id=" + video.getId());
String where = "";
switch(video.getDownloadedType()){
case "audio":
where = " SET downloadedAudio=0 WHERE downloadedAudio=1 AND videoId='" + video.getVideoId()+"'";
break;
case "video":
where = " SET downloadedVideo=0 WHERE downloadedVideo=1 AND videoId='" + video.getVideoId()+"'";
break;
}
//remove downloaded status from results
db.execSQL("UPDATE "+results_table_name+ where);
public void clearHistoryItem(HistoryItem video, boolean delete_file){
// SQLiteDatabase db = this.getWritableDatabase();
//
// if (delete_file){
// try{
// Cursor cursor = db.rawQuery("SELECT * FROM " + history_table_name + " WHERE url='" +
// video.getURL() + "' AND type='"+video.getDownloadedType() + "' LIMIT 1", null);
//
// if(cursor.moveToFirst()){
// String path = cursor.getString(cursor.getColumnIndex(downloadPath));
// File file = new File(path);
// if(file.exists()){
// file.delete();
// }
// }
// }catch (Exception e){
// Toast.makeText(context, R.string.error_deleting_file, Toast.LENGTH_SHORT).show();
// }
// }
//
//
// db.execSQL("DELETE FROM " + history_table_name + " WHERE id=" + video.getId());
//
// String where = "";
// switch(video.getDownloadedType()){
// case "audio":
// where = " SET downloadedAudio=0 WHERE downloadedAudio=1 AND videoId='" + video.getVideoId()+"'";
// break;
// case "video":
// where = " SET downloadedVideo=0 WHERE downloadedVideo=1 AND videoId='" + video.getVideoId()+"'";
// break;
// }
//
// //remove downloaded status from results
// db.execSQL("UPDATE "+results_table_name+ where);
}
public void clearDeletedHistory(){
ArrayList<Video> videos = getHistory("","","","");
for (int i = 0; i < videos.size(); i++){
Video video = videos.get(i);
String path = video.getDownloadPath();
File file = new File(path);
if(!file.exists() && !path.isEmpty()){
clearHistoryItem(video, false);
}
}
// ArrayList<ResultItem> videos = getHistory("","","","");
// for (int i = 0; i < videos.size(); i++){
// ResultItem video = videos.get(i);
// String path = video.getDownloadPath();
// File file = new File(path);
// if(!file.exists() && !path.isEmpty()){
// clearHistoryItem(video, false);
// }
// }
}
public void clearDownloadingHistory(){
ArrayList<Video> videos = getHistory("","","","");
for (int i = 0; i < videos.size(); i++){
Video video = videos.get(i);
if(video.isQueuedDownload()){
clearHistoryItem(video, false);
}
}
// ArrayList<ResultItem> videos = getHistory("","","","");
// for (int i = 0; i < videos.size(); i++){
// ResultItem video = videos.get(i);
// if(video.isQueuedDownload()){
// clearHistoryItem(video, false);
// }
// }
}
public void clearDuplicateHistory(){
@ -176,36 +178,36 @@ public class DatabaseManager extends SQLiteOpenHelper {
@SuppressLint("Range")
public ArrayList<Video> getResults(){
public ArrayList<ResultItem> getResults(){
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT * FROM " + results_table_name, null);
ArrayList<Video> list = new ArrayList<>();
ArrayList<ResultItem> list = new ArrayList<>();
if(cursor.moveToFirst()){
do {
// on below line we are adding the data from cursor to our array list.
list.add(new Video(cursor.getString(cursor.getColumnIndex(videoId)),
cursor.getString(cursor.getColumnIndex(url)),
cursor.getString(cursor.getColumnIndex(title)),
cursor.getString(cursor.getColumnIndex(author)),
cursor.getString(cursor.getColumnIndex(duration)),
cursor.getString(cursor.getColumnIndex(thumb)),
cursor.getInt(cursor.getColumnIndex(downloadedAudio)),
cursor.getInt(cursor.getColumnIndex(downloadedVideo)),
cursor.getInt(cursor.getColumnIndex(isPlaylistItem)),
cursor.getString(cursor.getColumnIndex(website)),
cursor.getInt(cursor.getColumnIndex(downloadedAudio)),
cursor.getInt(cursor.getColumnIndex(downloadingVideo)),
cursor.getString(cursor.getColumnIndex(playlistTitle))));
} while (cursor.moveToNext());
}
cursor.close();
// if(cursor.moveToFirst()){
// do {
// // on below line we are adding the data from cursor to our array list.
// list.add(new ResultItem(cursor.getString(cursor.getColumnIndex(videoId)),
// cursor.getString(cursor.getColumnIndex(url)),
// cursor.getString(cursor.getColumnIndex(title)),
// cursor.getString(cursor.getColumnIndex(author)),
// cursor.getString(cursor.getColumnIndex(duration)),
// cursor.getString(cursor.getColumnIndex(thumb)),
// cursor.getInt(cursor.getColumnIndex(downloadedAudio)),
// cursor.getInt(cursor.getColumnIndex(downloadedVideo)),
// cursor.getInt(cursor.getColumnIndex(isPlaylistItem)),
// cursor.getString(cursor.getColumnIndex(website)),
// cursor.getInt(cursor.getColumnIndex(downloadedAudio)),
// cursor.getInt(cursor.getColumnIndex(downloadingVideo)),
// cursor.getString(cursor.getColumnIndex(playlistTitle))));
// } while (cursor.moveToNext());
// }
//
// cursor.close();
return list;
}
@SuppressLint("Range")
public ArrayList<Video> getHistory(String query, String format, String site, String sort){
public ArrayList<ResultItem> getHistory(String query, String format, String site, String sort){
SQLiteDatabase db = this.getReadableDatabase();
if (sort == null || sort.isEmpty()) sort = "DESC";
@ -215,91 +217,91 @@ public class DatabaseManager extends SQLiteOpenHelper {
" AND website LIKE '%"+site+"%'"+
" ORDER BY id "+sort,
null);
ArrayList<Video> list = new ArrayList<>();
if(cursor.moveToFirst()){
do {
// on below line we are adding the data from cursor to our array list.
list.add(new Video(cursor.getInt(cursor.getColumnIndex(id)),
cursor.getString(cursor.getColumnIndex(url)),
cursor.getString(cursor.getColumnIndex(title)),
cursor.getString(cursor.getColumnIndex(author)),
cursor.getString(cursor.getColumnIndex(duration)),
cursor.getString(cursor.getColumnIndex(thumb)),
cursor.getString(cursor.getColumnIndex(type)),
cursor.getString(cursor.getColumnIndex(time)),
cursor.getString(cursor.getColumnIndex(downloadPath)),
cursor.getString(cursor.getColumnIndex(website)),
cursor.getInt(cursor.getColumnIndex(isQueuedDownload))));
} while (cursor.moveToNext());
}
cursor.close();
ArrayList<ResultItem> list = new ArrayList<>();
//
// if(cursor.moveToFirst()){
// do {
// // on below line we are adding the data from cursor to our array list.
// list.add(new ResultItem(cursor.getInt(cursor.getColumnIndex(id)),
// cursor.getString(cursor.getColumnIndex(url)),
// cursor.getString(cursor.getColumnIndex(title)),
// cursor.getString(cursor.getColumnIndex(author)),
// cursor.getString(cursor.getColumnIndex(duration)),
// cursor.getString(cursor.getColumnIndex(thumb)),
// cursor.getString(cursor.getColumnIndex(type)),
// cursor.getString(cursor.getColumnIndex(time)),
// cursor.getString(cursor.getColumnIndex(downloadPath)),
// cursor.getString(cursor.getColumnIndex(website)),
// cursor.getInt(cursor.getColumnIndex(isQueuedDownload))));
// } while (cursor.moveToNext());
// }
//
// cursor.close();
return list;
}
public void addToResults(ArrayList<Video> videot){
public void addToResults(ArrayList<ResultItem> videot){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
for(Video v : videot){
values.put(videoId, v.getVideoId());
values.put(url, v.getURL());
values.put(title, v.getTitle());
values.put(author, v.getAuthor());
values.put(duration, v.getDuration());
values.put(thumb, v.getThumb());
values.put(downloadedAudio, v.isAudioDownloaded());
values.put(downloadedVideo, v.isVideoDownloaded());
values.put(isPlaylistItem, v.getIsPlaylistItem());
values.put(website, v.getWebsite());
values.put(downloadingAudio, 0);
values.put(downloadingVideo, 0);
values.put(playlistTitle, v.getPlaylistTitle());
db.insert(results_table_name, null, values);
}
db.close();
// for(ResultItem v : videot){
// values.put(videoId, v.getVideoId());
// values.put(url, v.getURL());
// values.put(title, v.getTitle());
// values.put(author, v.getAuthor());
// values.put(duration, v.getDuration());
// values.put(thumb, v.getThumb());
// values.put(downloadedAudio, v.isAudioDownloaded());
// values.put(downloadedVideo, v.isVideoDownloaded());
// values.put(isPlaylistItem, v.getIsPlaylistItem());
// values.put(website, v.getWebsite());
// values.put(downloadingAudio, 0);
// values.put(downloadingVideo, 0);
// values.put(playlistTitle, v.getPlaylistTitle());
//
// db.insert(results_table_name, null, values);
// }
//
// db.close();
}
public void addToHistory(Video v){
public void addToHistory(ResultItem v){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(url, v.getURL());
values.put(title, v.getTitle());
values.put(author, v.getAuthor());
values.put(duration, v.getDuration());
values.put(thumb, v.getThumb());
values.put(type, v.getDownloadedType());
values.put(time, v.getDownloadedTime());
values.put(downloadPath, v.getDownloadPath());
values.put(website, v.getWebsite());
values.put(isQueuedDownload, (v.isQueuedDownload()) ? 1 : 0);
db.insert(history_table_name, null, values);
db.close();
//
// values.put(url, v.getURL());
// values.put(title, v.getTitle());
// values.put(author, v.getAuthor());
// values.put(duration, v.getDuration());
// values.put(thumb, v.getThumb());
// values.put(type, v.getDownloadedType());
// values.put(time, v.getDownloadedTime());
// values.put(downloadPath, v.getDownloadPath());
// values.put(website, v.getWebsite());
// values.put(isQueuedDownload, (v.isQueuedDownload()) ? 1 : 0);
//
// db.insert(history_table_name, null, values);
// db.close();
}
public void updateHistoryItem(Video v){
public void updateHistoryItem(ResultItem v){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(url, v.getURL());
values.put(title, v.getTitle());
values.put(author, v.getAuthor());
values.put(duration, v.getDuration());
values.put(thumb, v.getThumb());
values.put(type, v.getDownloadedType());
values.put(time, v.getDownloadedTime());
values.put(downloadPath, v.getDownloadPath());
values.put(website, v.getWebsite());
values.put(isQueuedDownload, (v.isQueuedDownload()) ? 1 : 0);
String where = url + "='"+v.getURL()+"' AND "+isQueuedDownload+"=1 AND "+type+"='"+v.getDownloadedType()+"'";
db.update(history_table_name, values, where, null);
db.close();
// values.put(url, v.getURL());
// values.put(title, v.getTitle());
// values.put(author, v.getAuthor());
// values.put(duration, v.getDuration());
// values.put(thumb, v.getThumb());
// values.put(type, v.getDownloadedType());
// values.put(time, v.getDownloadedTime());
// values.put(downloadPath, v.getDownloadPath());
// values.put(website, v.getWebsite());
// values.put(isQueuedDownload, (v.isQueuedDownload()) ? 1 : 0);
//
// String where = url + "='"+v.getURL()+"' AND "+isQueuedDownload+"=1 AND "+type+"='"+v.getDownloadedType()+"'";
// db.update(history_table_name, values, where, null);
// db.close();
}
public void updateDownloadStatusOnResult(String id, String type, boolean downloaded){

View file

@ -1,338 +0,0 @@
package com.deniscerri.ytdlnis.database;
import android.os.Parcel;
import android.os.Parcelable;
public class Video implements Parcelable, Cloneable {
private int id;
private String videoId;
private String title;
private String author;
private String duration;
private String thumb;
private String url;
private String downloadedType;
private int downloadedAudio;
private int downloadedVideo;
private String downloadedTime;
private int isPlaylistItem;
private String downloadPath;
private String website;
private boolean downloadingAudio;
private boolean downloadingVideo;
private boolean queuedDownload;
private String playlistTitle;
private String audioFormat;
private String videoFormat;
private String videoQuality;
// RESULTS OBJECT
public Video(String videoId, String url, String title, String author, String duration, String thumb,
int downloadedAudio, int downloadedVideo, int isPlaylistItem, String website, int downloadingAudio, int downloadingVideo, String playlistTitle) {
this.videoId = videoId;
this.url = url;
this.title = title;
this.author = author;
this.duration = duration;
this.thumb = thumb;
this.downloadedVideo = downloadedVideo;
this.downloadedAudio = downloadedAudio;
this.isPlaylistItem = isPlaylistItem;
this.website = website;
this.downloadingAudio = intToBoolean(downloadingAudio);
this.downloadingVideo = intToBoolean(downloadingVideo);
this.playlistTitle = playlistTitle;
}
//HISTORY OBJECT
public Video(int id, String url, String title, String author, String duration, String thumb,
String downloadedType, String downloadedTime, String downloadPath, String website, int queued) {
this.id = id;
this.url = url;
this.title = title;
this.author = author;
this.duration = duration;
this.thumb = thumb;
this.downloadedType = downloadedType;
this.downloadedTime = downloadedTime;
this.downloadPath = downloadPath;
this.website = website;
this.queuedDownload = (queued == 1);
}
private boolean intToBoolean(int i){
return i == 1;
}
protected Video(Parcel in) {
id = in.readInt();
videoId = in.readString();
title = in.readString();
author = in.readString();
duration = in.readString();
thumb = in.readString();
url = in.readString();
downloadedType = in.readString();
downloadedAudio = in.readInt();
downloadedVideo = in.readInt();
downloadedTime = in.readString();
isPlaylistItem = in.readInt();
downloadPath = in.readString();
website = in.readString();
playlistTitle = in.readString();
audioFormat = in.readString();
videoFormat = in.readString();
videoQuality = in.readString();
}
public static final Creator<Video> CREATOR = new Creator<Video>() {
@Override
public Video createFromParcel(Parcel in) {
return new Video(in);
}
@Override
public Video[] newArray(int size) {
return new Video[size];
}
};
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getVideoId() {
return videoId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public String getDuration() {
return duration;
}
public String getThumb() {
return thumb;
}
public String getDownloadedType() {
return downloadedType;
}
public void setDownloadedType(String downloadedType) {
this.downloadedType = downloadedType;
}
public String getDownloadedTime() {
return downloadedTime;
}
public void setDownloadedTime(String downloadedTime) {
this.downloadedTime = downloadedTime;
}
public int getIsPlaylistItem() {
return isPlaylistItem;
}
public void setIsPlaylistItem(int playlistItem) {
this.isPlaylistItem = playlistItem;
}
public int isAudioDownloaded() {
return downloadedAudio;
}
public int isVideoDownloaded() {
return downloadedVideo;
}
public String getURL(){ return url; }
public void setURL(String url) { this.url = url;}
public String getWebsite() { return website; }
public void setWebsite(String site){ this.website = site; }
public void setVideoId(String videoId) {
this.videoId = videoId;
}
public void setAuthor(String author) {
this.author = author;
}
public void setDuration(String duration) {
this.duration = duration;
}
public void setThumb(String thumb) {
this.thumb = thumb;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getDownloadedAudio() {
return downloadedAudio;
}
public void setDownloadedAudio(int downloadedAudio) {
this.downloadedAudio = downloadedAudio;
}
public int getDownloadedVideo() {
return downloadedVideo;
}
public void setDownloadedVideo(int downloadedVideo) {
this.downloadedVideo = downloadedVideo;
}
public String getDownloadPath() {
return downloadPath;
}
public void setDownloadPath(String downloadPath) {
this.downloadPath = downloadPath;
}
public boolean isDownloadingAudio() {
return downloadingAudio;
}
public void setDownloadingAudio(boolean downloadingAudio) {
this.downloadingAudio = downloadingAudio;
}
public boolean isDownloadingVideo() {
return downloadingVideo;
}
public void setDownloadingVideo(boolean downloadingVideo) {
this.downloadingVideo = downloadingVideo;
}
public boolean isDownloading() {
return this.downloadingAudio || this.downloadingVideo;
}
public String getPlaylistTitle() {
return playlistTitle;
}
public void setPlaylistTitle(String playlistTitle) {
this.playlistTitle = playlistTitle;
}
public boolean isQueuedDownload() {
return queuedDownload;
}
public void setQueuedDownload(boolean queuedDownload) {
this.queuedDownload = queuedDownload;
}
public String getAudioFormat() {
return audioFormat;
}
public void setAudioFormat(String audioFormat) {
this.audioFormat = audioFormat;
}
public String getVideoFormat() {
return videoFormat;
}
public void setVideoFormat(String videoFormat) {
this.videoFormat = videoFormat;
}
public String getVideoQuality() {
return videoQuality;
}
public void setVideoQuality(String videoQuality) {
this.videoQuality = videoQuality;
}
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(id);
parcel.writeString(videoId);
parcel.writeString(title);
parcel.writeString(author);
parcel.writeString(duration);
parcel.writeString(thumb);
parcel.writeString(url);
parcel.writeString(downloadedType);
parcel.writeInt(downloadedAudio);
parcel.writeInt(downloadedVideo);
parcel.writeString(downloadedTime);
parcel.writeInt(isPlaylistItem);
parcel.writeString(downloadPath);
parcel.writeString(website);
parcel.writeString(playlistTitle);
parcel.writeString(audioFormat);
parcel.writeString(videoFormat);
parcel.writeString(videoQuality);
}
@Override
public String toString() {
return "Video{" +
"id=" + id +
", videoId='" + videoId + '\'' +
", title='" + title + '\'' +
", author='" + author + '\'' +
", duration='" + duration + '\'' +
", thumb='" + thumb + '\'' +
", url='" + url + '\'' +
", downloadedType='" + downloadedType + '\'' +
", downloadedAudio=" + downloadedAudio +
", downloadedVideo=" + downloadedVideo +
", downloadedTime='" + downloadedTime + '\'' +
", isPlaylistItem=" + isPlaylistItem +
", downloadPath='" + downloadPath + '\'' +
", website='" + website + '\'' +
", downloadingAudio=" + downloadingAudio +
", downloadingVideo=" + downloadingVideo +
", queuedDownload=" + queuedDownload +
", playlistTitle='" + playlistTitle + '\'' +
", audioFormat='" + audioFormat + '\'' +
", videoFormat='" + videoFormat + '\'' +
", videoQuality='" + videoQuality + '\'' +
'}';
}
}

View file

@ -10,7 +10,7 @@ import com.deniscerri.ytdlnis.util.FileUtil
interface DownloadDao {
@Query("SELECT * FROM downloads")
fun getAllDownloads() : List<DownloadItem>
fun getAllDownloads() : LiveData<List<DownloadItem>>
@Query("SELECT * FROM downloads WHERE status='Queued'")
fun getQueuedDownloads() : List<DownloadItem>

View file

@ -4,10 +4,11 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "downloads")
@Entity(tableName = "commandTemplates")
data class CommandTemplate(
@PrimaryKey(autoGenerate = true)
val id: Int,
val title: String,
val content: String
)
){
@PrimaryKey(autoGenerate = true)
var id: Int? = null
}

View file

@ -6,8 +6,6 @@ import androidx.room.PrimaryKey
@Entity(tableName = "downloads")
data class DownloadItem(
@PrimaryKey(autoGenerate = true)
val id: Int,
val url: String,
val title: String,
val author: String,
@ -28,4 +26,7 @@ data class DownloadItem(
val playlistTitle: String,
@ColumnInfo(defaultValue = "Queued")
var status: String
)
){
@PrimaryKey(autoGenerate = true)
var id: Int? = null
}

View file

@ -8,13 +8,14 @@ import com.yausername.youtubedl_android.mapper.VideoInfo
@Entity(tableName = "results")
data class ResultItem(
@PrimaryKey(autoGenerate = true)
val id: Int,
val url: String,
val title: String,
val author: String,
val duration: String,
val thumb: String,
val website: String,
val playlistTitle: String
)
var playlistTitle: String
){
@PrimaryKey(autoGenerate = true)
var id: Int? = null
}

View file

@ -5,6 +5,7 @@ import com.deniscerri.ytdlnis.database.dao.DownloadDao
import com.deniscerri.ytdlnis.database.models.DownloadItem
class DownloadRepository(private val downloadDao: DownloadDao) {
val allDownloads : LiveData<List<DownloadItem>> = downloadDao.getAllDownloads()
suspend fun insert(item: DownloadItem){
downloadDao.insert(item)
@ -19,7 +20,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
}
suspend fun getQueuedDownloads() : List<DownloadItem>{
var items = downloadDao.getQueuedDownloads();
val items = downloadDao.getQueuedDownloads()
for (i in items){
i.status = "Downloading"
downloadDao.update(i)

View file

@ -30,7 +30,7 @@ class HistoryRepository(private val historyDao: HistoryDao) {
}
suspend fun delete(item: HistoryItem, deleteFile: Boolean){
historyDao.delete(item.id)
historyDao.delete(item.id!!)
if (deleteFile){
val fileUtil = FileUtil()
fileUtil.deleteFile(item.downloadPath)
@ -53,7 +53,7 @@ class HistoryRepository(private val historyDao: HistoryDao) {
val fileUtil = FileUtil()
items.value?.forEach { item ->
if (!fileUtil.exists(item.downloadPath)){
historyDao.delete(item.id)
historyDao.delete(item.id!!)
}
}
}

View file

@ -1,17 +1,26 @@
package com.deniscerri.ytdlnis.database.repository
import android.content.Context
import androidx.lifecycle.LiveData
import com.deniscerri.ytdlnis.database.dao.DownloadDao
import com.deniscerri.ytdlnis.database.dao.ResultDao
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.util.InfoUtil
class ResultRepository(private val resultDao: ResultDao) {
val allResults : LiveData<List<ResultItem>> = resultDao.getResults()
suspend fun insert(items: List<ResultItem>){
items.forEach {
resultDao.insert(it)
suspend fun insert(it: ResultItem){
resultDao.insert(it)
}
suspend fun updateTrending(context: Context){
resultDao.deleteAll();
val infoUtil = InfoUtil(context)
val items = infoUtil.getTrending(context)
for (i in items){
resultDao.insert(i!!);
}
}

View file

@ -3,27 +3,37 @@ package com.deniscerri.ytdlnis.database.viewmodel
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.viewModelScope
import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.HistoryItem
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.repository.ResultRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class ResultViewModel(application: Application) : AndroidViewModel(application) {
private val repository : ResultRepository
private val allResults : LiveData<List<ResultItem>>
val items : LiveData<List<ResultItem>>
init {
val dao = DBManager.getInstance(application).resultDao
repository = ResultRepository(dao)
allResults = repository.allResults
items = repository.allResults
}
fun insert(items: List<ResultItem>) = viewModelScope.launch(Dispatchers.IO){
repository.insert(items)
fun checkTrending() : Boolean{
return items.value?.get(0)?.playlistTitle.equals("ytdlnis-TRENDING");
}
fun getTrending() = viewModelScope.launch(Dispatchers.IO){
repository.updateTrending(getApplication<Application>().applicationContext);
}
fun insert(items: ArrayList<ResultItem?>) = viewModelScope.launch(Dispatchers.IO){
items.forEach {
repository.insert(it!!)
}
}
fun deleteAll() = viewModelScope.launch(Dispatchers.IO) {

View file

@ -1,13 +1,13 @@
package com.deniscerri.ytdlnis.service;
import com.deniscerri.ytdlnis.database.Video;
import com.deniscerri.ytdlnis.database.models.ResultItem;
import java.util.LinkedList;
public class DownloadInfo {
private Video video;
private ResultItem video;
private int progress;
private LinkedList<Video> downloadQueue;
private LinkedList<ResultItem> downloadQueue;
private String outputLine;
private String downloadStatus;
private String downloadPath;
@ -15,11 +15,11 @@ public class DownloadInfo {
public DownloadInfo(){}
public Video getVideo() {
public ResultItem getVideo() {
return video;
}
public void setVideo(Video video) {
public void setVideo(ResultItem video) {
this.video = video;
}
@ -31,11 +31,11 @@ public class DownloadInfo {
this.progress = progress;
}
public LinkedList<Video> getDownloadQueue() {
public LinkedList<ResultItem> getDownloadQueue() {
return downloadQueue;
}
public void setDownloadQueue(LinkedList<Video> downloadQueue) {
public void setDownloadQueue(LinkedList<ResultItem> downloadQueue) {
this.downloadQueue = downloadQueue;
this.video = downloadQueue.peek();
}

View file

@ -1,7 +1,5 @@
package com.deniscerri.ytdlnis.service;
import com.deniscerri.ytdlnis.database.Video;
public interface IDownloaderListener {
void onDownloadStart(DownloadInfo downloadInfo);
void onDownloadProgress(DownloadInfo downloadInfo);

View file

@ -1,14 +1,16 @@
package com.deniscerri.ytdlnis.service;
import android.app.Activity;
import com.deniscerri.ytdlnis.database.Video;
import com.deniscerri.ytdlnis.database.models.ResultItem;
import java.util.ArrayList;
public interface IDownloaderService {
DownloadInfo getInfo();
void addActivity(Activity activity, ArrayList<IDownloaderListener> callback);
void removeActivity(Activity activity);
void updateQueue(ArrayList<Video> queue);
void updateQueue(ArrayList<ResultItem> queue);
void cancelDownload(boolean cancelAll);
void removeItemFromDownloadQueue(Video video, String type);
void removeItemFromDownloadQueue(ResultItem video, String type);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,921 @@
package com.deniscerri.ytdlnis.ui
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.InputType
import android.util.Log
import android.view.*
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.*
import androidx.appcompat.widget.SearchView
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.adapter.HomeAdapter
import com.deniscerri.ytdlnis.database.DatabaseManager
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.databinding.FragmentDownloadsBinding
import com.deniscerri.ytdlnis.service.DownloadInfo
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.InfoUtil
import com.facebook.shimmer.ShimmerFrameLayout
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.button.MaterialButton
import com.google.android.material.chip.ChipGroup
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.google.android.material.textfield.TextInputLayout
import java.util.*
import java.util.regex.Pattern
import kotlin.collections.ArrayList
class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickListener {
private var progressBar: LinearProgressIndicator? = null
private var inputQuery: String? = null
private var inputQueries: LinkedList<String?>? = null
private var inputQueriesLength = 0
private var homeAdapter: HomeAdapter? = null
private var searchSuggestions: ScrollView? = null
private var searchSuggestionsLinearLayout: LinearLayout? = null
private var downloadFabs: CoordinatorLayout? = null
private var downloadAllFabCoordinator: CoordinatorLayout? = null
private var homeFabs: CoordinatorLayout? = null
private var infoUtil: InfoUtil? = null
private var downloadQueue: ArrayList<ResultItem>? = null
private var downloadInfo: DownloadInfo? = null
private lateinit var resultViewModel : ResultViewModel
private var downloading = false
private var fragmentView: View? = null
private var databaseManager: DatabaseManager? = null
private var activity: Activity? = null
private var mainActivity: MainActivity? = null
private var fragmentContext: Context? = null
private var layoutinflater: LayoutInflater? = null
private var shimmerCards: ShimmerFrameLayout? = null
private var topAppBar: MaterialToolbar? = null
private var recyclerView: RecyclerView? = null
private var bottomSheet: BottomSheetDialog? = null
private var sortSheet: BottomSheetDialog? = null
private var uiHandler: Handler? = null
private var noResults: RelativeLayout? = null
private var selectionChips: LinearLayout? = null
private var websiteGroup: ChipGroup? = null
private var resultsList: List<ResultItem?>? = null
private var selectedObjects: ArrayList<ResultItem>? = null
private var deleteFab: ExtendedFloatingActionButton? = null
private var fileUtil: FileUtil? = null
private var _binding : FragmentDownloadsBinding? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentDownloadsBinding.inflate(inflater, container, false)
fragmentView = inflater.inflate(R.layout.fragment_home, container, false)
activity = getActivity()
mainActivity = activity as MainActivity?
return fragmentView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
fragmentContext = context
layoutinflater = LayoutInflater.from(context)
shimmerCards = view.findViewById(R.id.shimmer_downloads_framelayout)
topAppBar = view.findViewById(R.id.downloads_toolbar)
noResults = view.findViewById(R.id.downloads_no_results)
selectionChips = view.findViewById(R.id.downloads_selection_chips)
websiteGroup = view.findViewById(R.id.website_chip_group)
deleteFab = view.findViewById(R.id.delete_selected_fab)
fileUtil = FileUtil()
deleteFab?.tag = "deleteSelected"
deleteFab?.setOnClickListener(this)
uiHandler = Handler(Looper.getMainLooper())
selectedObjects = ArrayList()
downloading = mainActivity!!.isDownloadServiceRunning()
downloadQueue = ArrayList()
resultsList = mutableListOf()
selectedObjects = ArrayList()
//initViews
shimmerCards = view.findViewById(R.id.shimmer_results_framelayout)
topAppBar = view.findViewById(R.id.home_toolbar)
searchSuggestions = view.findViewById(R.id.search_suggestions_scroll_view)
searchSuggestionsLinearLayout = view.findViewById(R.id.search_suggestions_linear_layout)
homeAdapter =
HomeAdapter(
this,
activity
)
recyclerView = view.findViewById(R.id.recyclerViewHome)
recyclerView?.layoutManager = LinearLayoutManager(context)
recyclerView?.adapter = homeAdapter
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
resultViewModel.items.observe(viewLifecycleOwner) {
homeAdapter!!.submitList(it)
resultsList = it
shimmerCards!!.stopShimmer()
shimmerCards!!.visibility = GONE
}
initMenu()
homeFabs = view.findViewById(R.id.home_fabs)
downloadFabs = homeFabs!!.findViewById(R.id.download_selected_coordinator)
downloadAllFabCoordinator = homeFabs!!.findViewById(R.id.download_all_coordinator)
val musicFab = downloadFabs!!.findViewById<FloatingActionButton>(R.id.audio_fab)
val videoFab = downloadFabs!!.findViewById<FloatingActionButton>(R.id.video_fab)
musicFab.tag = "SELECT##audio"
videoFab.tag = "SELECT##video"
musicFab.setOnClickListener(this)
videoFab.setOnClickListener(this)
val downloadAllFab =
downloadAllFabCoordinator!!.findViewById<ExtendedFloatingActionButton>(R.id.download_all_fab)
downloadAllFab.tag = "downloadAll"
downloadAllFab.setOnClickListener(this)
if (inputQueries != null) {
resultViewModel.deleteAll()
inputQueriesLength = inputQueries!!.size
shimmerCards!!.startShimmer()
shimmerCards!!.visibility = VISIBLE
val thread = Thread {
while (!inputQueries!!.isEmpty()) {
inputQuery = inputQueries!!.pop()
parseQuery(false)
}
try {
Handler(Looper.getMainLooper()).post {
// DOWNLOAD ALL BUTTON
if (resultsList!!.size > 1 || inputQueriesLength > 1) {
downloadAllFabCoordinator!!.visibility = VISIBLE
}
// databaseManager = DatabaseManager(context)
// databaseManager!!.clearResults()
// for (v in resultsList!!) v!!.isPlaylistItem = 1
// databaseManager!!.addToResults(resultsList)
}
} catch (ignored: Exception) {
}
}
thread.start()
} else {
if (resultViewModel.checkTrending()){
shimmerCards!!.startShimmer()
shimmerCards!!.visibility = VISIBLE
resultViewModel.getTrending()
}
}
}
//
// private fun initCards() {
// val uiHandler = Handler(Looper.getMainLooper())
// try {
// val thread = Thread {
// databaseManager = DatabaseManager(context)
// resultsList = databaseManager!!.results
// var playlistTitle = ""
// try {
// playlistTitle = resultsList.get(0).playlistTitle
// } catch (ignored: Exception) {
// }
// if (resultsList.size == 0 || playlistTitle == getString(R.string.trendingPlaylist) && !downloading) {
// try {
// databaseManager!!.clearResults()
// uiHandler.post {
// shimmerCards!!.startShimmer()
// shimmerCards!!.visibility = View.VISIBLE
// }
// infoUtil = InfoUtil(context!!)
// resultsList = infoUtil!!.getTrending(context!!)
// databaseManager!!.addToResults(resultsList)
// } catch (e: Exception) {
// Log.e(TAG, e.toString())
// }
// } else {
// if (!downloading) {
// homeAdapter!!.add(resultsList)
// for (i in resultsList.indices) {
// val tmp = resultsList.get(i)
// if (tmp!!.isDownloading) {
// updateDownloadingStatusOnResult(tmp, "audio", false)
// updateDownloadingStatusOnResult(tmp, "video", false)
// }
// }
// }
// }
// uiHandler.post {
// if (homeAdapter!!.itemCount != resultsList.size) {
// homeAdapter!!.add(resultsList)
// }
// shimmerCards!!.stopShimmer()
// shimmerCards!!.visibility = View.GONE
// }
// databaseManager!!.close()
// if (resultsList != null) {
// uiHandler.post { scrollToTop() }
// if (resultsList!!.size > 1 && resultsList!![1]!!.isPlaylistItem == 1) {
// uiHandler.post { downloadAllFabCoordinator!!.visibility = View.VISIBLE }
// }
// }
// }
// thread.start()
// } catch (e: Exception) {
// Log.e(TAG, e.toString())
// uiHandler.post {
// shimmerCards!!.stopShimmer()
// shimmerCards!!.visibility = View.GONE
// }
// }
// }
private fun initMenu() {
val onActionExpandListener: MenuItem.OnActionExpandListener =
object : MenuItem.OnActionExpandListener {
override fun onMenuItemActionExpand(menuItem: MenuItem): Boolean {
homeFabs!!.visibility = View.GONE
recyclerView!!.visibility = View.GONE
searchSuggestions!!.visibility = View.VISIBLE
return true
}
override fun onMenuItemActionCollapse(menuItem: MenuItem): Boolean {
homeFabs!!.visibility = View.VISIBLE
recyclerView!!.visibility = View.VISIBLE
searchSuggestions!!.visibility = View.GONE
return true
}
}
if (downloading) {
topAppBar!!.menu.findItem(R.id.cancel_download).isVisible = true
}
topAppBar!!.menu.findItem(R.id.search).setOnActionExpandListener(onActionExpandListener)
val searchView = topAppBar!!.menu.findItem(R.id.search).actionView as SearchView?
searchView!!.inputType = InputType.TYPE_TEXT_VARIATION_URI
searchView.queryHint = getString(R.string.search_hint)
databaseManager = DatabaseManager(context)
infoUtil = InfoUtil(requireContext())
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
topAppBar!!.menu.findItem(R.id.search).collapseActionView()
downloadAllFabCoordinator!!.visibility = GONE
downloadFabs!!.visibility = View.GONE
selectedObjects = ArrayList()
inputQuery = query.trim { it <= ' ' }
shimmerCards!!.startShimmer()
shimmerCards!!.visibility = View.VISIBLE
resultViewModel.deleteAll()
val thread = Thread {
parseQuery(true)
// DOWNLOAD ALL BUTTON
if (resultsList!!.size > 1 && resultsList!![1]!!.playlistTitle.isNotEmpty()) {
Handler(Looper.getMainLooper()).post {
downloadAllFabCoordinator!!.visibility = View.VISIBLE
}
}
}
thread.start()
return true
}
override fun onQueryTextChange(newText: String): Boolean {
searchSuggestionsLinearLayout!!.removeAllViews()
if (newText.isEmpty()) return false
val thread = Thread {
val suggestions = infoUtil!!.getSearchSuggestions(newText)
for (i in suggestions.indices) {
val v = LayoutInflater.from(fragmentContext)
.inflate(R.layout.search_suggestion_item, null)
val textView = v.findViewById<TextView>(R.id.suggestion_text)
textView.text = suggestions[i]
Handler(Looper.getMainLooper()).post {
searchSuggestionsLinearLayout!!.addView(
v
)
}
textView.setOnClickListener { onQueryTextSubmit(textView.text.toString()) }
val mb = v.findViewById<MaterialButton>(R.id.set_search_query_button)
mb.setOnClickListener {
searchView.setQuery(
textView.text,
false
)
}
}
}
thread.start()
return false
}
})
topAppBar!!.setOnClickListener { view: View? -> scrollToTop() }
topAppBar!!.setOnMenuItemClickListener { m: MenuItem ->
val itemId = m.itemId
if (itemId == R.id.delete_results) {
resultViewModel.getTrending()
selectedObjects = ArrayList()
downloadAllFabCoordinator!!.visibility = View.GONE
downloadFabs!!.visibility = View.GONE
}
// } else if (itemId == R.id.cancel_download) {
// try {
// mainActivity!!.cancelDownloadService()
// topAppBar!!.menu.findItem(itemId).isVisible = false
// for (i in downloadInfo!!.downloadQueue.indices) {
// val vid = downloadInfo!!.downloadQueue[i]
// val type = vid.downloadedType
// updateDownloadingStatusOnResult(vid, type, false)
// }
// downloadQueue = ArrayList()
// downloading = false
// } catch (ignored: Exception) {
// }
// }
true
}
}
fun handleIntent(intent: Intent) {
inputQueries = LinkedList()
inputQueries!!.add(intent.getStringExtra(Intent.EXTRA_TEXT))
}
fun handleFileIntent(lines: LinkedList<String?>?) {
inputQueries = lines
}
fun scrollToTop() {
recyclerView!!.scrollToPosition(0)
(topAppBar!!.parent as AppBarLayout).setExpanded(true, true)
}
private fun parseQuery(resetResults: Boolean) {
databaseManager = DatabaseManager(context)
infoUtil = InfoUtil(requireContext())
Handler(Looper.getMainLooper()).post { scrollToTop() }
var type = "Search"
val p = Pattern.compile("^(https?)://(www.)?youtu(.be)?")
val m = p.matcher(inputQuery!!)
if (m.find()) {
type = "Video"
if (inputQuery!!.contains("playlist?list=")) {
type = "Playlist"
}
} else if (inputQuery!!.contains("http")) {
type = "Default"
}
Log.e(TAG, "$inputQuery $type")
try {
when (type) {
"Search" -> {
try {
val res = infoUtil!!.search(
inputQuery!!
)
Handler(Looper.getMainLooper()).post {
if (resetResults) resultViewModel.deleteAll()
resultViewModel.insert(res)
}
} catch (e: Exception) {
Log.e(TAG, e.toString())
Handler(Looper.getMainLooper()).post {
shimmerCards!!.stopShimmer()
shimmerCards!!.visibility = View.GONE
}
}
}
"Video" -> {
var el: Array<String?> =
inputQuery!!.split("/".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()
inputQuery = el[el.size - 1]
if (inputQuery!!.contains("watch?v=")) {
inputQuery = inputQuery!!.substring(8)
}
el = inputQuery!!.split("&".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()
inputQuery = el[0]
el = inputQuery!!.split("\\?".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()
inputQuery = el[0]
try {
val v = infoUtil!!.getVideo(inputQuery!!)
val res = ArrayList<ResultItem?>()
res.add(v)
Handler(Looper.getMainLooper()).post {
if (resetResults) resultViewModel.deleteAll()
resultViewModel.insert(res)
}
} catch (e: Exception) {
Log.e(TAG, e.toString())
Handler(Looper.getMainLooper()).post {
shimmerCards!!.stopShimmer()
shimmerCards!!.visibility = View.GONE
}
}
}
"Playlist" -> {
inputQuery =
inputQuery!!.split("list=".toRegex()).dropLastWhile { it.isEmpty() }
.toTypedArray()[1]
var nextPageToken = ""
if (resetResults) resultViewModel.deleteAll()
do {
val tmp = infoUtil!!.getPlaylist(inputQuery!!, nextPageToken)
val tmp_vids = tmp.videos
val tmp_token = tmp.nextPageToken
Handler(Looper.getMainLooper()).post {
resultViewModel.insert(tmp_vids)
}
if (tmp_token.isEmpty()) break
if (tmp_token == nextPageToken) break
nextPageToken = tmp_token
} while (true)
}
"Default" -> {
try {
val video = infoUtil!!.getFromYTDL(
inputQuery!!
)
Handler(Looper.getMainLooper()).post {
if (resetResults) resultViewModel.deleteAll()
resultViewModel.insert(video)
}
Handler(Looper.getMainLooper()).post {
shimmerCards!!.stopShimmer()
shimmerCards!!.visibility = View.GONE
}
} catch (e: Exception) {
Log.e(TAG, e.toString())
}
}
}
} catch (e: Exception) {
Log.e(TAG, e.toString())
}
databaseManager!!.close()
}
fun findVideo(url: String): ResultItem? {
for (i in resultsList!!.indices) {
val v = resultsList!![i]
if (v!!.url == url) {
return v
}
}
return null
}
@SuppressLint("ResourceType")
override fun onButtonClick(position: Int, type: String) {
// Log.e(TAG, type)
// val vid = resultsList!![position]
// vid!!.downloadedType = type
// val btn = recyclerView!!.findViewWithTag<MaterialButton>(vid.videoId + "##" + type)
// if (downloading) {
// try {
// if (btn.getTag(R.id.cancelDownload) == "true") {
// mainActivity!!.removeItemFromDownloadQueue(vid, type)
// return
// }
// } catch (ignored: Exception) {
// }
// }
// val sharedPreferences =
// context!!.getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
// if (sharedPreferences.getBoolean("download_card", true)) {
// selectedObjects!!.clear()
// selectedObjects!!.add(resultsList!![position])
// showConfigureDownloadCard(type)
// } else {
// downloadQueue!!.add(vid)
// updateDownloadingStatusOnResult(vid, type, true)
// if (isStoragePermissionGranted) {
// mainActivity!!.startDownloadService(downloadQueue, listener)
// downloadQueue!!.clear()
// }
// }
}
private val isStoragePermissionGranted: Boolean
get() = if (ActivityCompat.checkSelfPermission(
requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
== PackageManager.PERMISSION_GRANTED
) {
true
} else {
downloadQueue = ArrayList()
ActivityCompat.requestPermissions(
requireActivity(),
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
1
)
false
}
fun updateDownloadStatusOnResult(v: ResultItem?, type: String?, downloaded: Boolean) {
// if (v != null) {
// databaseManager = DatabaseManager(context)
// try {
// databaseManager!!.updateDownloadStatusOnResult(v.videoId, type, downloaded)
// databaseManager!!.close()
// } catch (ignored: Exception) {
// }
// }
}
fun updateDownloadingStatusOnResult(v: ResultItem?, type: String, isDownloading: Boolean) {
// if (v != null) {
// if (type == "audio") v.isDownloadingAudio =
// isDownloading else if (type == "video") v.isDownloadingVideo = isDownloading
// homeAdapter!!.updateVideoListItem(v, resultsList!!.indexOf(v))
// databaseManager = DatabaseManager(context)
// try {
// databaseManager!!.updateDownloadingStatusOnResult(v.videoId, type, isDownloading)
// databaseManager!!.close()
// } catch (ignored: Exception) {
// }
// }
}
override fun onCardClick(position: Int, add: Boolean) {
val item = resultsList!![position]
if (add) selectedObjects!!.add(item!!) else selectedObjects!!.remove(item)
if (selectedObjects!!.size > 1) {
downloadAllFabCoordinator!!.visibility = View.GONE
downloadFabs!!.visibility = View.VISIBLE
} else {
downloadFabs!!.visibility = View.GONE
if (resultsList!!.size > 1 && resultsList!![1]!!.playlistTitle.isNotEmpty()) {
downloadAllFabCoordinator!!.visibility = View.VISIBLE
}
}
}
override fun onClick(v: View) {
val viewIdName: String
viewIdName = try {
v.tag.toString()
} catch (e: Exception) {
""
}
if (!viewIdName.isEmpty()) {
if (viewIdName.contains("audio") || viewIdName.contains("video")) {
val buttonData =
viewIdName.split("##".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
if (buttonData[0] == "SELECT") {
initSelectedDownload(buttonData[1])
}
}
if (viewIdName == "downloadAll") {
//remove previously selected
for (i in selectedObjects!!.indices) {
val vid = findVideo(
selectedObjects!![i]!!.url
)
homeAdapter!!.notifyItemChanged(resultsList!!.indexOf(vid))
}
selectedObjects = ArrayList()
downloadFabs!!.visibility = View.GONE
bottomSheet = BottomSheetDialog(fragmentContext!!)
bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet!!.setContentView(R.layout.home_download_all_bottom_sheet)
val first = bottomSheet!!.findViewById<TextInputLayout>(R.id.first_textinput)
first!!.editText!!.setText(1.toString())
val last = bottomSheet!!.findViewById<TextInputLayout>(R.id.last_textinput)
last!!.editText!!.setText(resultsList!!.size.toString())
val audio = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_audio_button)
audio!!.setOnClickListener { view: View? ->
val start = first.editText!!.text.toString().toInt()
val end = last.editText!!.text.toString().toInt()
initDownloadAll(bottomSheet!!, start, end, "audio")
}
val video = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_video_button)
video!!.setOnClickListener { view: View? ->
val start = first.editText!!.text.toString().toInt()
val end = last.editText!!.text.toString().toInt()
initDownloadAll(bottomSheet!!, start, end, "video")
}
bottomSheet!!.show()
bottomSheet!!.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
}
}
private fun initDownloadAll(
bottomSheet: BottomSheetDialog,
start: Int,
end: Int,
type: String
) {
// var start = start
// var end = end
// if (start > end) {
// val first = bottomSheet.findViewById<TextInputLayout>(R.id.first_textinput)
// first!!.error = getString(R.string.first_cant_be_larger_than_last)
// val last = bottomSheet.findViewById<TextInputLayout>(R.id.last_textinput)
// last!!.error = getString(R.string.last_cant_be_smaller_than_first)
// return
// }
// bottomSheet.cancel()
// start--
// end--
// if (start <= 1) start = 0
// val sharedPreferences =
// requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
// if (sharedPreferences.getBoolean("download_card", true)) {
// selectedObjects!!.clear()
// selectedObjects!!.addAll(resultsList!!.subList(start, end + 1) as ArrayList<ResultItem>)
// showConfigureDownloadCard(type)
// } else {
// for (i in start..end) {
// val vid = findVideo(
// resultsList!![i]!!.getURL()
// )
// vid!!.downloadedType = type
// updateDownloadingStatusOnResult(vid, type, true)
// downloadQueue!!.add(vid)
// }
// if (isStoragePermissionGranted) {
// mainActivity!!.startDownloadService(downloadQueue, listener)
// downloadQueue!!.clear()
// }
// }
}
private fun initSelectedDownload(type: String) {
// val sharedPreferences =
// context!!.getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
// val editor = sharedPreferences.edit()
// if (sharedPreferences.getBoolean("download_card", true)) {
// showConfigureDownloadCard(type)
// } else {
// for (i in selectedObjects!!.indices) {
// val vid = findVideo(
// selectedObjects!![i]!!.getURL()
// )
// vid!!.downloadedType = type
// updateDownloadingStatusOnResult(vid, type, true)
// homeAdapter!!.notifyItemChanged(resultsList!!.indexOf(vid))
// downloadQueue!!.add(vid)
// }
// selectedObjects = ArrayList()
// homeAdapter!!.clearCheckedVideos()
// downloadFabs!!.visibility = View.GONE
// if (isStoragePermissionGranted) {
// mainActivity!!.startDownloadService(downloadQueue, listener)
// downloadQueue!!.clear()
// }
// }
}
private fun showConfigureDownloadCard(type: String) {
// val sharedPreferences =
// context!!.getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
// val editor = sharedPreferences.edit()
// try {
// bottomSheet = BottomSheetDialog(fragmentContext!!)
// bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
// if (type == "audio") {
// bottomSheet!!.setContentView(R.layout.home_download_audio_bottom_sheet)
// val title = bottomSheet!!.findViewById<TextInputLayout>(R.id.title_textinput)
// if (selectedObjects!!.size > 1) {
// title!!.editText!!.setText(getString(R.string.mutliple_titles))
// title.editText!!.isClickable = false
// title.editText!!.isLongClickable = false
// } else {
// title!!.editText!!.setText(selectedObjects!![0]!!.title)
// title.editText!!.addTextChangedListener(object : TextWatcher {
// override fun beforeTextChanged(
// charSequence: CharSequence,
// i: Int,
// i1: Int,
// i2: Int
// ) {
// }
//
// override fun onTextChanged(
// charSequence: CharSequence,
// i: Int,
// i1: Int,
// i2: Int
// ) {
// }
//
// override fun afterTextChanged(editable: Editable) {
// val index = resultsList!!.indexOf(selectedObjects!![0])
// resultsList!![index]!!.title = editable.toString()
// }
// })
// }
// val author = bottomSheet!!.findViewById<TextInputLayout>(R.id.author_textinput)
// if (selectedObjects!!.size > 1) {
// author!!.editText!!.setText(getString(R.string.mutliple_authors))
// author.editText!!.isClickable = false
// author.editText!!.isLongClickable = false
// } else {
// author!!.editText!!.setText(selectedObjects!![0]!!.author)
// author.editText!!.addTextChangedListener(object : TextWatcher {
// override fun beforeTextChanged(
// charSequence: CharSequence,
// i: Int,
// i1: Int,
// i2: Int
// ) {
// }
//
// override fun onTextChanged(
// charSequence: CharSequence,
// i: Int,
// i1: Int,
// i2: Int
// ) {
// }
//
// override fun afterTextChanged(editable: Editable) {
// val index = resultsList!!.indexOf(selectedObjects!![0])
// resultsList!![index]!!.author = editable.toString()
// }
// })
// }
// val audioFormats = context!!.resources.getStringArray(R.array.music_formats)
// val audioFormat = bottomSheet!!.findViewById<TextInputLayout>(R.id.audio_format)
// val autoCompleteTextView =
// bottomSheet!!.findViewById<AutoCompleteTextView>(R.id.audio_format_textview)
// val preference = sharedPreferences.getString("audio_format", "mp3")
// autoCompleteTextView!!.setText(preference, false)
// (audioFormat!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
// AdapterView.OnItemClickListener { adapterView: AdapterView<*>?, view: View?, index: Int, l: Long ->
// for (i in selectedObjects!!.indices) {
// val vid = findVideo(
// selectedObjects!![i]!!.getURL()
// )
// vid!!.audioFormat = audioFormats[index]
// }
// editor.putString("audio_format", audioFormats[index])
// editor.apply()
// }
// } else {
// bottomSheet!!.setContentView(R.layout.home_download_video_bottom_sheet)
// val title = bottomSheet!!.findViewById<TextInputLayout>(R.id.title_textinput)
// if (selectedObjects!!.size > 1) {
// title!!.editText!!.setText(getString(R.string.mutliple_titles))
// title.editText!!.isClickable = false
// title.editText!!.isLongClickable = false
// } else {
// title!!.editText!!.setText(selectedObjects!![0]!!.title)
// title.editText!!.addTextChangedListener(object : TextWatcher {
// override fun beforeTextChanged(
// charSequence: CharSequence,
// i: Int,
// i1: Int,
// i2: Int
// ) {
// }
//
// override fun onTextChanged(
// charSequence: CharSequence,
// i: Int,
// i1: Int,
// i2: Int
// ) {
// }
//
// override fun afterTextChanged(editable: Editable) {
// val index = resultsList!!.indexOf(selectedObjects!![0])
// resultsList!![index]!!.title = editable.toString()
// }
// })
// }
// val videoFormats = context!!.resources.getStringArray(R.array.video_formats)
// val videoQualities = context!!.resources.getStringArray(R.array.video_quality)
// val videoFormat = bottomSheet!!.findViewById<TextInputLayout>(R.id.video_format)
// var autoCompleteTextView =
// bottomSheet!!.findViewById<AutoCompleteTextView>(R.id.video_format_textview)
// var preference = sharedPreferences.getString("video_format", "webm")
// autoCompleteTextView!!.setText(preference, false)
// (videoFormat!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
// AdapterView.OnItemClickListener { adapterView: AdapterView<*>?, view: View?, index: Int, l: Long ->
// for (i in selectedObjects!!.indices) {
// val vid = findVideo(
// selectedObjects!![i]!!.getURL()
// )
// vid!!.videoFormat = videoFormats[index]
// }
// editor.putString("video_format", videoFormats[index])
// editor.apply()
// }
// val videoQuality = bottomSheet!!.findViewById<TextInputLayout>(R.id.video_quality)
// autoCompleteTextView = bottomSheet!!.findViewById(R.id.video_quality_textview)
// preference = sharedPreferences.getString("video_quality", "Best Quality")
// autoCompleteTextView!!.setText(preference, false)
// (videoQuality!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
// AdapterView.OnItemClickListener { adapterView: AdapterView<*>?, view: View?, index: Int, l: Long ->
// for (i in selectedObjects!!.indices) {
// val vid = findVideo(
// selectedObjects!![i]!!.getURL()
// )
// vid!!.videoQuality = videoQualities[index]
// }
// editor.putString("video_quality", videoQualities[index])
// editor.apply()
// }
// val embedSubs = bottomSheet!!.findViewById<Chip>(R.id.embed_subtitles)
// embedSubs!!.isChecked = sharedPreferences.getBoolean("embed_subtitles", false)
// embedSubs.setOnClickListener { view: View? ->
// if (embedSubs.isChecked) {
// editor.putBoolean("embed_subtitles", true)
// } else {
// editor.putBoolean("embed_subtitles", false)
// }
// editor.apply()
// }
// val addChapters = bottomSheet!!.findViewById<Chip>(R.id.add_chapters)
// addChapters!!.isChecked = sharedPreferences.getBoolean("add_chapters", false)
// addChapters.setOnClickListener { view: View? ->
// if (addChapters.isChecked) {
// editor.putBoolean("add_chapters", true)
// } else {
// editor.putBoolean("add_chapters", false)
// }
// editor.apply()
// }
// val saveThumbnail = bottomSheet!!.findViewById<Chip>(R.id.save_thumbnail)
// saveThumbnail!!.isChecked = sharedPreferences.getBoolean("write_thumbnail", false)
// saveThumbnail.setOnClickListener { view: View? ->
// if (saveThumbnail.isChecked) {
// editor.putBoolean("write_thumbnail", true)
// } else {
// editor.putBoolean("write_thumbnail", false)
// }
// editor.apply()
// }
// }
// val cancel = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_cancel_button)
// cancel!!.setOnClickListener { view: View? -> bottomSheet!!.cancel() }
// val download = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_download_button)
// download!!.setOnClickListener { view: View? ->
// for (i in selectedObjects!!.indices) {
// val vid = findVideo(
// selectedObjects!![i]!!.getURL()
// )
// vid!!.downloadedType = type
// updateDownloadingStatusOnResult(vid, type, true)
// homeAdapter!!.notifyItemChanged(resultsList!!.indexOf(vid))
// downloadQueue!!.add(vid)
// }
// selectedObjects = ArrayList()
// homeAdapter!!.clearCheckedVideos()
// downloadFabs!!.visibility = View.GONE
// if (isStoragePermissionGranted) {
// mainActivity!!.startDownloadService(downloadQueue, listener)
// downloadQueue!!.clear()
// }
// bottomSheet!!.cancel()
// }
// bottomSheet!!.show()
// bottomSheet!!.window!!.setLayout(
// ViewGroup.LayoutParams.MATCH_PARENT,
// ViewGroup.LayoutParams.MATCH_PARENT
// )
// } catch (e: Exception) {
// Toast.makeText(fragmentContext, e.message, Toast.LENGTH_LONG).show()
// }
}
companion object {
private const val TAG = "HomeFragment"
}
}

View file

@ -3,17 +3,13 @@ package com.deniscerri.ytdlnis.util
import android.content.Context
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.FileUtils
import android.provider.DocumentsContract
import android.util.Log
import android.webkit.MimeTypeMap
import android.widget.Toast
import com.deniscerri.ytdlnis.DownloaderService
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.Video
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.ResultItem
import java.io.File
import java.io.InputStream
import java.io.OutputStream
import java.net.URLDecoder
import java.nio.charset.StandardCharsets
@ -55,7 +51,7 @@ class FileUtil() {
return formattedPath.toString()
}
fun scanMedia(video: Video, context: Context) : String {
fun scanMedia(video: DownloadItem, context: Context) : String {
val files = ArrayList<File>()
val title: String = video.title.replace("[^a-zA-Z0-9]".toRegex(), "")
var pathTmp: String

View file

@ -5,7 +5,7 @@ import android.content.Context
import android.util.Log
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.DatabaseManager
import com.deniscerri.ytdlnis.database.Video
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.yausername.youtubedl_android.YoutubeDL
import com.yausername.youtubedl_android.YoutubeDLRequest
import org.json.JSONArray
@ -16,9 +16,10 @@ import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
import java.util.*
import kotlin.collections.ArrayList
class InfoUtil(context: Context) {
private var videos: ArrayList<Video?>
private var items: ArrayList<ResultItem?>
private var key: String? = null
private var useInvidous = false
private var databaseManager: DatabaseManager? = null
@ -32,7 +33,7 @@ class InfoUtil(context: Context) {
} catch (e: Exception) {
e.printStackTrace()
}
videos = ArrayList()
items = ArrayList()
}
private fun init(){
@ -51,16 +52,16 @@ class InfoUtil(context: Context) {
}
@Throws(JSONException::class)
fun search(query: String): ArrayList<Video?> {
fun search(query: String): ArrayList<ResultItem?> {
init()
videos = ArrayList()
items = ArrayList()
return if (key!!.isEmpty()) {
if (useInvidous) searchFromInvidous(query) else getFromYTDL(query)
} else searchFromKey(query)
}
@Throws(JSONException::class)
fun searchFromKey(query: String): ArrayList<Video?> {
fun searchFromKey(query: String): ArrayList<ResultItem?> {
//short data
val res =
genericRequest("https://youtube.googleapis.com/youtube/v3/search?part=snippet&q=$query&maxResults=25&regionCode=$countryCODE&key=$key")
@ -102,14 +103,14 @@ class InfoUtil(context: Context) {
if (v == null || v.thumb.isEmpty()) {
continue
}
videos.add(createVideofromJSON(snippet))
items.add(createVideofromJSON(snippet))
}
}
return videos
return items
}
@Throws(JSONException::class)
fun searchFromInvidous(query: String): ArrayList<Video?> {
fun searchFromInvidous(query: String): ArrayList<ResultItem?> {
val dataArray = genericArrayRequest(invidousURL + "search?q=" + query + "?type=video")
if (dataArray.length() == 0) return getFromYTDL(query)
for (i in 0 until dataArray.length()) {
@ -128,15 +129,15 @@ class InfoUtil(context: Context) {
if (v == null || v.thumb.isEmpty()) {
continue
}
videos.add(v)
items.add(v)
}
return videos
return items
}
@Throws(JSONException::class)
fun getPlaylist(id: String, nextPageToken: String): PlaylistTuple {
init()
videos = ArrayList()
items = ArrayList()
if (key!!.isEmpty()) {
return if (useInvidous) getPlaylistFromInvidous(id) else PlaylistTuple(
"",
@ -186,12 +187,12 @@ class InfoUtil(context: Context) {
} else {
j++
}
v.isPlaylistItem = 1
videos.add(v)
v.playlistTitle = "YTDLnis"
items.add(v)
i++
}
val next = res.optString("nextPageToken")
return PlaylistTuple(next, videos)
return PlaylistTuple(next, items)
}
private fun getPlaylistFromInvidous(id: String): PlaylistTuple {
@ -208,17 +209,16 @@ class InfoUtil(context: Context) {
val v = createVideofromInvidiousJSON(element)
if (v == null || v.thumb.isEmpty()) continue
v.playlistTitle = res.getString("title")
v.isPlaylistItem = 1
videos.add(v)
items.add(v)
}
} catch (e: Exception) {
e.printStackTrace()
}
return PlaylistTuple("", videos)
return PlaylistTuple("", items)
}
@Throws(JSONException::class)
fun getVideo(id: String): Video? {
fun getVideo(id: String): ResultItem? {
init()
if (key!!.isEmpty()) {
return if (useInvidous) {
@ -245,8 +245,8 @@ class InfoUtil(context: Context) {
return createVideofromJSON(res)
}
private fun createVideofromJSON(obj: JSONObject): Video? {
var video: Video? = null
private fun createVideofromJSON(obj: JSONObject): ResultItem? {
var video: ResultItem? = null
try {
val id = obj.getString("videoID")
val title = obj.getString("title").toString()
@ -257,20 +257,14 @@ class InfoUtil(context: Context) {
val downloadedAudio = databaseManager!!.checkDownloaded(url, "audio")
val downloadedVideo = databaseManager!!.checkDownloaded(url, "video")
val isPlaylist = 0
video = Video(
video = ResultItem(
id,
url,
title,
author,
duration,
thumb,
downloadedAudio,
downloadedVideo,
isPlaylist,
"youtube",
0,
0,
""
"youtube"
)
} catch (e: Exception) {
Log.e(TAG, e.toString())
@ -278,8 +272,8 @@ class InfoUtil(context: Context) {
return video
}
private fun createVideofromInvidiousJSON(obj: JSONObject): Video? {
var video: Video? = null
private fun createVideofromInvidiousJSON(obj: JSONObject): ResultItem? {
var video: ResultItem? = null
try {
val id = obj.getString("videoId")
val title = obj.getString("title").toString()
@ -290,20 +284,14 @@ class InfoUtil(context: Context) {
val downloadedAudio = databaseManager!!.checkDownloaded(url, "audio")
val downloadedVideo = databaseManager!!.checkDownloaded(url, "video")
val isPlaylist = 0
video = Video(
video = ResultItem(
id,
url,
title,
author,
duration,
thumb,
downloadedAudio,
downloadedVideo,
isPlaylist,
"youtube",
0,
0,
""
"youtube"
)
} catch (e: Exception) {
Log.e(TAG, e.toString())
@ -395,16 +383,16 @@ class InfoUtil(context: Context) {
}
@Throws(JSONException::class)
fun getTrending(context: Context): ArrayList<Video?> {
fun getTrending(context: Context): ArrayList<ResultItem?> {
init()
videos = ArrayList()
items = ArrayList()
return if (key!!.isEmpty()) {
if (useInvidous) getTrendingFromInvidous(context) else ArrayList()
} else getTrendingFromKey(context)
}
@Throws(JSONException::class)
fun getTrendingFromKey(context: Context): ArrayList<Video?> {
fun getTrendingFromKey(context: Context): ArrayList<ResultItem?> {
val url = "https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&videoCategoryId=10&regionCode=$countryCODE&maxResults=25&key=$key"
//short data
val res = genericRequest(url)
@ -428,12 +416,12 @@ class InfoUtil(context: Context) {
continue
}
v.playlistTitle = context.getString(R.string.trendingPlaylist)
videos.add(v)
items.add(v)
}
return videos
return items
}
private fun getTrendingFromInvidous(context: Context): ArrayList<Video?> {
private fun getTrendingFromInvidous(context: Context): ArrayList<ResultItem?> {
val url = invidousURL + "trending?type=music&region=" + countryCODE
val res = genericArrayRequest(url)
try {
@ -443,17 +431,17 @@ class InfoUtil(context: Context) {
val v = createVideofromInvidiousJSON(element)
if (v == null || v.thumb.isEmpty()) continue
v.playlistTitle = context.getString(R.string.trendingPlaylist)
videos.add(v)
items.add(v)
}
} catch (e: Exception) {
e.printStackTrace()
}
return videos
return items
}
fun getFromYTDL(query: String): ArrayList<Video?> {
fun getFromYTDL(query: String): ArrayList<ResultItem?> {
init()
videos = ArrayList()
items = ArrayList()
try {
val request = YoutubeDLRequest(query)
request.addOption("--flat-playlist")
@ -501,28 +489,22 @@ class InfoUtil(context: Context) {
val website = if (jsonObject.has("ie_key")) jsonObject.getString("ie_key") else jsonObject.getString("extractor")
var playlistTitle: String? = ""
if (jsonObject.has("playlist")) playlistTitle = jsonObject.getString("playlist")
videos.add(
Video(
jsonObject.getString("id"),
items.add(
ResultItem(
url,
title,
author,
author!!,
duration,
thumb,
databaseManager!!.checkDownloaded(url, "audio"),
databaseManager!!.checkDownloaded(url, "video"),
isPlaylist,
thumb!!,
website,
0,
0,
playlistTitle
playlistTitle!!
)
)
}
} catch (e: Exception) {
e.printStackTrace()
}
return videos
return items
}
fun getSearchSuggestions(query: String): ArrayList<String> {
@ -598,7 +580,7 @@ class InfoUtil(context: Context) {
class PlaylistTuple internal constructor(
var nextPageToken: String,
var videos: ArrayList<Video?>
var videos: ArrayList<ResultItem?>
)
companion object {

View file

@ -50,7 +50,7 @@ class DownloadWorker(
setForeground(foregroundInfo)
while (queue.isNotEmpty()){
val item = dao.getDownloadById(queue[0].id)
val item = dao.getDownloadById(queue[0].id!!)
queue.removeFirst()
val url = item.url
val request = YoutubeDLRequest(url)

View file

@ -45,7 +45,7 @@
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:id="@+id/recycler_view_home"
android:id="@+id/recyclerViewHome"
android:nestedScrollingEnabled="false"
android:orientation="vertical"
android:layout_height="match_parent"

View file

@ -36,8 +36,8 @@ buildscript {
}
plugins {
id 'com.android.application' version '7.3.1' apply false
id 'com.android.library' version '7.3.1' apply false
id 'com.android.application' version '7.4.0' apply false
id 'com.android.library' version '7.4.0' apply false
id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
}