From 7684a15e9492ba1fd928c9707783570504d6864f Mon Sep 17 00:00:00 2001
From: Sadwhy <99601717+Sadwhy@users.noreply.github.com>
Date: Thu, 28 Dec 2023 16:13:42 +0600
Subject: [PATCH 1/6] Changed a few strings (#101)
* :)
Changed some strings
* More strings
* New string
---
.github/workflows/beta.yml | 2 +-
app/src/main/res/values-en-rDW/strings.xml | 2 +-
app/src/main/res/values/strings.xml | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/beta.yml b/.github/workflows/beta.yml
index a9145485..1f24338c 100644
--- a/.github/workflows/beta.yml
+++ b/.github/workflows/beta.yml
@@ -50,7 +50,7 @@ jobs:
shell: bash
run: |
contentbody=$( jq -Rsa . <<< "${{ github.event.head_commit.message }}" )
- curl -F "payload_json={\"content\":\" everyone **${{ env.VERSION }}**\n\n${contentbody:1:-1}\"}" -F "dantotsu_debug=@app/build/outputs/apk/debug/app-debug.apk" ${{ secrets.DISCORD_WEBHOOK }}
+ curl -F "payload_json={\"content\":\" Debug-Build **${{ env.VERSION }}**\n\n${contentbody:1:-1}\"}" -F "dantotsu_debug=@app/build/outputs/apk/debug/app-debug.apk" ${{ secrets.DISCORD_WEBHOOK }}
- name: Delete Old Pre-Releases
id: delete-pre-releases
diff --git a/app/src/main/res/values-en-rDW/strings.xml b/app/src/main/res/values-en-rDW/strings.xml
index e50ccd3a..293ce3fc 100644
--- a/app/src/main/res/values-en-rDW/strings.xml
+++ b/app/src/main/res/values-en-rDW/strings.xml
@@ -228,7 +228,7 @@
Subtitle Background Color
Subtitle Window Color
"The subtitle window is the part left and right from them. (where the background isn\'t)"
- Note: Changing Subtitle formatting only works with Soft-Subbed Sources such as Zoro!
+ Note: Changing above settings only affects Soft-Subtitles!
Subtitle Font
Subtitle Size
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6a8da5ba..23e646c5 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -227,7 +227,7 @@
Subtitle Background Color
Subtitle Window Color
"The subtitle window is the part left and right from them. (where the background isn\'t)"
- Note: Changing Subtitle formatting only works with Soft-Subbed Sources such as Zoro!
+ Note: Changing above settings only affects Soft-Subtitles!
Subtitle Font
Subtitle Size
From bbc986784b69540e85bc99908988443487b3ca60 Mon Sep 17 00:00:00 2001
From: aayush262 <99584765+aayush2622@users.noreply.github.com>
Date: Thu, 28 Dec 2023 15:45:10 +0530
Subject: [PATCH 2/6] small changes (#102)
---
.../download/manga/OfflineMangaFragment.kt | 10 +++++++++
.../ani/dantotsu/home/AnimePageAdapter.kt | 6 -----
.../java/ani/dantotsu/home/HomeFragment.kt | 8 -------
.../ani/dantotsu/home/MangaPageAdapter.kt | 6 -----
.../ani/dantotsu/media/user/ListActivity.kt | 5 ++---
.../settings/UserInterfaceSettings.kt | 1 -
.../settings/UserInterfaceSettingsActivity.kt | 7 ------
.../main/res/layout/activity_extensions.xml | 17 --------------
.../activity_user_interface_settings.xml | 22 -------------------
9 files changed, 12 insertions(+), 70 deletions(-)
diff --git a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt
index 85a06bed..d405583c 100644
--- a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt
+++ b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt
@@ -14,6 +14,8 @@ import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.view.animation.AlphaAnimation
+import android.view.animation.LayoutAnimationController
import android.view.animation.OvershootInterpolator
import android.widget.AbsListView
import android.widget.AutoCompleteTextView
@@ -145,6 +147,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
gridView.visibility = View.GONE
gridView = view.findViewById(R.id.gridView1)
gridView.adapter = adapter
+ gridView.scheduleLayoutAnimation()
gridView.visibility = View.VISIBLE
adapter.notifyNewGrid()
}
@@ -152,8 +155,15 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
gridView = if(style == 0) view.findViewById(R.id.gridView) else view.findViewById(R.id.gridView1)
gridView.visibility = View.VISIBLE
getDownloads()
+
+ val fadeIn = AlphaAnimation(0f, 1f)
+ fadeIn.duration = 200 // animations pog
+ val animation = LayoutAnimationController(fadeIn)
+
+ gridView.layoutAnimation = animation
adapter = OfflineMangaAdapter(requireContext(), downloads, this)
gridView.adapter = adapter
+ gridView.scheduleLayoutAnimation()
gridView.setOnItemClickListener { parent, view, position, id ->
// Get the OfflineMangaModel that was clicked
val item = adapter.getItem(position) as OfflineMangaModel
diff --git a/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt b/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt
index 43c1bc3e..732ac43f 100644
--- a/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/home/AnimePageAdapter.kt
@@ -77,12 +77,6 @@ class AnimePageAdapter : RecyclerView.Adapter("ui_settings") ?: UserInterfaceSettings()
- if (!uiSettings.immersiveModeList) {
+ if (!uiSettings.immersiveMode) {
this.window.statusBarColor =
ContextCompat.getColor(this, R.color.nav_bg_inv)
binding.root.fitsSystemWindows = true
@@ -78,8 +78,7 @@ class ListActivity : AppCompatActivity() {
setContentView(binding.root)
val anime = intent.getBooleanExtra("anime", true)
- binding.listTitle.text =
- intent.getStringExtra("username") + "'s " + (if (anime) "Anime" else "Manga") + " List"
+ binding.listTitle.text = (if (anime) "Anime" else "Manga") + " List"
binding.listTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
diff --git a/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettings.kt b/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettings.kt
index 15fd20a8..39dd18b1 100644
--- a/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettings.kt
+++ b/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettings.kt
@@ -10,7 +10,6 @@ data class UserInterfaceSettings(
//App
var immersiveMode: Boolean = false,
- var immersiveModeList: Boolean = false,
var smallView: Boolean = true,
var defaultStartUpTab: Int = 1,
var homeLayoutShow: MutableList = mutableListOf(
diff --git a/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettingsActivity.kt b/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettingsActivity.kt
index 189d691f..fb49c496 100644
--- a/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettingsActivity.kt
+++ b/app/src/main/java/ani/dantotsu/settings/UserInterfaceSettingsActivity.kt
@@ -68,13 +68,6 @@ class UserInterfaceSettingsActivity : AppCompatActivity() {
saveData(ui, settings)
restartApp()
}
- binding.uiSettingsImmersiveList.isChecked = settings.immersiveModeList
- binding.uiSettingsImmersiveList.setOnCheckedChangeListener { _, isChecked ->
- settings.immersiveModeList = isChecked
- saveData(ui, settings)
- restartApp()
- }
-
binding.uiSettingsBannerAnimation.isChecked = settings.bannerAnimations
binding.uiSettingsBannerAnimation.setOnCheckedChangeListener { _, isChecked ->
settings.bannerAnimations = isChecked
diff --git a/app/src/main/res/layout/activity_extensions.xml b/app/src/main/res/layout/activity_extensions.xml
index 85a8db87..728236ba 100644
--- a/app/src/main/res/layout/activity_extensions.xml
+++ b/app/src/main/res/layout/activity_extensions.xml
@@ -81,23 +81,6 @@
app:tabPaddingStart="16dp"
app:tabTextAppearance="@style/NavBarText"
app:tabGravity="fill">
-
-
-
-
-
-
-
Date: Thu, 28 Dec 2023 19:27:03 -0600
Subject: [PATCH 3/6] move countdown to 28 days
---
app/src/main/java/ani/dantotsu/Functions.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/ani/dantotsu/Functions.kt b/app/src/main/java/ani/dantotsu/Functions.kt
index 6a789743..2ac075f0 100644
--- a/app/src/main/java/ani/dantotsu/Functions.kt
+++ b/app/src/main/java/ani/dantotsu/Functions.kt
@@ -673,7 +673,7 @@ fun copyToClipboard(string: String, toast: Boolean = true) {
@SuppressLint("SetTextI18n")
fun countDown(media: Media, view: ViewGroup) {
- if (media.anime?.nextAiringEpisode != null && media.anime.nextAiringEpisodeTime != null && (media.anime.nextAiringEpisodeTime!! - System.currentTimeMillis() / 1000) <= 86400 * 7.toLong()) {
+ if (media.anime?.nextAiringEpisode != null && media.anime.nextAiringEpisodeTime != null && (media.anime.nextAiringEpisodeTime!! - System.currentTimeMillis() / 1000) <= 86400 * 28.toLong()) {
val v = ItemCountDownBinding.inflate(LayoutInflater.from(view.context), view, false)
view.addView(v.root, 0)
v.mediaCountdownText.text =
From 41830dba4d59c9e5c03deeb0bc6aaf79be6f20aa Mon Sep 17 00:00:00 2001
From: Finnley Somdahl <87634197+rebelonion@users.noreply.github.com>
Date: Thu, 28 Dec 2023 21:57:20 -0600
Subject: [PATCH 4/6] tap between manga pages (paged)
---
.../manga/mangareader/BaseImageAdapter.kt | 5 +-
.../media/manga/mangareader/ImageAdapter.kt | 10 +++
.../manga/mangareader/MangaReaderActivity.kt | 64 +++++++++++++++++--
app/src/main/res/layout/item_image.xml | 2 +-
4 files changed, 74 insertions(+), 7 deletions(-)
diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/BaseImageAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/BaseImageAdapter.kt
index 6a18b9e8..d1732b5a 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/BaseImageAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/BaseImageAdapter.kt
@@ -89,7 +89,7 @@ abstract class BaseImageAdapter(
}
} else {
val detector = GestureDetectorCompat(view.context, object : GesturesListener() {
- override fun onSingleClick(event: MotionEvent) = activity.handleController()
+ override fun onSingleClick(event: MotionEvent) = activity.handleController(event = event)
})
view.findViewById(R.id.imgProgCover).apply {
setOnTouchListener { _, event ->
@@ -112,6 +112,9 @@ abstract class BaseImageAdapter(
activity.lifecycleScope.launch { loadImage(holder.bindingAdapterPosition, view) }
}
+ abstract fun isZoomed(): Boolean
+ abstract fun setZoom(zoom: Float)
+
abstract suspend fun loadImage(position: Int, parent: View): Boolean
companion object {
diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/ImageAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/ImageAdapter.kt
index 97651499..45ae40f0 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/ImageAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/ImageAdapter.kt
@@ -91,4 +91,14 @@ open class ImageAdapter(
}
override fun getItemCount(): Int = images.size
+
+ override fun isZoomed(): Boolean {
+ val imageView = activity.findViewById(R.id.imgProgImageNoGestures)
+ return imageView.scale > imageView.minScale
+ }
+
+ override fun setZoom(zoom: Float) {
+ val imageView = activity.findViewById(R.id.imgProgImageNoGestures)
+ imageView.setScaleAndCenter(zoom, imageView.center)
+ }
}
diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt
index d6d72871..067ee56f 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt
@@ -6,6 +6,7 @@ import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
+import android.content.res.Resources
import android.graphics.Bitmap
import android.os.Build
import android.os.Bundle
@@ -702,8 +703,60 @@ class MangaReaderActivity : AppCompatActivity() {
goneTimer.schedule(timerTask, controllerDuration)
}
- fun handleController(shouldShow: Boolean? = null) {
+ enum class pressPos {
+ LEFT, RIGHT, CENTER
+ }
+
+ fun handleController(shouldShow: Boolean? = null, event: MotionEvent? = null) {
+ var pressLocation = pressPos.CENTER
if (!sliding) {
+ if (event != null && settings.default.layout == PAGED) {
+ if (event.action != MotionEvent.ACTION_UP) return
+ val x = event.rawX.toInt()
+ val y = event.rawY.toInt()
+ val screenWidth = Resources.getSystem().displayMetrics.widthPixels
+ //if in the 1st 1/5th of the screen width, left and lower than 1/5th of the screen height, left
+ if (screenWidth / 5 in (x + 1).. screenWidth - screenWidth / 5 && y > screenWidth / 5) {
+ pressLocation = if (settings.default.direction == RIGHT_TO_LEFT) {
+ pressPos.LEFT
+ } else {
+ pressPos.RIGHT
+ }
+ }
+ }
+
+ // if pressLocation is left or right go to previous or next page (paged mode only)
+ if (pressLocation == pressPos.LEFT) {
+
+ if (binding.mangaReaderPager.currentItem > 0) {
+ //if the current images zoomed in, go back to normal before going to previous page
+ if (imageAdapter?.isZoomed() == true) {
+ imageAdapter?.setZoom(1f)
+ }
+ binding.mangaReaderPager.currentItem -= 1
+ return
+ }
+
+ } else if (pressLocation == pressPos.RIGHT) {
+ if (binding.mangaReaderPager.currentItem < maxChapterPage - 1) {
+ //if the current images zoomed in, go back to normal before going to next page
+ if (imageAdapter?.isZoomed() == true) {
+ imageAdapter?.setZoom(1f)
+ }
+ //if right to left, go to previous page
+ binding.mangaReaderPager.currentItem += 1
+ return
+ }
+ }
+
if (!settings.showSystemBars) {
hideBars()
checkNotch()
@@ -796,7 +849,7 @@ class MangaReaderActivity : AppCompatActivity() {
private fun progress(runnable: Runnable) {
if (maxChapterPage - currentChapterPage <= 1 && Anilist.userid != null) {
- if (showProgressDialog) {
+ if (showProgressDialog) {
val dialogView = layoutInflater.inflate(R.layout.item_custom_dialog, null)
val checkbox = dialogView.findViewById(R.id.dialog_checkbox)
@@ -805,8 +858,9 @@ class MangaReaderActivity : AppCompatActivity() {
saveData("${media.id}_progressDialog", isChecked)
showProgressDialog = !isChecked
}
- val incognito = currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
- ?.getBoolean("incognito", false) ?: false
+ val incognito =
+ currContext()?.getSharedPreferences("Dantotsu", Context.MODE_PRIVATE)
+ ?.getBoolean("incognito", false) ?: false
AlertDialog.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.title_update_progress))
.apply {
@@ -818,7 +872,7 @@ class MangaReaderActivity : AppCompatActivity() {
.setCancelable(false)
.setPositiveButton(getString(R.string.yes)) { dialog, _ ->
saveData("${media.id}_save_progress", true)
- updateProgress(
+ updateProgress(
media,
MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!)
.toString()
diff --git a/app/src/main/res/layout/item_image.xml b/app/src/main/res/layout/item_image.xml
index 64055928..72a66e12 100644
--- a/app/src/main/res/layout/item_image.xml
+++ b/app/src/main/res/layout/item_image.xml
@@ -7,7 +7,7 @@
app:gest_disableGestures="true"
app:gest_rotationEnabled="true"
app:gest_restrictRotation="true"
- app:gest_doubleTapZoom="3"
+ app:gest_doubleTapZoom="1.5"
app:gest_maxZoom="6">
Date: Sat, 30 Dec 2023 05:12:46 -0600
Subject: [PATCH 5/6] first working version of anime downloads
---
app/src/main/AndroidManifest.xml | 2 +-
.../ani/dantotsu/download/DownloadsManager.kt | 104 +++++++++--------
.../download/anime/AnimeDownloaderService.kt | 55 ++++++---
.../download/manga/MangaDownloaderService.kt | 6 +-
.../download/manga/OfflineMangaFragment.kt | 43 ++++---
.../download/novel/NovelDownloaderService.kt | 6 +-
...Service.kt => ExoplayerDownloadService.kt} | 2 +-
.../ani/dantotsu/download/video/Helper.kt | 90 ++++++++++++---
.../ani/dantotsu/media/anime/ExoplayerView.kt | 64 ++++++++---
.../dantotsu/media/manga/MangaReadFragment.kt | 12 +-
.../dantotsu/media/novel/NovelReadFragment.kt | 14 +--
.../java/ani/dantotsu/parsers/AnimeSources.kt | 10 +-
.../ani/dantotsu/parsers/AniyomiAdapter.kt | 15 +--
.../java/ani/dantotsu/parsers/BaseSources.kt | 13 +++
.../java/ani/dantotsu/parsers/MangaSources.kt | 3 -
.../dantotsu/parsers/OfflineAnimeParser.kt | 106 ++++++++++++++++++
.../dantotsu/parsers/OfflineMangaParser.kt | 2 +-
.../dantotsu/parsers/OfflineNovelParser.kt | 5 +-
.../ani/dantotsu/parsers/VideoExtractor.kt | 6 +-
19 files changed, 402 insertions(+), 156 deletions(-)
rename app/src/main/java/ani/dantotsu/download/video/{MyDownloadService.kt => ExoplayerDownloadService.kt} (91%)
create mode 100644 app/src/main/java/ani/dantotsu/parsers/OfflineAnimeParser.kt
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 23f4b20d..8ab6c3ca 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -273,7 +273,7 @@
android:permission="android.permission.BIND_REMOTEVIEWS"
android:exported="true" />
diff --git a/app/src/main/java/ani/dantotsu/download/DownloadsManager.kt b/app/src/main/java/ani/dantotsu/download/DownloadsManager.kt
index b3536ea4..d6247ec5 100644
--- a/app/src/main/java/ani/dantotsu/download/DownloadsManager.kt
+++ b/app/src/main/java/ani/dantotsu/download/DownloadsManager.kt
@@ -15,43 +15,43 @@ class DownloadsManager(private val context: Context) {
private val gson = Gson()
private val downloadsList = loadDownloads().toMutableList()
- val mangaDownloads: List
- get() = downloadsList.filter { it.type == Download.Type.MANGA }
- val animeDownloads: List
- get() = downloadsList.filter { it.type == Download.Type.ANIME }
- val novelDownloads: List
- get() = downloadsList.filter { it.type == Download.Type.NOVEL }
+ val mangaDownloadedTypes: List
+ get() = downloadsList.filter { it.type == DownloadedType.Type.MANGA }
+ val animeDownloadedTypes: List
+ get() = downloadsList.filter { it.type == DownloadedType.Type.ANIME }
+ val novelDownloadedTypes: List
+ get() = downloadsList.filter { it.type == DownloadedType.Type.NOVEL }
private fun saveDownloads() {
val jsonString = gson.toJson(downloadsList)
prefs.edit().putString("downloads_key", jsonString).apply()
}
- private fun loadDownloads(): List {
+ private fun loadDownloads(): List {
val jsonString = prefs.getString("downloads_key", null)
return if (jsonString != null) {
- val type = object : TypeToken>() {}.type
+ val type = object : TypeToken>() {}.type
gson.fromJson(jsonString, type)
} else {
emptyList()
}
}
- fun addDownload(download: Download) {
- downloadsList.add(download)
+ fun addDownload(downloadedType: DownloadedType) {
+ downloadsList.add(downloadedType)
saveDownloads()
}
- fun removeDownload(download: Download) {
- downloadsList.remove(download)
- removeDirectory(download)
+ fun removeDownload(downloadedType: DownloadedType) {
+ downloadsList.remove(downloadedType)
+ removeDirectory(downloadedType)
saveDownloads()
}
- fun removeMedia(title: String, type: Download.Type) {
- val subDirectory = if (type == Download.Type.MANGA) {
+ fun removeMedia(title: String, type: DownloadedType.Type) {
+ val subDirectory = if (type == DownloadedType.Type.MANGA) {
"Manga"
- } else if (type == Download.Type.ANIME) {
+ } else if (type == DownloadedType.Type.ANIME) {
"Anime"
} else {
"Novel"
@@ -76,16 +76,16 @@ class DownloadsManager(private val context: Context) {
}
private fun cleanDownloads() {
- cleanDownload(Download.Type.MANGA)
- cleanDownload(Download.Type.ANIME)
- cleanDownload(Download.Type.NOVEL)
+ cleanDownload(DownloadedType.Type.MANGA)
+ cleanDownload(DownloadedType.Type.ANIME)
+ cleanDownload(DownloadedType.Type.NOVEL)
}
- private fun cleanDownload(type: Download.Type) {
+ private fun cleanDownload(type: DownloadedType.Type) {
// remove all folders that are not in the downloads list
- val subDirectory = if (type == Download.Type.MANGA) {
+ val subDirectory = if (type == DownloadedType.Type.MANGA) {
"Manga"
- } else if (type == Download.Type.ANIME) {
+ } else if (type == DownloadedType.Type.ANIME) {
"Anime"
} else {
"Novel"
@@ -94,18 +94,18 @@ class DownloadsManager(private val context: Context) {
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
"Dantotsu/$subDirectory"
)
- val downloadsSubList = if (type == Download.Type.MANGA) {
- mangaDownloads
- } else if (type == Download.Type.ANIME) {
- animeDownloads
+ val downloadsSubLists = if (type == DownloadedType.Type.MANGA) {
+ mangaDownloadedTypes
+ } else if (type == DownloadedType.Type.ANIME) {
+ animeDownloadedTypes
} else {
- novelDownloads
+ novelDownloadedTypes
}
if (directory.exists()) {
val files = directory.listFiles()
if (files != null) {
for (file in files) {
- if (!downloadsSubList.any { it.title == file.name }) {
+ if (!downloadsSubLists.any { it.title == file.name }) {
val deleted = file.deleteRecursively()
}
}
@@ -122,7 +122,7 @@ class DownloadsManager(private val context: Context) {
}
}
- fun saveDownloadsListToJSONFileInDownloadsFolder(downloadsList: List) //for debugging
+ fun saveDownloadsListToJSONFileInDownloadsFolder(downloadsList: List) //for debugging
{
val jsonString = gson.toJson(downloadsList)
val file = File(
@@ -138,25 +138,33 @@ class DownloadsManager(private val context: Context) {
file.writeText(jsonString)
}
- fun queryDownload(download: Download): Boolean {
- return downloadsList.contains(download)
+ fun queryDownload(downloadedType: DownloadedType): Boolean {
+ return downloadsList.contains(downloadedType)
}
- private fun removeDirectory(download: Download) {
- val directory = if (download.type == Download.Type.MANGA) {
+ fun queryDownload(title: String, chapter: String, type: DownloadedType.Type? = null): Boolean {
+ return if (type == null) {
+ downloadsList.any { it.title == title && it.chapter == chapter }
+ } else {
+ downloadsList.any { it.title == title && it.chapter == chapter && it.type == type }
+ }
+ }
+
+ private fun removeDirectory(downloadedType: DownloadedType) {
+ val directory = if (downloadedType.type == DownloadedType.Type.MANGA) {
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
- "Dantotsu/Manga/${download.title}/${download.chapter}"
+ "Dantotsu/Manga/${downloadedType.title}/${downloadedType.chapter}"
)
- } else if (download.type == Download.Type.ANIME) {
+ } else if (downloadedType.type == DownloadedType.Type.ANIME) {
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
- "Dantotsu/Anime/${download.title}/${download.chapter}"
+ "Dantotsu/Anime/${downloadedType.title}/${downloadedType.chapter}"
)
} else {
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
- "Dantotsu/Novel/${download.title}/${download.chapter}"
+ "Dantotsu/Novel/${downloadedType.title}/${downloadedType.chapter}"
)
}
@@ -173,26 +181,26 @@ class DownloadsManager(private val context: Context) {
}
}
- fun exportDownloads(download: Download) { //copies to the downloads folder available to the user
- val directory = if (download.type == Download.Type.MANGA) {
+ fun exportDownloads(downloadedType: DownloadedType) { //copies to the downloads folder available to the user
+ val directory = if (downloadedType.type == DownloadedType.Type.MANGA) {
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
- "Dantotsu/Manga/${download.title}/${download.chapter}"
+ "Dantotsu/Manga/${downloadedType.title}/${downloadedType.chapter}"
)
- } else if (download.type == Download.Type.ANIME) {
+ } else if (downloadedType.type == DownloadedType.Type.ANIME) {
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
- "Dantotsu/Anime/${download.title}/${download.chapter}"
+ "Dantotsu/Anime/${downloadedType.title}/${downloadedType.chapter}"
)
} else {
File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
- "Dantotsu/Novel/${download.title}/${download.chapter}"
+ "Dantotsu/Novel/${downloadedType.title}/${downloadedType.chapter}"
)
}
val destination = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
- "Dantotsu/${download.title}/${download.chapter}"
+ "Dantotsu/${downloadedType.title}/${downloadedType.chapter}"
)
if (directory.exists()) {
val copied = directory.copyRecursively(destination, true)
@@ -206,10 +214,10 @@ class DownloadsManager(private val context: Context) {
}
}
- fun purgeDownloads(type: Download.Type) {
- val directory = if (type == Download.Type.MANGA) {
+ fun purgeDownloads(type: DownloadedType.Type) {
+ val directory = if (type == DownloadedType.Type.MANGA) {
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Manga")
- } else if (type == Download.Type.ANIME) {
+ } else if (type == DownloadedType.Type.ANIME) {
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Anime")
} else {
File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Dantotsu/Novel")
@@ -237,7 +245,7 @@ class DownloadsManager(private val context: Context) {
}
-data class Download(val title: String, val chapter: String, val type: Type) : Serializable {
+data class DownloadedType(val title: String, val chapter: String, val type: Type) : Serializable {
enum class Type {
MANGA,
ANIME,
diff --git a/app/src/main/java/ani/dantotsu/download/anime/AnimeDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/anime/AnimeDownloaderService.kt
index 256e9577..1519f8a8 100644
--- a/app/src/main/java/ani/dantotsu/download/anime/AnimeDownloaderService.kt
+++ b/app/src/main/java/ani/dantotsu/download/anime/AnimeDownloaderService.kt
@@ -8,7 +8,6 @@ import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.content.pm.ServiceInfo
-import android.graphics.Bitmap
import android.os.Build
import android.os.Environment
import android.os.IBinder
@@ -18,15 +17,15 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.media3.common.util.UnstableApi
+import androidx.media3.exoplayer.offline.Download
+import androidx.media3.exoplayer.offline.DownloadManager
import androidx.media3.exoplayer.offline.DownloadService
import ani.dantotsu.R
import ani.dantotsu.currActivity
-import ani.dantotsu.download.Download
+import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
-import ani.dantotsu.download.anime.AnimeDownloaderService
-import ani.dantotsu.download.anime.AnimeServiceDataSingleton
import ani.dantotsu.download.video.Helper
-import ani.dantotsu.download.video.MyDownloadService
+import ani.dantotsu.download.video.ExoplayerDownloadService
import ani.dantotsu.logger
import ani.dantotsu.media.Media
import ani.dantotsu.media.anime.AnimeWatchFragment
@@ -44,14 +43,11 @@ import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SChapterImpl
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Deferred
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.async
-import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@@ -161,7 +157,7 @@ class AnimeDownloaderService : Service() {
val url = AnimeServiceDataSingleton.downloadQueue.find { it.getTaskName() == taskName }?.video?.file?.url ?: ""
DownloadService.sendRemoveDownload(
this@AnimeDownloaderService,
- MyDownloadService::class.java,
+ ExoplayerDownloadService::class.java,
url,
false
)
@@ -220,16 +216,28 @@ class AnimeDownloaderService : Service() {
}
saveMediaInfo(task)
- downloadsManager.addDownload(
- Download(
- task.title,
- task.episode,
- Download.Type.ANIME,
- )
+ var continueDownload = false
+ downloadManager.addListener(
+ object : androidx.media3.exoplayer.offline.DownloadManager.Listener {
+ override fun onDownloadChanged(
+ downloadManager: DownloadManager,
+ download: Download,
+ finalException: Exception?
+ ) {
+ continueDownload = true
+ }
+ }
)
+ //set an async timeout of 30 seconds before setting continueDownload to true
+ launch {
+ kotlinx.coroutines.delay(30000)
+ continueDownload = true
+ }
+
+
// periodically check if the download is complete
- while (downloadManager.downloadIndex.getDownload(task.video.file.url) != null) {
+ while (downloadManager.downloadIndex.getDownload(task.video.file.url) != null || continueDownload == false) {
val download = downloadManager.downloadIndex.getDownload(task.video.file.url)
if (download != null) {
if (download.state == androidx.media3.exoplayer.offline.Download.STATE_FAILED) {
@@ -251,6 +259,13 @@ class AnimeDownloaderService : Service() {
task.getTaskName(),
task.video.file.url
).apply()
+ downloadsManager.addDownload(
+ DownloadedType(
+ task.title,
+ task.episode,
+ DownloadedType.Type.ANIME,
+ )
+ )
broadcastDownloadFinished(task.getTaskName())
break
}
@@ -284,9 +299,11 @@ class AnimeDownloaderService : Service() {
GlobalScope.launch(Dispatchers.IO) {
val directory = File(
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
- "Dantotsu/Anime/${task.title}"
+ "${DownloadsManager.animeLocation}/${task.title}"
)
+ val episodeDirectory = File(directory, task.episode)
if (!directory.exists()) directory.mkdirs()
+ if (!episodeDirectory.exists()) episodeDirectory.mkdirs()
val file = File(directory, "media.json")
val gson = GsonBuilder()
@@ -305,6 +322,9 @@ class AnimeDownloaderService : Service() {
if (media != null) {
media.cover = media.cover?.let { downloadImage(it, directory, "cover.jpg") }
media.banner = media.banner?.let { downloadImage(it, directory, "banner.jpg") }
+ if (task.episodeImage != null) {
+ downloadImage(task.episodeImage, episodeDirectory, "episodeImage.jpg")
+ }
val jsonString = gson.toJson(media)
withContext(Dispatchers.Main) {
@@ -395,6 +415,7 @@ class AnimeDownloaderService : Service() {
val video: Video,
val subtitle: Subtitle? = null,
val sourceMedia: Media? = null,
+ val episodeImage: String? = null,
val retries: Int = 2,
val simultaneousDownloads: Int = 2,
) {
diff --git a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
index fce89d3e..202a4b8f 100644
--- a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
+++ b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
@@ -18,7 +18,7 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import ani.dantotsu.R
-import ani.dantotsu.download.Download
+import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.logger
import ani.dantotsu.media.Media
@@ -246,10 +246,10 @@ class MangaDownloaderService : Service() {
saveMediaInfo(task)
downloadsManager.addDownload(
- Download(
+ DownloadedType(
task.title,
task.chapter,
- Download.Type.MANGA
+ DownloadedType.Type.MANGA
)
)
broadcastDownloadFinished(task.chapter)
diff --git a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt
index d405583c..1b7c031b 100644
--- a/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt
+++ b/app/src/main/java/ani/dantotsu/download/manga/OfflineMangaFragment.kt
@@ -2,7 +2,6 @@ package ani.dantotsu.download.manga
import android.animation.ObjectAnimator
import android.content.Context
-import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Build
@@ -23,20 +22,16 @@ import android.widget.GridView
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.cardview.widget.CardView
-import androidx.core.app.ActivityCompat.recreate
import androidx.fragment.app.Fragment
import ani.dantotsu.R
-import ani.dantotsu.Refresh
import ani.dantotsu.currActivity
import ani.dantotsu.currContext
-import ani.dantotsu.download.Download
+import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
-import ani.dantotsu.initActivity
import ani.dantotsu.logger
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.setSafeOnClickListener
-import ani.dantotsu.settings.SettingsActivity
import ani.dantotsu.settings.SettingsDialogFragment
import ani.dantotsu.snackString
import ani.dantotsu.statusBarHeight
@@ -168,8 +163,8 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
// Get the OfflineMangaModel that was clicked
val item = adapter.getItem(position) as OfflineMangaModel
val media =
- downloadManager.mangaDownloads.firstOrNull { it.title == item.title }
- ?: downloadManager.novelDownloads.firstOrNull { it.title == item.title }
+ downloadManager.mangaDownloadedTypes.firstOrNull { it.title == item.title }
+ ?: downloadManager.novelDownloadedTypes.firstOrNull { it.title == item.title }
media?.let {
startActivity(
Intent(requireContext(), MediaDetailsActivity::class.java)
@@ -184,10 +179,10 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
gridView.setOnItemLongClickListener { parent, view, position, id ->
// Get the OfflineMangaModel that was clicked
val item = adapter.getItem(position) as OfflineMangaModel
- val type: Download.Type = if (downloadManager.mangaDownloads.any { it.title == item.title }) {
- Download.Type.MANGA
+ val type: DownloadedType.Type = if (downloadManager.mangaDownloadedTypes.any { it.title == item.title }) {
+ DownloadedType.Type.MANGA
} else {
- Download.Type.NOVEL
+ DownloadedType.Type.NOVEL
}
// Alert dialog to confirm deletion
val builder = androidx.appcompat.app.AlertDialog.Builder(requireContext(), R.style.MyPopup)
@@ -292,19 +287,19 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
private fun getDownloads() {
downloads = listOf()
- val mangaTitles = downloadManager.mangaDownloads.map { it.title }.distinct()
+ val mangaTitles = downloadManager.mangaDownloadedTypes.map { it.title }.distinct()
val newMangaDownloads = mutableListOf()
for (title in mangaTitles) {
- val _downloads = downloadManager.mangaDownloads.filter { it.title == title }
+ val _downloads = downloadManager.mangaDownloadedTypes.filter { it.title == title }
val download = _downloads.first()
val offlineMangaModel = loadOfflineMangaModel(download)
newMangaDownloads += offlineMangaModel
}
downloads = newMangaDownloads
- val novelTitles = downloadManager.novelDownloads.map { it.title }.distinct()
+ val novelTitles = downloadManager.novelDownloadedTypes.map { it.title }.distinct()
val newNovelDownloads = mutableListOf()
for (title in novelTitles) {
- val _downloads = downloadManager.novelDownloads.filter { it.title == title }
+ val _downloads = downloadManager.novelDownloadedTypes.filter { it.title == title }
val download = _downloads.first()
val offlineMangaModel = loadOfflineMangaModel(download)
newNovelDownloads += offlineMangaModel
@@ -313,17 +308,17 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
}
- private fun getMedia(download: Download): Media? {
- val type = if (download.type == Download.Type.MANGA) {
+ private fun getMedia(downloadedType: DownloadedType): Media? {
+ val type = if (downloadedType.type == DownloadedType.Type.MANGA) {
"Manga"
- } else if (download.type == Download.Type.ANIME) {
+ } else if (downloadedType.type == DownloadedType.Type.ANIME) {
"Anime"
} else {
"Novel"
}
val directory = File(
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
- "Dantotsu/$type/${download.title}"
+ "Dantotsu/$type/${downloadedType.title}"
)
//load media.json and convert to media class with gson
return try {
@@ -343,23 +338,23 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
}
}
- private fun loadOfflineMangaModel(download: Download): OfflineMangaModel {
- val type = if (download.type == Download.Type.MANGA) {
+ private fun loadOfflineMangaModel(downloadedType: DownloadedType): OfflineMangaModel {
+ val type = if (downloadedType.type == DownloadedType.Type.MANGA) {
"Manga"
- } else if (download.type == Download.Type.ANIME) {
+ } else if (downloadedType.type == DownloadedType.Type.ANIME) {
"Anime"
} else {
"Novel"
}
val directory = File(
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
- "Dantotsu/$type/${download.title}"
+ "Dantotsu/$type/${downloadedType.title}"
)
//load media.json and convert to media class with gson
try {
val media = File(directory, "media.json")
val mediaJson = media.readText()
- val mediaModel = getMedia(download)!!
+ val mediaModel = getMedia(downloadedType)!!
val cover = File(directory, "cover.jpg")
val coverUri: Uri? = if (cover.exists()) {
Uri.fromFile(cover)
diff --git a/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt
index e987cd25..d729ef57 100644
--- a/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt
+++ b/app/src/main/java/ani/dantotsu/download/novel/NovelDownloaderService.kt
@@ -17,7 +17,7 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import ani.dantotsu.R
-import ani.dantotsu.download.Download
+import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.logger
import ani.dantotsu.media.Media
@@ -330,10 +330,10 @@ class NovelDownloaderService : Service() {
saveMediaInfo(task)
downloadsManager.addDownload(
- Download(
+ DownloadedType(
task.title,
task.chapter,
- Download.Type.NOVEL
+ DownloadedType.Type.NOVEL
)
)
broadcastDownloadFinished(task.originalLink)
diff --git a/app/src/main/java/ani/dantotsu/download/video/MyDownloadService.kt b/app/src/main/java/ani/dantotsu/download/video/ExoplayerDownloadService.kt
similarity index 91%
rename from app/src/main/java/ani/dantotsu/download/video/MyDownloadService.kt
rename to app/src/main/java/ani/dantotsu/download/video/ExoplayerDownloadService.kt
index 8c0d56d2..3d7a1802 100644
--- a/app/src/main/java/ani/dantotsu/download/video/MyDownloadService.kt
+++ b/app/src/main/java/ani/dantotsu/download/video/ExoplayerDownloadService.kt
@@ -11,7 +11,7 @@ import androidx.media3.exoplayer.scheduler.Scheduler
import ani.dantotsu.R
@UnstableApi
-class MyDownloadService : DownloadService(1, 2000, "download_service", R.string.downloads, 0) {
+class ExoplayerDownloadService : DownloadService(1, 2000, "download_service", R.string.downloads, 0) {
companion object {
private const val JOB_ID = 1
private const val FOREGROUND_NOTIFICATION_ID = 1
diff --git a/app/src/main/java/ani/dantotsu/download/video/Helper.kt b/app/src/main/java/ani/dantotsu/download/video/Helper.kt
index 056e96ee..4c10752d 100644
--- a/app/src/main/java/ani/dantotsu/download/video/Helper.kt
+++ b/app/src/main/java/ani/dantotsu/download/video/Helper.kt
@@ -3,6 +3,7 @@ package ani.dantotsu.download.video
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
+import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
@@ -12,6 +13,7 @@ import android.util.Log
import androidx.annotation.OptIn
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
+import androidx.core.content.ContextCompat.getString
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.MimeTypes
@@ -32,6 +34,8 @@ import androidx.media3.exoplayer.scheduler.Requirements
import androidx.media3.ui.TrackSelectionDialogBuilder
import ani.dantotsu.R
import ani.dantotsu.defaultHeaders
+import ani.dantotsu.download.DownloadedType
+import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.anime.AnimeDownloaderService
import ani.dantotsu.download.anime.AnimeServiceDataSingleton
import ani.dantotsu.logError
@@ -50,7 +54,8 @@ import java.util.concurrent.*
object Helper {
- var simpleCache: SimpleCache? = null
+ private var simpleCache: SimpleCache? = null
+
@SuppressLint("UnsafeOptInUsageError")
fun downloadVideo(context: Context, video: Video, subtitle: Subtitle?) {
val dataSourceFactory = DataSource.Factory {
@@ -96,18 +101,18 @@ object Helper {
)
downloadHelper.prepare(object : DownloadHelper.Callback {
override fun onPrepared(helper: DownloadHelper) {
- TrackSelectionDialogBuilder(
- context, "Select thingy", helper.getTracks(0).groups
+ /*TrackSelectionDialogBuilder( TODO: use this for subtitles
+ context, "Select Source", helper.getTracks(0).groups
) { _, overrides ->
val params = TrackSelectionParameters.Builder(context)
overrides.forEach {
params.addOverride(it.value)
}
helper.addTrackSelection(0, params.build())
- MyDownloadService
+ ExoplayerDownloadService
DownloadService.sendAddDownload(
context,
- MyDownloadService::class.java,
+ ExoplayerDownloadService::class.java,
helper.getDownloadRequest(null),
false
)
@@ -117,6 +122,14 @@ object Helper {
if (it.frameRate > 0f) it.height.toString() + "p" else it.height.toString() + "p (fps : N/A)"
}
build().show()
+ }*/
+ helper.getDownloadRequest(null).let {
+ DownloadService.sendAddDownload(
+ context,
+ ExoplayerDownloadService::class.java,
+ it,
+ false
+ )
}
}
@@ -149,7 +162,7 @@ object Helper {
}
val threadPoolSize = Runtime.getRuntime().availableProcessors()
val executorService = Executors.newFixedThreadPool(threadPoolSize)
- val downloadManager = DownloadManager(
+ val downloadManager = DownloadManager(
context,
database,
getSimpleCache(context),
@@ -160,15 +173,15 @@ object Helper {
Requirements(Requirements.NETWORK or Requirements.DEVICE_STORAGE_NOT_LOW)
maxParallelDownloads = 3
}
- downloadManager.addListener(
- object : DownloadManager.Listener { // Override methods of interest here.
+ downloadManager.addListener( //for testing
+ object : DownloadManager.Listener {
override fun onDownloadChanged(
downloadManager: DownloadManager,
download: Download,
finalException: Exception?
) {
if (download.state == Download.STATE_COMPLETED) {
- Log.e("Downloader", "Download Completed")
+ Log.e("Downloader", "Download Completed")
} else if (download.state == Download.STATE_FAILED) {
Log.e("Downloader", "Download Failed")
} else if (download.state == Download.STATE_STOPPED) {
@@ -199,6 +212,7 @@ object Helper {
return downloadDirectory!!
}
+ @OptIn(UnstableApi::class)
fun startAnimeDownloadService(
context: Context,
title: String,
@@ -224,16 +238,62 @@ object Helper {
subtitle,
sourceMedia
)
- AnimeServiceDataSingleton.downloadQueue.offer(downloadTask)
- if (!AnimeServiceDataSingleton.isServiceRunning) {
- val intent = Intent(context, AnimeDownloaderService::class.java)
- ContextCompat.startForegroundService(context, intent)
- AnimeServiceDataSingleton.isServiceRunning = true
+ val downloadsManger = Injekt.get()
+ val downloadCheck = downloadsManger
+ .queryDownload(title, episode, DownloadedType.Type.ANIME)
+
+ if (downloadCheck) {
+ AlertDialog.Builder(context)
+ .setTitle("Download Exists")
+ .setMessage("A download for this episode already exists. Do you want to overwrite it?")
+ .setPositiveButton("Yes") { _, _ ->
+ DownloadService.sendRemoveDownload(
+ context,
+ ExoplayerDownloadService::class.java,
+ context.getSharedPreferences(
+ getString(context, R.string.anime_downloads),
+ Context.MODE_PRIVATE
+ ).getString(
+ downloadTask.getTaskName(),
+ ""
+ ) ?: "",
+ false
+ )
+ context.getSharedPreferences(
+ getString(context, R.string.anime_downloads),
+ Context.MODE_PRIVATE
+ ).edit()
+ .remove(downloadTask.getTaskName())
+ .apply()
+ downloadsManger.removeDownload(
+ DownloadedType(
+ title,
+ episode,
+ DownloadedType.Type.ANIME
+ )
+ )
+ AnimeServiceDataSingleton.downloadQueue.offer(downloadTask)
+ if (!AnimeServiceDataSingleton.isServiceRunning) {
+ val intent = Intent(context, AnimeDownloaderService::class.java)
+ ContextCompat.startForegroundService(context, intent)
+ AnimeServiceDataSingleton.isServiceRunning = true
+ }
+ }
+ .setNegativeButton("No") { _, _ -> }
+ .show()
+ } else {
+ AnimeServiceDataSingleton.downloadQueue.offer(downloadTask)
+ if (!AnimeServiceDataSingleton.isServiceRunning) {
+ val intent = Intent(context, AnimeDownloaderService::class.java)
+ ContextCompat.startForegroundService(context, intent)
+ AnimeServiceDataSingleton.isServiceRunning = true
+ }
}
}
- @OptIn(UnstableApi::class) private fun getSimpleCache(context: Context): SimpleCache {
+ @OptIn(UnstableApi::class)
+ fun getSimpleCache(context: Context): SimpleCache {
return if (simpleCache == null) {
val downloadDirectory = File(getDownloadDirectory(context), DOWNLOAD_CONTENT_DIRECTORY)
val database = Injekt.get()
diff --git a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt
index 253080e2..8643d9de 100644
--- a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt
+++ b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt
@@ -4,6 +4,7 @@ import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.app.Dialog
+import android.app.DownloadManager
import android.app.PictureInPictureParams
import android.app.PictureInPictureUiState
import android.content.ActivityNotFoundException
@@ -97,7 +98,9 @@ import kotlin.math.min
import kotlin.math.roundToInt
import androidx.media3.cast.SessionAvailabilityListener
import androidx.media3.cast.CastPlayer
+import androidx.media3.exoplayer.offline.Download
import androidx.mediarouter.app.MediaRouteButton
+import ani.dantotsu.download.video.Helper
import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.gms.cast.framework.CastContext
@@ -150,6 +153,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
private var orientationListener: OrientationEventListener? = null
+ private var downloadId: String? = null
+
companion object {
var initialized = false
lateinit var media: Media
@@ -475,7 +480,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
if (isInitialized) {
isPlayerPlaying = exoPlayer.isPlaying
(exoPlay.drawable as Animatable?)?.start()
- if (isPlayerPlaying || castPlayer.isPlaying ) {
+ if (isPlayerPlaying || castPlayer.isPlaying) {
Glide.with(this).load(R.drawable.anim_play_to_pause).into(exoPlay)
exoPlayer.pause()
castPlayer.pause()
@@ -1115,7 +1120,21 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
if (settings.cursedSpeeds)
arrayOf(1f, 1.25f, 1.5f, 1.75f, 2f, 2.5f, 3f, 4f, 5f, 10f, 25f, 50f)
else
- arrayOf(0.25f, 0.33f, 0.5f, 0.66f, 0.75f, 1f, 1.15f, 1.25f, 1.33f, 1.5f, 1.66f, 1.75f, 2f)
+ arrayOf(
+ 0.25f,
+ 0.33f,
+ 0.5f,
+ 0.66f,
+ 0.75f,
+ 1f,
+ 1.15f,
+ 1.25f,
+ 1.33f,
+ 1.5f,
+ 1.66f,
+ 1.75f,
+ 2f
+ )
val speedsName = speeds.map { "${it}x" }.toTypedArray()
var curSpeed = loadData("${media.id}_speed", this) ?: settings.defaultSpeed
@@ -1292,7 +1311,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
if (video?.format == VideoType.CONTAINER || (loadData("settings_download_manager")
?: 0) != 0
) {
- but.visibility = View.VISIBLE
+ //but.visibility = View.VISIBLE TODO: not sure if this is needed
but.setOnClickListener {
download(this, episode, animeTitle.text.toString())
}
@@ -1317,8 +1336,9 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
dataSource
}
cacheFactory = CacheDataSource.Factory().apply {
- setCache(simpleCache)
+ setCache(Helper.getSimpleCache(this@ExoplayerView))
setUpstreamDataSourceFactory(dataSourceFactory)
+ setCacheWriteDataSinkFactory(null)
}
val mimeType = when (video?.format) {
@@ -1327,15 +1347,33 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
else -> MimeTypes.APPLICATION_MP4
}
- val builder = MediaItem.Builder().setUri(video!!.file.url).setMimeType(mimeType)
- logger("url: ${video!!.file.url}")
- logger("mimeType: $mimeType")
+ val downloadedMediaItem = if (ext.server.offline) {
+ val key = ext.server.name
+ downloadId = getSharedPreferences(getString(R.string.anime_downloads), MODE_PRIVATE)
+ .getString(key, null)
+ if (downloadId != null) {
+ Helper.downloadManager(this)
+ .downloadIndex.getDownload(downloadId!!)?.request?.toMediaItem()
+ } else {
+ snackString("Download not found")
+ null
+ }
+ } else null
- if (sub != null) {
- val listofnotnullsubs = immutableListOf(sub).filterNotNull()
- builder.setSubtitleConfigurations(listofnotnullsubs)
+ mediaItem = if (downloadedMediaItem == null) {
+ val builder = MediaItem.Builder().setUri(video!!.file.url).setMimeType(mimeType)
+ logger("url: ${video!!.file.url}")
+ logger("mimeType: $mimeType")
+
+ if (sub != null) {
+ val listofnotnullsubs = immutableListOf(sub).filterNotNull()
+ builder.setSubtitleConfigurations(listofnotnullsubs)
+ }
+ builder.build()
+ } else {
+ downloadedMediaItem
}
- mediaItem = builder.build()
+
//Source
exoSource.setOnClickListener {
@@ -1457,7 +1495,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
exoPlayer.release()
VideoCache.release()
mediaSession?.release()
- if(DiscordServiceRunningSingleton.running) {
+ if (DiscordServiceRunningSingleton.running) {
val stopIntent = Intent(this, DiscordService::class.java)
DiscordServiceRunningSingleton.running = false
stopService(stopIntent)
@@ -1594,7 +1632,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
if (isInitialized) {
if (exoPlayer.currentPosition.toFloat() / exoPlayer.duration > settings.watchPercentage) {
preloading = true
- nextEpisode(false) { i ->
+ nextEpisode(false) { i -> //TODO: make sure this works for offline episodes
val ep = episodes[episodeArr[currentEpisodeIndex + i]] ?: return@nextEpisode
val selected = media.selected ?: return@nextEpisode
lifecycleScope.launch(Dispatchers.IO) {
diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
index 9b70ef7f..241c979a 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
@@ -29,7 +29,7 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.*
import ani.dantotsu.databinding.FragmentAnimeWatchBinding
-import ani.dantotsu.download.Download
+import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.manga.MangaDownloaderService
import ani.dantotsu.download.manga.MangaServiceDataSingleton
@@ -166,7 +166,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
chapterAdapter =
MangaChapterAdapter(style ?: uiSettings.mangaDefaultView, media, this)
- for (download in downloadManager.mangaDownloads) {
+ for (download in downloadManager.mangaDownloadedTypes) {
chapterAdapter.stopDownload(download.chapter)
}
@@ -482,10 +482,10 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
fun onMangaChapterRemoveDownloadClick(i: String) {
downloadManager.removeDownload(
- Download(
+ DownloadedType(
media.nameMAL ?: media.nameRomaji,
i,
- Download.Type.MANGA
+ DownloadedType.Type.MANGA
)
)
chapterAdapter.deleteDownload(i)
@@ -500,10 +500,10 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
// Remove the download from the manager and update the UI
downloadManager.removeDownload(
- Download(
+ DownloadedType(
media.nameMAL ?: media.nameRomaji,
i,
- Download.Type.MANGA
+ DownloadedType.Type.MANGA
)
)
chapterAdapter.purgeDownload(i)
diff --git a/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt b/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt
index 7154412a..b3e14ed2 100644
--- a/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt
+++ b/app/src/main/java/ani/dantotsu/media/novel/NovelReadFragment.kt
@@ -22,7 +22,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.databinding.FragmentAnimeWatchBinding
-import ani.dantotsu.download.Download
+import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.novel.NovelDownloaderService
import ani.dantotsu.download.novel.NovelServiceDataSingleton
@@ -92,10 +92,10 @@ class NovelReadFragment : Fragment(),
override fun downloadedCheckWithStart(novel: ShowResponse): Boolean {
val downloadsManager = Injekt.get()
if (downloadsManager.queryDownload(
- Download(
+ DownloadedType(
media.nameMAL ?: media.nameRomaji,
novel.name,
- Download.Type.NOVEL
+ DownloadedType.Type.NOVEL
)
)
) {
@@ -124,10 +124,10 @@ class NovelReadFragment : Fragment(),
override fun downloadedCheck(novel: ShowResponse): Boolean {
val downloadsManager = Injekt.get()
return downloadsManager.queryDownload(
- Download(
+ DownloadedType(
media.nameMAL ?: media.nameRomaji,
novel.name,
- Download.Type.NOVEL
+ DownloadedType.Type.NOVEL
)
)
}
@@ -135,10 +135,10 @@ class NovelReadFragment : Fragment(),
override fun deleteDownload(novel: ShowResponse) {
val downloadsManager = Injekt.get()
downloadsManager.removeDownload(
- Download(
+ DownloadedType(
media.nameMAL ?: media.nameRomaji,
novel.name,
- Download.Type.NOVEL
+ DownloadedType.Type.NOVEL
)
)
}
diff --git a/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt b/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt
index c48a4b3e..dd3faaef 100644
--- a/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt
@@ -12,11 +12,17 @@ object AnimeSources : WatchSources() {
suspend fun init(fromExtensions: StateFlow>) {
// Initialize with the first value from StateFlow
val initialExtensions = fromExtensions.first()
- list = createParsersFromExtensions(initialExtensions)
+ list = createParsersFromExtensions(initialExtensions) + Lazier(
+ { OfflineAnimeParser() },
+ "Downloaded"
+ )
// Update as StateFlow emits new values
fromExtensions.collect { extensions ->
- list = createParsersFromExtensions(extensions)
+ list = createParsersFromExtensions(extensions) + Lazier(
+ { OfflineAnimeParser() },
+ "Downloaded"
+ )
}
}
diff --git a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
index 27aece5f..08c69b55 100644
--- a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
@@ -616,17 +616,18 @@ class VideoServerPassthrough(val videoServer: VideoServer) : VideoExtractor() {
val fileName = queryPairs.find { it.first == "file" }?.second ?: ""
format = getVideoType(fileName)
- if (format == null) {
- val networkHelper = Injekt.get()
- format = headRequest(videoUrl, networkHelper)
- }
+ // this solves a problem no one has, so I'm commenting it out for now
+ //if (format == null) {
+ // val networkHelper = Injekt.get()
+ // format = headRequest(videoUrl, networkHelper)
+ //}
}
- // If the format is still undetermined, log an error or handle it appropriately
+ // If the format is still undetermined, log an error
if (format == null) {
logger("Unknown video format: $videoUrl")
- FirebaseCrashlytics.getInstance()
- .recordException(Exception("Unknown video format: $videoUrl"))
+ //FirebaseCrashlytics.getInstance()
+ // .recordException(Exception("Unknown video format: $videoUrl"))
format = VideoType.CONTAINER
}
val headersMap: Map =
diff --git a/app/src/main/java/ani/dantotsu/parsers/BaseSources.kt b/app/src/main/java/ani/dantotsu/parsers/BaseSources.kt
index f8535772..95fd4d63 100644
--- a/app/src/main/java/ani/dantotsu/parsers/BaseSources.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/BaseSources.kt
@@ -46,6 +46,19 @@ abstract class WatchSources : BaseSources() {
sEpisode = it.sEpisode
)
}
+ } else if (parser is OfflineAnimeParser) {
+ parser.loadEpisodes(showLink, extra, SAnime.create()).forEach {
+ map[it.number] = Episode(
+ it.number,
+ it.link,
+ it.title,
+ it.description,
+ it.thumbnail,
+ it.isFiller,
+ extra = it.extra,
+ sEpisode = it.sEpisode
+ )
+ }
}
}
return map
diff --git a/app/src/main/java/ani/dantotsu/parsers/MangaSources.kt b/app/src/main/java/ani/dantotsu/parsers/MangaSources.kt
index 0f8a5642..3709ddaa 100644
--- a/app/src/main/java/ani/dantotsu/parsers/MangaSources.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/MangaSources.kt
@@ -7,9 +7,6 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first
object MangaSources : MangaReadSources() {
- // Instantiate the static parser
- private val offlineMangaParser by lazy { OfflineMangaParser() }
-
override var list: List> = emptyList()
suspend fun init(fromExtensions: StateFlow>) {
diff --git a/app/src/main/java/ani/dantotsu/parsers/OfflineAnimeParser.kt b/app/src/main/java/ani/dantotsu/parsers/OfflineAnimeParser.kt
new file mode 100644
index 00000000..dca2199e
--- /dev/null
+++ b/app/src/main/java/ani/dantotsu/parsers/OfflineAnimeParser.kt
@@ -0,0 +1,106 @@
+package ani.dantotsu.parsers
+
+import android.os.Environment
+import ani.dantotsu.currContext
+import ani.dantotsu.download.DownloadsManager
+import ani.dantotsu.logger
+import ani.dantotsu.media.anime.AnimeNameAdapter
+import eu.kanade.tachiyomi.animesource.model.SAnime
+import eu.kanade.tachiyomi.animesource.model.SEpisode
+import eu.kanade.tachiyomi.animesource.model.SEpisodeImpl
+import me.xdrop.fuzzywuzzy.FuzzySearch
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+import java.io.File
+
+class OfflineAnimeParser : AnimeParser() {
+ private val downloadManager = Injekt.get()
+
+ override val name = "Offline"
+ override val saveName = "Offline"
+ override val hostUrl = "Offline"
+ override val isDubAvailableSeparately = false
+ override val isNSFW = false
+
+ override suspend fun loadEpisodes(
+ animeLink: String,
+ extra: Map?,
+ sAnime: SAnime
+ ): List {
+ val directory = File(
+ currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
+ "${DownloadsManager.animeLocation}/$animeLink"
+ )
+ //get all of the folder names and add them to the list
+ val episodes = mutableListOf()
+ if (directory.exists()) {
+ directory.listFiles()?.forEach {
+ if (it.isDirectory) {
+ val episode = Episode(
+ it.name,
+ "$animeLink - ${it.name}",
+ it.name,
+ null,
+ null,
+ sEpisode = SEpisodeImpl()
+ )
+ episodes.add(episode)
+ }
+ }
+ episodes.sortBy { AnimeNameAdapter.findEpisodeNumber(it.number) }
+ return episodes
+ }
+ return emptyList()
+ }
+
+ override suspend fun loadVideoServers(
+ episodeLink: String,
+ extra: Map?,
+ sEpisode: SEpisode
+ ): List {
+ return listOf(
+ VideoServer(
+ episodeLink,
+ offline = true
+ )
+ )
+ }
+
+
+ override suspend fun search(query: String): List {
+ val titles = downloadManager.animeDownloadedTypes.map { it.title }.distinct()
+ val returnTitles: MutableList = mutableListOf()
+ for (title in titles) {
+ if (FuzzySearch.ratio(title.lowercase(), query.lowercase()) > 80) {
+ returnTitles.add(title)
+ }
+ }
+ val returnList: MutableList = mutableListOf()
+ for (title in returnTitles) {
+ returnList.add(ShowResponse(title, title, title))
+ }
+ return returnList
+ }
+
+ override suspend fun getVideoExtractor(server: VideoServer): VideoExtractor {
+ return OfflineVideoExtractor(server)
+ }
+
+}
+
+class OfflineVideoExtractor(val videoServer: VideoServer) : VideoExtractor() {
+ override val server: VideoServer
+ get() = videoServer
+
+ override suspend fun extract(): VideoContainer {
+ val sublist = emptyList()
+ //we need to return a "fake" video so that the app doesn't crash
+ val video = Video(
+ null,
+ VideoType.CONTAINER,
+ "",
+ )
+ return VideoContainer(listOf(video), sublist)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ani/dantotsu/parsers/OfflineMangaParser.kt b/app/src/main/java/ani/dantotsu/parsers/OfflineMangaParser.kt
index 218a0f9a..deffb420 100644
--- a/app/src/main/java/ani/dantotsu/parsers/OfflineMangaParser.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/OfflineMangaParser.kt
@@ -76,7 +76,7 @@ class OfflineMangaParser : MangaParser() {
}
override suspend fun search(query: String): List {
- val titles = downloadManager.mangaDownloads.map { it.title }.distinct()
+ val titles = downloadManager.mangaDownloadedTypes.map { it.title }.distinct()
val returnTitles: MutableList = mutableListOf()
for (title in titles) {
if (FuzzySearch.ratio(title.lowercase(), query.lowercase()) > 80) {
diff --git a/app/src/main/java/ani/dantotsu/parsers/OfflineNovelParser.kt b/app/src/main/java/ani/dantotsu/parsers/OfflineNovelParser.kt
index ca47b50a..fece57e2 100644
--- a/app/src/main/java/ani/dantotsu/parsers/OfflineNovelParser.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/OfflineNovelParser.kt
@@ -3,10 +3,7 @@ package ani.dantotsu.parsers
import android.os.Environment
import ani.dantotsu.currContext
import ani.dantotsu.download.DownloadsManager
-import ani.dantotsu.logger
import ani.dantotsu.media.manga.MangaNameAdapter
-import eu.kanade.tachiyomi.source.model.SChapter
-import eu.kanade.tachiyomi.source.model.SManga
import me.xdrop.fuzzywuzzy.FuzzySearch
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -53,7 +50,7 @@ class OfflineNovelParser: NovelParser() {
}
override suspend fun search(query: String): List {
- val titles = downloadManager.novelDownloads.map { it.title }.distinct()
+ val titles = downloadManager.novelDownloadedTypes.map { it.title }.distinct()
val returnTitles: MutableList = mutableListOf()
for (title in titles) {
if (FuzzySearch.ratio(title.lowercase(), query.lowercase()) > 80) {
diff --git a/app/src/main/java/ani/dantotsu/parsers/VideoExtractor.kt b/app/src/main/java/ani/dantotsu/parsers/VideoExtractor.kt
index ce56182c..c98b8a42 100644
--- a/app/src/main/java/ani/dantotsu/parsers/VideoExtractor.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/VideoExtractor.kt
@@ -57,11 +57,15 @@ data class VideoServer(
val name: String,
val embed: FileUrl,
val extraData: Map? = null,
- val video: eu.kanade.tachiyomi.animesource.model.Video? = null
+ val video: eu.kanade.tachiyomi.animesource.model.Video? = null,
+ val offline: Boolean = false
) : Serializable {
constructor(name: String, embedUrl: String, extraData: Map? = null)
: this(name, FileUrl(embedUrl), extraData)
+ constructor(name: String, offline: Boolean)
+ : this(name, FileUrl(""), null, null, offline)
+
constructor(
name: String,
embedUrl: String,
From 7fae64bee91cedb27f0033d2c918283f9b0e8df3 Mon Sep 17 00:00:00 2001
From: Sadwhy <99601717+Sadwhy@users.noreply.github.com>
Date: Sat, 30 Dec 2023 17:13:15 +0600
Subject: [PATCH 6/6] Ignore readme on workflow (#107)
---
.github/workflows/beta.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/beta.yml b/.github/workflows/beta.yml
index 1f24338c..e54229eb 100644
--- a/.github/workflows/beta.yml
+++ b/.github/workflows/beta.yml
@@ -4,6 +4,8 @@ on:
push:
branches:
- dev
+ paths-ignore:
+ - '**/README.md'
jobs:
build: