Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ class JSPlaylistDetails: JSPlaylist, IPlatformPlaylistDetails {
onProgress?.invoke(videos.size);
}

return Playlist(UUID.randomUUID().toString(), name, videos.map { SerializedPlatformVideo.fromVideo(it)});
return Playlist(UUID.randomUUID().toString(), name, videos.map { SerializedPlatformVideo.fromVideo(it)}, url);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import androidx.lifecycle.lifecycleScope
import com.futo.platformplayer.*
import com.futo.platformplayer.activities.IWithResultLauncher
import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylist
import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylistDetails
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo
import com.futo.platformplayer.constructs.TaskHandler
Expand All @@ -22,11 +23,13 @@ import com.futo.platformplayer.states.StateDownloads
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.states.StatePlaylists
import com.futo.platformplayer.views.adapters.viewholders.SelectablePlaylist
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuItem
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuTextInput
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class PlaylistFragment : MainFragment() {
override val isMainView : Boolean = true;
Expand Down Expand Up @@ -112,6 +115,113 @@ class PlaylistFragment : MainFragment() {
_editPlaylistOverlay = editPlaylistOverlay;
_editPlaylistNameInput = nameInput;

setOnSync {
val playlist = _playlist ?: return@setOnSync;
if (playlist.url.isNullOrBlank()) {
UIDialogs.appToast("Can't sync playlist because it was not imported", false);
}
else {
UISlideOverlays.showOverlay(overlayContainer, "Sync " + context.getString(R.string.playlist) + " [${playlist.name}] with source", null, {},
SlideUpMenuItem(
context,
R.drawable.ic_sync,
"Keep videos not on source",
"",
tag = 1,
call = {
val taskLoadPlaylist = TaskHandler<String, IPlatformPlaylistDetails?>({fragment.lifecycleScope}, { link -> StatePlatform.instance.getPlaylist(link); })
.success {
if (it != null) {
val playlistToImport = SelectablePlaylist(it);

UIDialogs.showDialogProgress(context) {
it.setText("Syncing playlist..");
it.setProgress(0f);
_fragment.lifecycleScope.launch(Dispatchers.IO) {
withContext(Dispatchers.Main) {
it.setText("Syncing playlist..\n[${playlistToImport.playlist.name}]");
}
try {
val tempPlaylist = playlistToImport.playlist.toPlaylist();
tempPlaylist.id = playlist.id;
tempPlaylist.videos = (tempPlaylist.videos + playlist.videos)
.distinctBy { it.id } as java.util.ArrayList<SerializedPlatformVideo>;
StatePlaylists.instance.createOrUpdatePlaylist(tempPlaylist, true, true);
playlist.videos = tempPlaylist.videos;
}
catch(ex: Throwable) {
UIDialogs.appToast("Failed to sync [${playlistToImport.playlist.name}]\n" + ex.message);
}

withContext(Dispatchers.Main) {
UIDialogs.toast("${playlist.name} " + "Synced");
_fragment.closeSegment();
it.dismiss();
}
}
}

}

onShown(playlist)

}.exceptionWithParameter<Throwable> { ex, para ->
Logger.w(ChannelFragment.TAG, "Failed to load results.", ex);
UIDialogs.appToast(context.getString(R.string.failed_to_fetch) + "\n${para}\n" + ex.message, false)
};
taskLoadPlaylist.run(playlist.url);
}),
SlideUpMenuItem(
context,
R.drawable.ic_sync,
"Remove videos not on source",
"",
tag = 2,
call = {
val taskLoadPlaylist = TaskHandler<String, IPlatformPlaylistDetails?>({fragment.lifecycleScope}, { link -> StatePlatform.instance.getPlaylist(link); })
.success {
if (it != null) {
val playlistToImport = SelectablePlaylist(it);

UIDialogs.showDialogProgress(context) {
it.setText("Syncing playlist..");
it.setProgress(0f);
_fragment.lifecycleScope.launch(Dispatchers.IO) {
withContext(Dispatchers.Main) {
it.setText("Syncing playlist..\n[${playlistToImport.playlist.name}]");
}
try {
val tempPlaylist = playlistToImport.playlist.toPlaylist();
tempPlaylist.id = playlist.id;
StatePlaylists.instance.createOrUpdatePlaylist(tempPlaylist, true, true);
playlist.videos = tempPlaylist.videos;
}
catch(ex: Throwable) {
UIDialogs.appToast("Failed to sync [${playlistToImport.playlist.name}]\n" + ex.message);
}

withContext(Dispatchers.Main) {
UIDialogs.toast("${playlist.name} " + "Synced");
_fragment.closeSegment();
it.dismiss();
}
}
}

}

onShown(playlist)

}.exceptionWithParameter<Throwable> { ex, para ->
Logger.w(ChannelFragment.TAG, "Failed to load results.", ex);
UIDialogs.appToast(context.getString(R.string.failed_to_fetch) + "\n${para}\n" + ex.message, false)
};
taskLoadPlaylist.run(playlist.url);
})
);
}
};

setOnShare {
val playlist = _playlist ?: return@setOnShare;
val reconstruction = StatePlaylists.instance.playlistStore.getReconstructionString(playlist);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@ abstract class VideoListEditorView : LinearLayout {
private var _buttonShare: ImageButton;
private var _buttonEdit: ImageButton;
private var _buttonSearch: ImageButton;
private var _buttonSync: ImageButton;

private var _search: SearchView;

private var _onSync: (()->Unit)? = null;
private var _onShare: (()->Unit)? = null;

private var _loadedVideos: List<IPlatformVideo>? = null;
Expand All @@ -68,6 +70,7 @@ abstract class VideoListEditorView : LinearLayout {
val buttonPlayAll = findViewById<LinearLayout>(R.id.button_play_all);
val buttonShuffle = findViewById<LinearLayout>(R.id.button_shuffle);
_buttonEdit = findViewById(R.id.button_edit);
_buttonSync = findViewById(R.id.button_sync);
_buttonDownload = findViewById(R.id.button_download);
_buttonDownload.visibility = View.GONE;
_buttonExport = findViewById(R.id.button_export);
Expand All @@ -94,6 +97,14 @@ abstract class VideoListEditorView : LinearLayout {
}
}

val onSync = _onSync;
if(onSync != null) {
_buttonSync.setOnClickListener { hideSearchKeyboard(); onSync.invoke() };
_buttonSync.visibility = View.VISIBLE;
}
else
_buttonSync.visibility = View.GONE;

_buttonShare = findViewById(R.id.button_share);
val onShare = _onShare;
if(onShare != null) {
Expand All @@ -118,6 +129,15 @@ abstract class VideoListEditorView : LinearLayout {
_videoListEditorView = videoListEditorView;
}

fun setOnSync(onSync: (()-> Unit)? = null) {
_onSync = onSync;
_buttonSync.setOnClickListener {
hideSearchKeyboard();
onSync?.invoke();
};
_buttonSync.visibility = View.VISIBLE;
}

fun setOnShare(onShare: (()-> Unit)? = null) {
_onShare = onShare;
_buttonShare.setOnClickListener {
Expand Down
8 changes: 6 additions & 2 deletions app/src/main/java/com/futo/platformplayer/models/Playlist.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,19 @@ class Playlist {
@kotlinx.serialization.Serializable(with = OffsetDateTimeSerializer::class)
var datePlayed: OffsetDateTime = OffsetDateTime.MIN;

constructor(){}
var url: String = "";

constructor(name: String, list: List<SerializedPlatformVideo>) {
this.name = name;
this.videos = ArrayList(list);
}
constructor(id: String, name: String, list: List<SerializedPlatformVideo>) {
constructor(id: String, name: String, list: List<SerializedPlatformVideo>, url: String? = null) {
this.id = id;
this.name = name;
this.videos = ArrayList(list);
if (url != null) {
this.url = url
};
}

fun makeCopy(newName: String? = null): Playlist {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,9 @@ class StatePlaylists {
createOrUpdatePlaylist(newPlaylist);
return newPlaylist;
}
fun createOrUpdatePlaylist(playlist: Playlist, isUserInteraction: Boolean = true) {
fun createOrUpdatePlaylist(playlist: Playlist, isUserInteraction: Boolean = true, onlyExisting: Boolean = false) {
playlist.dateUpdate = OffsetDateTime.now();
playlistStore.saveAsync(playlist, true);
playlistStore.saveAsync(playlist, true, onlyExisting);
if(playlist.id.isNotEmpty()) {
if (StateDownloads.instance.isPlaylistCached(playlist.id)) {
StateDownloads.instance.checkForOutdatedPlaylistVideos(playlist.id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.futo.platformplayer.stores.v2
import com.futo.platformplayer.assume
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.models.ImportCache
import com.futo.platformplayer.models.Playlist
import com.futo.platformplayer.states.StateApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -263,6 +264,16 @@ class ManagedStore<T>{
else null;

var file = getFile(obj);
try {
if (file == null && (obj as Playlist).id.isNotEmpty() && onlyExisting) {
val files = _files.filter { (key, _) -> (key as Playlist).id == (obj as Playlist).id }
if (files.size == 1) {
file = files.values.first();
}
}
} catch (_: ClassCastException) {
// not a playlist
}
if (file != null) {
Logger.v(TAG, "Saving file ${logName(file.id)}");
val encoded = _serializer.serialize(_class, obj);
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_sync.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:fillColor="@android:color/white"
android:pathData="M 16 4 C 10.886719 4 6.617188 7.160156 4.875 11.625 L 6.71875 12.375 C 8.175781 8.640625 11.710938 6 16 6 C 19.242188 6 22.132813 7.589844 23.9375 10 L 20 10 L 20 12 L 27 12 L 27 5 L 25 5 L 25 8.09375 C 22.808594 5.582031 19.570313 4 16 4 Z M 25.28125 19.625 C 23.824219 23.359375 20.289063 26 16 26 C 12.722656 26 9.84375 24.386719 8.03125 22 L 12 22 L 12 20 L 5 20 L 5 27 L 7 27 L 7 23.90625 C 9.1875 26.386719 12.394531 28 16 28 C 21.113281 28 25.382813 24.839844 27.125 20.375 Z"/>
</vector>
19 changes: 18 additions & 1 deletion app/src/main/res/layout/fragment_video_list_editor.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,31 @@
android:gravity="center"
android:layout_marginStart="5dp"
android:layout_marginRight="10dp"
app:layout_constraintRight_toLeftOf="@id/button_export"
app:layout_constraintRight_toLeftOf="@id/button_sync"
app:layout_constraintTop_toTopOf="@id/button_share"
android:orientation="horizontal"
android:scaleType="fitCenter"
app:srcCompat="@drawable/ic_edit"
android:padding="10dp"
app:tint="@color/white" />

<ImageButton
android:id="@+id/button_sync"
android:layout_width="40dp"
android:layout_height="40dp"
android:contentDescription="@string/cd_button_sync"
android:background="@drawable/background_button_round"
android:gravity="center"
android:layout_marginStart="5dp"
android:layout_marginRight="10dp"
app:layout_constraintRight_toLeftOf="@id/button_export"
app:layout_constraintTop_toTopOf="@id/button_share"
android:orientation="horizontal"
android:scaleType="fitCenter"
app:srcCompat="@drawable/ic_sync"
android:padding="10dp"
app:tint="@color/white" />

<ImageButton
android:id="@+id/button_export"
android:layout_width="40dp"
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,7 @@
<string name="cd_button_stop">Stop</string>
<string name="cd_button_scan_qr">Scan QR code</string>
<string name="cd_button_help">Help</string>
<string name="cd_button_sync">Sync</string>
<string name="cd_image_polycentric">Change Polycentric profile picture</string>
<string-array name="home_screen_array">
<item>Recommendations</item>
Expand Down