mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-08-02 22:30:40 +00:00
Fix issues with attempting to download sources that are not supported (including mixed playlists)
This commit is contained in:
parent
67e29999ef
commit
8bb1ff87c0
10 changed files with 113 additions and 26 deletions
|
@ -0,0 +1,15 @@
|
||||||
|
package com.futo.platformplayer
|
||||||
|
|
||||||
|
import com.futo.platformplayer.api.media.models.streams.IVideoSourceDescriptor
|
||||||
|
import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor
|
||||||
|
import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource
|
||||||
|
import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource
|
||||||
|
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
|
||||||
|
import com.futo.platformplayer.helpers.VideoHelper
|
||||||
|
|
||||||
|
fun IPlatformVideoDetails.isDownloadable(): Boolean = VideoHelper.isDownloadable(this);
|
||||||
|
fun IVideoSource.isDownloadable(): Boolean = VideoHelper.isDownloadable(this);
|
||||||
|
fun IAudioSource.isDownloadable(): Boolean = VideoHelper.isDownloadable(this);
|
||||||
|
|
||||||
|
|
||||||
|
fun IVideoSourceDescriptor.hasAnySource(): Boolean = this.videoSources.any() || (this is VideoUnMuxedSourceDescriptor && this.audioSources.any());
|
|
@ -4,7 +4,6 @@ import android.content.ContentResolver
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.futo.platformplayer.api.http.ManagedHttpClient
|
import com.futo.platformplayer.api.http.ManagedHttpClient
|
||||||
import com.futo.platformplayer.api.http.server.handlers.HttpConstantHandler
|
|
||||||
import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor
|
import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor
|
||||||
import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlSource
|
import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlSource
|
||||||
import com.futo.platformplayer.api.media.models.streams.sources.IVideoUrlSource
|
import com.futo.platformplayer.api.media.models.streams.sources.IVideoUrlSource
|
||||||
|
@ -29,7 +28,6 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class UISlideOverlays {
|
class UISlideOverlays {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -68,6 +66,12 @@ class UISlideOverlays {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!VideoHelper.isDownloadable(video)) {
|
||||||
|
Logger.i(TAG, "Attempted to open downloads without valid sources for [${video.name}]: ${video.url}");
|
||||||
|
UIDialogs.toast( "No downloadable sources (yet)");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
items.add(SlideUpMenuGroup(container.context, "Video", videoSources,
|
items.add(SlideUpMenuGroup(container.context, "Video", videoSources,
|
||||||
listOf(listOf(SlideUpMenuItem(container.context, R.drawable.ic_movie, "None", "Audio Only", "none", {
|
listOf(listOf(SlideUpMenuItem(container.context, R.drawable.ic_movie, "None", "Audio Only", "none", {
|
||||||
selectedVideo = null;
|
selectedVideo = null;
|
||||||
|
@ -76,7 +80,7 @@ class UISlideOverlays {
|
||||||
menu?.setOk("Download");
|
menu?.setOk("Download");
|
||||||
}, false)) +
|
}, false)) +
|
||||||
videoSources
|
videoSources
|
||||||
.filter { it is IVideoUrlSource }
|
.filter { it.isDownloadable() }
|
||||||
.map {
|
.map {
|
||||||
SlideUpMenuItem(container.context, R.drawable.ic_movie, it.name, "${it.width}x${it.height}", it, {
|
SlideUpMenuItem(container.context, R.drawable.ic_movie, it.name, "${it.width}x${it.height}", it, {
|
||||||
selectedVideo = it as IVideoUrlSource;
|
selectedVideo = it as IVideoUrlSource;
|
||||||
|
@ -88,14 +92,14 @@ class UISlideOverlays {
|
||||||
));
|
));
|
||||||
|
|
||||||
if(Settings.instance.downloads.getDefaultVideoQualityPixels() > 0 && videoSources.size > 0)
|
if(Settings.instance.downloads.getDefaultVideoQualityPixels() > 0 && videoSources.size > 0)
|
||||||
selectedVideo = VideoHelper.selectBestVideoSource(videoSources.filter { it is IVideoUrlSource }.asIterable(),
|
selectedVideo = VideoHelper.selectBestVideoSource(videoSources.filter { it.isDownloadable() }.asIterable(),
|
||||||
Settings.instance.downloads.getDefaultVideoQualityPixels(),
|
Settings.instance.downloads.getDefaultVideoQualityPixels(),
|
||||||
FutoVideoPlayerBase.PREFERED_VIDEO_CONTAINERS) as IVideoUrlSource;
|
FutoVideoPlayerBase.PREFERED_VIDEO_CONTAINERS) as IVideoUrlSource;
|
||||||
|
|
||||||
|
|
||||||
audioSources?.let { audioSources ->
|
audioSources?.let { audioSources ->
|
||||||
items.add(SlideUpMenuGroup(container.context, "Audio", audioSources, audioSources
|
items.add(SlideUpMenuGroup(container.context, "Audio", audioSources, audioSources
|
||||||
.filter { it is IAudioUrlSource }
|
.filter { VideoHelper.isDownloadable(it) }
|
||||||
.map {
|
.map {
|
||||||
SlideUpMenuItem(container.context, R.drawable.ic_music, it.name, "${it.bitrate}", it, {
|
SlideUpMenuItem(container.context, R.drawable.ic_music, it.name, "${it.bitrate}", it, {
|
||||||
selectedAudio = it as IAudioUrlSource;
|
selectedAudio = it as IAudioUrlSource;
|
||||||
|
@ -111,7 +115,7 @@ class UISlideOverlays {
|
||||||
menu?.selectOption(asources, preferredAudioSource);
|
menu?.selectOption(asources, preferredAudioSource);
|
||||||
|
|
||||||
|
|
||||||
selectedAudio = VideoHelper.selectBestAudioSource(audioSources.filter { it is IAudioUrlSource }.asIterable(),
|
selectedAudio = VideoHelper.selectBestAudioSource(audioSources.filter { it.isDownloadable() }.asIterable(),
|
||||||
FutoVideoPlayerBase.PREFERED_AUDIO_CONTAINERS,
|
FutoVideoPlayerBase.PREFERED_AUDIO_CONTAINERS,
|
||||||
Settings.instance.playback.getPrimaryLanguage(container.context),
|
Settings.instance.playback.getPrimaryLanguage(container.context),
|
||||||
if(Settings.instance.downloads.isHighBitrateDefault()) 9999999 else 1) as IAudioUrlSource?;
|
if(Settings.instance.downloads.isHighBitrateDefault()) 9999999 else 1) as IAudioUrlSource?;
|
||||||
|
|
|
@ -9,7 +9,7 @@ import com.futo.platformplayer.getOrNull
|
||||||
import com.futo.platformplayer.getOrThrow
|
import com.futo.platformplayer.getOrThrow
|
||||||
import com.futo.platformplayer.orNull
|
import com.futo.platformplayer.orNull
|
||||||
|
|
||||||
class JSHLSManifestAudioSource : IAudioUrlSource, IHLSManifestAudioSource, JSSource {
|
class JSHLSManifestAudioSource : IHLSManifestAudioSource, JSSource {
|
||||||
override val container : String get() = "application/vnd.apple.mpegurl";
|
override val container : String get() = "application/vnd.apple.mpegurl";
|
||||||
override val codec: String = "HLS";
|
override val codec: String = "HLS";
|
||||||
override val name : String;
|
override val name : String;
|
||||||
|
@ -31,9 +31,6 @@ class JSHLSManifestAudioSource : IAudioUrlSource, IHLSManifestAudioSource, JSSou
|
||||||
priority = obj.getOrNull(config, "priority", contextName) ?: false;
|
priority = obj.getOrNull(config, "priority", contextName) ?: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAudioUrl(): String {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromV8HLSNullable(config: IV8PluginConfig, obj: V8Value?) : JSHLSManifestAudioSource? = obj.orNull { fromV8HLS(config, it as V8ValueObject) };
|
fun fromV8HLSNullable(config: IV8PluginConfig, obj: V8Value?) : JSHLSManifestAudioSource? = obj.orNull { fromV8HLS(config, it as V8ValueObject) };
|
||||||
|
|
|
@ -7,7 +7,7 @@ import com.futo.platformplayer.engine.IV8PluginConfig
|
||||||
import com.futo.platformplayer.getOrNull
|
import com.futo.platformplayer.getOrNull
|
||||||
import com.futo.platformplayer.getOrThrow
|
import com.futo.platformplayer.getOrThrow
|
||||||
|
|
||||||
class JSHLSManifestSource : IVideoUrlSource, IHLSManifestSource, JSSource {
|
class JSHLSManifestSource : IHLSManifestSource, JSSource {
|
||||||
override val width : Int = 0;
|
override val width : Int = 0;
|
||||||
override val height : Int = 0;
|
override val height : Int = 0;
|
||||||
override val container : String get() = "application/vnd.apple.mpegurl";
|
override val container : String get() = "application/vnd.apple.mpegurl";
|
||||||
|
@ -28,8 +28,4 @@ class JSHLSManifestSource : IVideoUrlSource, IHLSManifestSource, JSSource {
|
||||||
|
|
||||||
priority = obj.getOrNull(config, "priority", contextName) ?: false;
|
priority = obj.getOrNull(config, "priority", contextName) ?: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getVideoUrl(): String {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,8 +1,19 @@
|
||||||
package com.futo.platformplayer.downloads
|
package com.futo.platformplayer.downloads
|
||||||
|
|
||||||
|
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
|
||||||
|
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
data class PlaylistDownloadDescriptor(
|
data class PlaylistDownloadDescriptor(
|
||||||
val id: String,
|
val id: String,
|
||||||
val targetPxCount: Long?,
|
val targetPxCount: Long?,
|
||||||
val targetBitrate: Long?
|
val targetBitrate: Long?
|
||||||
);
|
) {
|
||||||
|
var preventDownload: MutableList<String> = arrayListOf();
|
||||||
|
|
||||||
|
fun getPreventDownloadList(): List<String> = synchronized(preventDownload){ preventDownload };
|
||||||
|
fun shouldDownload(video: IPlatformVideo): Boolean {
|
||||||
|
synchronized(preventDownload) {
|
||||||
|
return !preventDownload.contains(video.url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,8 +13,11 @@ import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
|
||||||
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo
|
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo
|
||||||
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideoDetails
|
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideoDetails
|
||||||
import com.futo.platformplayer.constructs.Event1
|
import com.futo.platformplayer.constructs.Event1
|
||||||
|
import com.futo.platformplayer.exceptions.DownloadException
|
||||||
|
import com.futo.platformplayer.hasAnySource
|
||||||
import com.futo.platformplayer.helpers.FileHelper.Companion.sanitizeFileName
|
import com.futo.platformplayer.helpers.FileHelper.Companion.sanitizeFileName
|
||||||
import com.futo.platformplayer.helpers.VideoHelper
|
import com.futo.platformplayer.helpers.VideoHelper
|
||||||
|
import com.futo.platformplayer.isDownloadable
|
||||||
import com.futo.platformplayer.serializers.OffsetDateTimeNullableSerializer
|
import com.futo.platformplayer.serializers.OffsetDateTimeNullableSerializer
|
||||||
import com.futo.platformplayer.serializers.OffsetDateTimeSerializer
|
import com.futo.platformplayer.serializers.OffsetDateTimeSerializer
|
||||||
import com.futo.platformplayer.toHumanBitrate
|
import com.futo.platformplayer.toHumanBitrate
|
||||||
|
@ -147,27 +150,37 @@ class VideoDownload {
|
||||||
if(original !is IPlatformVideoDetails)
|
if(original !is IPlatformVideoDetails)
|
||||||
throw IllegalStateException("Original content is not media?");
|
throw IllegalStateException("Original content is not media?");
|
||||||
|
|
||||||
|
if(original.video.hasAnySource() && !original.isDownloadable()) {
|
||||||
|
Logger.i(TAG, "Attempted to download unsupported video [${original.name}]:${original.url}");
|
||||||
|
throw DownloadException("Unsupported video for downloading", false);
|
||||||
|
}
|
||||||
|
|
||||||
videoDetails = SerializedPlatformVideoDetails.fromVideo(original, if (subtitleSource != null) listOf(subtitleSource!!) else listOf());
|
videoDetails = SerializedPlatformVideoDetails.fromVideo(original, if (subtitleSource != null) listOf(subtitleSource!!) else listOf());
|
||||||
if(videoSource == null && targetPixelCount != null) {
|
if(videoSource == null && targetPixelCount != null) {
|
||||||
val vsource = VideoHelper.selectBestVideoSource(videoDetails!!.video, targetPixelCount!!.toInt(), arrayOf())
|
val vsource = VideoHelper.selectBestVideoSource(videoDetails!!.video, targetPixelCount!!.toInt(), arrayOf())
|
||||||
?: throw IllegalStateException("Could not find a valid video source for video");
|
// ?: throw IllegalStateException("Could not find a valid video source for video");
|
||||||
if(vsource is IVideoUrlSource)
|
if(vsource != null) {
|
||||||
videoSource = VideoUrlSource.fromUrlSource(vsource);
|
if (vsource is IVideoUrlSource)
|
||||||
else
|
videoSource = VideoUrlSource.fromUrlSource(vsource);
|
||||||
throw IllegalStateException("Download video source is not a url source");
|
else
|
||||||
|
throw DownloadException("Video source is not supported for downloading (yet)", false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(audioSource == null && targetBitrate != null) {
|
if(audioSource == null && targetBitrate != null) {
|
||||||
val asource = VideoHelper.selectBestAudioSource(videoDetails!!.video, arrayOf(), null, targetPixelCount)
|
val asource = VideoHelper.selectBestAudioSource(videoDetails!!.video, arrayOf(), null, targetPixelCount)
|
||||||
?: if(videoSource != null ) null
|
?: if(videoSource != null ) null
|
||||||
else throw IllegalStateException("Could not find a valid audio source for video");
|
else throw DownloadException("Could not find a valid video or audio source for download")
|
||||||
if(asource == null)
|
if(asource == null)
|
||||||
audioSource = null;
|
audioSource = null;
|
||||||
else if(asource is IAudioUrlSource)
|
else if(asource is IAudioUrlSource)
|
||||||
audioSource = AudioUrlSource.fromUrlSource(asource);
|
audioSource = AudioUrlSource.fromUrlSource(asource);
|
||||||
else
|
else
|
||||||
throw IllegalStateException("Download audio source is not a url source");
|
throw DownloadException("Audio source is not supported for downloading (yet)", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(videoSource == null && audioSource == null)
|
||||||
|
throw DownloadException("No valid sources found for video/audio");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
suspend fun download(client: ManagedHttpClient, onProgress: ((Double) -> Unit)? = null) = coroutineScope {
|
suspend fun download(client: ManagedHttpClient, onProgress: ((Double) -> Unit)? = null) = coroutineScope {
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.futo.platformplayer.exceptions
|
||||||
|
|
||||||
|
class DownloadException : Throwable {
|
||||||
|
val isRetryable: Boolean;
|
||||||
|
|
||||||
|
constructor(innerException: Throwable, retryable: Boolean = true): super(innerException) {
|
||||||
|
isRetryable = retryable;
|
||||||
|
}
|
||||||
|
constructor(msg: String, retryable: Boolean = true): super(msg) {
|
||||||
|
isRetryable = retryable;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,10 @@ import android.net.Uri
|
||||||
import com.futo.platformplayer.api.media.models.streams.IVideoSourceDescriptor
|
import com.futo.platformplayer.api.media.models.streams.IVideoSourceDescriptor
|
||||||
import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor
|
import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor
|
||||||
import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource
|
import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource
|
||||||
|
import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlSource
|
||||||
import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource
|
import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource
|
||||||
|
import com.futo.platformplayer.api.media.models.streams.sources.IVideoUrlSource
|
||||||
|
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
|
||||||
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSAudioUrlRangeSource
|
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSAudioUrlRangeSource
|
||||||
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSVideoUrlRangeSource
|
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSVideoUrlRangeSource
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
|
@ -17,6 +20,12 @@ import com.google.android.exoplayer2.upstream.ResolvingDataSource
|
||||||
class VideoHelper {
|
class VideoHelper {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
fun isDownloadable(detail: IPlatformVideoDetails) =
|
||||||
|
(detail.video.videoSources.any { isDownloadable(it) }) ||
|
||||||
|
(if (detail is VideoUnMuxedSourceDescriptor) detail.audioSources.any { isDownloadable(it) } else false);
|
||||||
|
fun isDownloadable(source: IVideoSource) = source is IVideoUrlSource;
|
||||||
|
fun isDownloadable(source: IAudioSource) = source is IAudioUrlSource;
|
||||||
|
|
||||||
fun selectBestVideoSource(desc: IVideoSourceDescriptor, desiredPixelCount : Int, prefContainers : Array<String>) : IVideoSource? = selectBestVideoSource(desc.videoSources.toList(), desiredPixelCount, prefContainers);
|
fun selectBestVideoSource(desc: IVideoSourceDescriptor, desiredPixelCount : Int, prefContainers : Array<String>) : IVideoSource? = selectBestVideoSource(desc.videoSources.toList(), desiredPixelCount, prefContainers);
|
||||||
fun selectBestVideoSource(sources: Iterable<IVideoSource>, desiredPixelCount : Int, prefContainers : Array<String>) : IVideoSource? {
|
fun selectBestVideoSource(sources: Iterable<IVideoSource>, desiredPixelCount : Int, prefContainers : Array<String>) : IVideoSource? {
|
||||||
val targetVideo = if(desiredPixelCount > 0)
|
val targetVideo = if(desiredPixelCount > 0)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import com.futo.platformplayer.*
|
||||||
import com.futo.platformplayer.activities.MainActivity
|
import com.futo.platformplayer.activities.MainActivity
|
||||||
import com.futo.platformplayer.api.http.ManagedHttpClient
|
import com.futo.platformplayer.api.http.ManagedHttpClient
|
||||||
import com.futo.platformplayer.downloads.VideoDownload
|
import com.futo.platformplayer.downloads.VideoDownload
|
||||||
|
import com.futo.platformplayer.exceptions.DownloadException
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
import com.futo.platformplayer.states.Announcement
|
import com.futo.platformplayer.states.Announcement
|
||||||
import com.futo.platformplayer.states.AnnouncementType
|
import com.futo.platformplayer.states.AnnouncementType
|
||||||
|
@ -134,6 +135,22 @@ class DownloadService : Service() {
|
||||||
Logger.w(TAG, "Video had no video or videodetail, removing download");
|
Logger.w(TAG, "Video had no video or videodetail, removing download");
|
||||||
StateDownloads.instance.removeDownload(currentVideo);
|
StateDownloads.instance.removeDownload(currentVideo);
|
||||||
}
|
}
|
||||||
|
else if(ex is DownloadException && !ex.isRetryable) {
|
||||||
|
Logger.w(TAG, "Video had exception that should not be retried");
|
||||||
|
StateDownloads.instance.removeDownload(currentVideo);
|
||||||
|
//Ensure impossible downloads are not retried for playlists
|
||||||
|
if(currentVideo.video != null && currentVideo.groupID != null && currentVideo.groupType == VideoDownload.GROUP_PLAYLIST) {
|
||||||
|
StateDownloads.instance.getPlaylistDownload(currentVideo.groupID!!)?.let {
|
||||||
|
synchronized(it.preventDownload) {
|
||||||
|
if(currentVideo?.video?.url != null && !it.preventDownload.contains(currentVideo!!.video!!.url)) {
|
||||||
|
it.preventDownload.add(currentVideo!!.video!!.url);
|
||||||
|
StateDownloads.instance.savePlaylistDownload(it);
|
||||||
|
Logger.w(TAG, "Preventing further download attempts in playlist [${it.id}] for [${currentVideo?.name}]:${currentVideo?.video?.url}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
Logger.e(TAG, "Failed download [${currentVideo.name}]: ${ex.message}", ex);
|
Logger.e(TAG, "Failed download [${currentVideo.name}]: ${ex.message}", ex);
|
||||||
currentVideo.error = ex.message;
|
currentVideo.error = ex.message;
|
||||||
|
|
|
@ -108,6 +108,11 @@ class StateDownloads {
|
||||||
fun getPlaylistDownload(playlistId: String): PlaylistDownloadDescriptor? {
|
fun getPlaylistDownload(playlistId: String): PlaylistDownloadDescriptor? {
|
||||||
return _downloadPlaylists.findItem { it.id == playlistId };
|
return _downloadPlaylists.findItem { it.id == playlistId };
|
||||||
}
|
}
|
||||||
|
fun savePlaylistDownload(playlistDownload: PlaylistDownloadDescriptor) {
|
||||||
|
synchronized(playlistDownload.preventDownload) {
|
||||||
|
_downloadPlaylists.save(playlistDownload);
|
||||||
|
}
|
||||||
|
}
|
||||||
fun deleteCachedPlaylist(id: String) {
|
fun deleteCachedPlaylist(id: String) {
|
||||||
val pdl = getPlaylistDownload(id);
|
val pdl = getPlaylistDownload(id);
|
||||||
if(pdl != null)
|
if(pdl != null)
|
||||||
|
@ -157,12 +162,15 @@ class StateDownloads {
|
||||||
val playlistsDownloaded = getCachedPlaylists();
|
val playlistsDownloaded = getCachedPlaylists();
|
||||||
for(playlist in playlistsDownloaded) {
|
for(playlist in playlistsDownloaded) {
|
||||||
val playlistDownload = getPlaylistDownload(playlist.playlist.id) ?: continue;
|
val playlistDownload = getPlaylistDownload(playlist.playlist.id) ?: continue;
|
||||||
|
val toIgnore = playlistDownload.getPreventDownloadList();
|
||||||
if(playlist.playlist.videos.any{ getCachedVideo(it.id) == null }) {
|
val missingVideoCount = playlist.playlist.videos.count { !toIgnore.contains(it.url) && getCachedVideo(it.id) == null };
|
||||||
Logger.i(TAG, "Found new videos on playlist [${playlist.playlist.name}]");
|
if(missingVideoCount > 0) {
|
||||||
|
Logger.i(TAG, "Found new videos (${missingVideoCount}) on playlist [${playlist.playlist.name}] to download");
|
||||||
continueDownload(playlistDownload, playlist.playlist);
|
continueDownload(playlistDownload, playlist.playlist);
|
||||||
hasChanged = true;
|
hasChanged = true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
Logger.v(TAG, "Offline playlist [${playlist.playlist.name}] is up to date");
|
||||||
}
|
}
|
||||||
return hasChanged;
|
return hasChanged;
|
||||||
}
|
}
|
||||||
|
@ -171,6 +179,11 @@ class StateDownloads {
|
||||||
var hasNew = false;
|
var hasNew = false;
|
||||||
for(item in playlist.videos) {
|
for(item in playlist.videos) {
|
||||||
val existing = getCachedVideo(item.id);
|
val existing = getCachedVideo(item.id);
|
||||||
|
|
||||||
|
if(!playlistDownload.shouldDownload(item)) {
|
||||||
|
Logger.i(TAG, "Not downloading for playlist [${playlistDownload.id}] Video [${item.name}]:${item.url}")
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if(existing == null) {
|
if(existing == null) {
|
||||||
val ongoingDownload = getDownloading().find { it.id.value == item.id.value && it.id.value != null };
|
val ongoingDownload = getDownloading().find { it.id.value == item.id.value && it.id.value != null };
|
||||||
if(ongoingDownload != null) {
|
if(ongoingDownload != null) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue