mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-04-19 19:14:51 +00:00
Merge branch 'master' of gitlab.futo.org:videostreaming/grayjay
This commit is contained in:
commit
a03b63ef74
15 changed files with 151 additions and 123 deletions
44
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
44
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
@ -1,19 +1,19 @@
|
|||
name: Bug Report
|
||||
description: Let us know about an unexpected error, a crash, or an incorrect behavior.
|
||||
labels: ["bug", "new"]
|
||||
labels: ["Bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# Thank you for taking the time to fill out this bug report.
|
||||
|
||||
|
||||
The [grayjay-android](https://github.com/futo-org/grayjay-android) issue tracker is reserved for issues relating to the Grayjay Android Application
|
||||
|
||||
For general usage questions, please see: [The Official FUTO Grayjay Zulip Channel](https://chat.futo.org/#narrow/stream/46-Grayjay)
|
||||
|
||||
## Filing a bug report
|
||||
|
||||
To fix your issues faster, we need clear reproduction cases - ideally allowing us to make it happen locally.
|
||||
To fix your issues faster, we need clear reproduction cases - ideally allowing us to make it happen locally.
|
||||
* Please include all needed context. For example, Device, OS, Application, your Grayjay Configurations and Plugin versioning info.
|
||||
* if you've found out a particular series of UI interactions can introduce buggy behavior, please label those steps 1-n with markdown
|
||||
|
||||
|
@ -41,18 +41,21 @@ body:
|
|||
label: What plugins are you seeing the problem on?
|
||||
multiple: true
|
||||
options:
|
||||
- All
|
||||
- Youtube
|
||||
- BiliBili (CN)
|
||||
- Twitch
|
||||
- Odysee
|
||||
- Rumble
|
||||
- Kick
|
||||
- PeerTube
|
||||
- Patreon
|
||||
- Nebula
|
||||
- SoundCloud
|
||||
- Other
|
||||
- "All"
|
||||
- "Youtube"
|
||||
- "Odysee"
|
||||
- "Rumble"
|
||||
- "Kick"
|
||||
- "Twitch"
|
||||
- "PeerTube"
|
||||
- "Patreon"
|
||||
- "Nebula"
|
||||
- "BiliBili (CN)"
|
||||
- "Bitchute"
|
||||
- "SoundCloud"
|
||||
- "Dailymotion"
|
||||
- "Apple Podcasts"
|
||||
- "Other"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
@ -72,6 +75,17 @@ body:
|
|||
- label: While logged out
|
||||
- label: N/A
|
||||
|
||||
- type: dropdown
|
||||
id: vpn
|
||||
attributes:
|
||||
label: Are you using a VPN?
|
||||
multiple: false
|
||||
options:
|
||||
- "No"
|
||||
- "Yes"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
name: Documentation Issue
|
||||
description: Report an issue or suggest a change in the documentation.
|
||||
labels: ["documentation", "new"]
|
||||
labels: ["Documentation"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# Thank you for opening a documentation change request.
|
||||
|
||||
The [grayjay-android](https://github.com/futo-org/grayjay-android) issue tracker is reserved for issues relating to the Grayjay Android Application. Use the `Documentation` issue type to report problems with the documentation in our code repositories, inside the application, or on [https://grayjay.app](https://grayjay.app)
|
||||
The [grayjay-android](https://github.com/futo-org/grayjay-android) issue tracker is reserved for issues relating to the Grayjay Android Application. Use the `Documentation` issue type to report problems with the documentation in our code repositories, inside the application, or on [https://grayjay.app](https://grayjay.app)
|
||||
Technical writers monitor this issue type, so report Grayjay bugs or feature requests with the `Bug report` or `Feature Request` issue types instead to get engineering attention.
|
||||
|
||||
For general usage questions, please see: [The Official FUTO Grayjay Zulip Channel](https://chat.futo.org/#narrow/stream/46-Grayjay)
|
||||
|
|
6
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
6
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
|
@ -1,6 +1,6 @@
|
|||
name: Feature Request
|
||||
description: Suggest a new feature or other enhancement.
|
||||
labels: ["enhancement", "new"]
|
||||
labels: ["Enhancement"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
@ -9,8 +9,6 @@ body:
|
|||
|
||||
The [grayjay-android](https://github.com/futo-org/grayjay-android) issue tracker is reserved for issues relating to the Grayjay Android Application
|
||||
|
||||
[External Contributions are closed at this time](https://github.com/tom-futo/grayjay-android/blob/master/CONTRIBUTION.md#contributing-to-core)
|
||||
|
||||
For discussion related to enhancements, please see: [The FUTO Grayjay Zulip Channel](https://chat.futo.org/#narrow/stream/46-Grayjay)
|
||||
|
||||
- type: textarea
|
||||
|
@ -55,4 +53,4 @@ body:
|
|||
attributes:
|
||||
value: |
|
||||
**Note:** If the submit button is disabled and you have filled out all required fields, please check that you did not forget a **Title** for the issue.
|
||||
|
||||
|
||||
|
|
34
.github/workflows/labeler.yml
vendored
34
.github/workflows/labeler.yml
vendored
|
@ -1,34 +0,0 @@
|
|||
name: Issue labeler
|
||||
on:
|
||||
issues:
|
||||
types: [ opened ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
label-component:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
# required for all workflows
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Parse issue form
|
||||
uses: stefanbuck/github-issue-parser@v3
|
||||
id: issue-parser
|
||||
with:
|
||||
template-path: .github/ISSUE_TEMPLATE/bug_report.yml
|
||||
|
||||
- name: Set labels based on plugin field
|
||||
uses: redhat-plumbers-in-action/advanced-issue-labeler@v2
|
||||
with:
|
||||
issue-form: ${{ steps.issue-parser.outputs.jsonString }}
|
||||
section: plugin
|
||||
block-list: |
|
||||
None
|
||||
Other
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -649,18 +649,9 @@ class VideoDetailView : ConstraintLayout {
|
|||
};
|
||||
|
||||
var hadDevice = false;
|
||||
StateSync.instance.deviceUpdatedOrAdded.subscribe(this) { id, session ->
|
||||
val hasDevice = StateSync.instance.hasAtLeastOneOnlineDevice();
|
||||
if(hasDevice != hadDevice) {
|
||||
hadDevice = hasDevice;
|
||||
fragment.lifecycleScope.launch(Dispatchers.Main) {
|
||||
updateMoreButtons();
|
||||
}
|
||||
}
|
||||
};
|
||||
StateSync.instance.deviceRemoved.subscribe(this) { id ->
|
||||
val hasDevice = StateSync.instance.hasAtLeastOneOnlineDevice();
|
||||
if(hasDevice != hadDevice) {
|
||||
val devicesChanged = { id: String ->
|
||||
val hasDevice = StateSync.instance.hasAuthorizedDevice();
|
||||
if (hasDevice != hadDevice) {
|
||||
hadDevice = hasDevice;
|
||||
fragment.lifecycleScope.launch(Dispatchers.Main) {
|
||||
updateMoreButtons();
|
||||
|
@ -668,6 +659,9 @@ class VideoDetailView : ConstraintLayout {
|
|||
}
|
||||
}
|
||||
|
||||
StateSync.instance.deviceUpdatedOrAdded.subscribe(this) { id, _ -> devicesChanged(id) };
|
||||
StateSync.instance.deviceRemoved.subscribe(this) { id -> devicesChanged(id) };
|
||||
|
||||
MediaControlReceiver.onLowerVolumeReceived.subscribe(this) { handleLowerVolume() };
|
||||
MediaControlReceiver.onPlayReceived.subscribe(this) { handlePlay() };
|
||||
MediaControlReceiver.onPauseReceived.subscribe(this) { handlePause() };
|
||||
|
@ -922,18 +916,25 @@ class VideoDetailView : ConstraintLayout {
|
|||
};
|
||||
_slideUpOverlay?.hide();
|
||||
},
|
||||
if(StateSync.instance.hasAtLeastOneOnlineDevice()) {
|
||||
if (StateSync.instance.hasAuthorizedDevice()) {
|
||||
RoundButton(context, R.drawable.ic_device, context.getString(R.string.send_to_device), TAG_SEND_TO_DEVICE) {
|
||||
val devices = StateSync.instance.getSessions();
|
||||
val devices = StateSync.instance.getAuthorizedSessions();
|
||||
val videoToSend = video ?: return@RoundButton;
|
||||
if(devices.size > 1) {
|
||||
//not implemented
|
||||
}
|
||||
else if(devices.size == 1){
|
||||
} else if(devices.size == 1){
|
||||
val device = devices.first();
|
||||
Logger.i(TAG, "Send to device? (public key: ${device.remotePublicKey}): " + videoToSend.url)
|
||||
UIDialogs.showConfirmationDialog(context, "Would you like to open\n[${videoToSend.name}]\non ${device.remotePublicKey}" , {
|
||||
Logger.i(TAG, "Send to device confirmed (public key: ${device.remotePublicKey}): " + videoToSend.url)
|
||||
|
||||
fragment.lifecycleScope.launch(Dispatchers.IO) {
|
||||
device.sendJsonData(GJSyncOpcodes.sendToDevices, SendToDevicePackage(videoToSend.url, (lastPositionMilliseconds/1000).toInt()));
|
||||
try {
|
||||
device.sendJsonData(GJSyncOpcodes.sendToDevices, SendToDevicePackage(videoToSend.url, (lastPositionMilliseconds / 1000).toInt()))
|
||||
Logger.i(TAG, "Send to device packet sent (public key: ${device.remotePublicKey}): " + videoToSend.url)
|
||||
} catch (e: Throwable) {
|
||||
Logger.e(TAG, "Send to device packet failed to send", e)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -13,16 +13,15 @@ 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.IAudioUrlSource
|
||||
import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlWidevineSource
|
||||
import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestAudioSource
|
||||
import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestSource
|
||||
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.streams.sources.IWidevineSource
|
||||
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.JSDashManifestRawAudioSource
|
||||
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSDashManifestRawSource
|
||||
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSSource
|
||||
import com.futo.platformplayer.api.media.platforms.js.models.sources.JSVideoUrlRangeSource
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.others.Language
|
||||
|
@ -47,8 +46,8 @@ class VideoHelper {
|
|||
return false
|
||||
}
|
||||
|
||||
fun isDownloadable(source: IVideoSource) = source is IVideoUrlSource || source is IHLSManifestSource || source is JSDashManifestRawSource;
|
||||
fun isDownloadable(source: IAudioSource) = (source is IAudioUrlSource || source is IHLSManifestAudioSource || source is JSDashManifestRawAudioSource) && source !is IAudioUrlWidevineSource
|
||||
fun isDownloadable(source: IVideoSource) = (source is IVideoUrlSource || source is IHLSManifestSource || source is JSDashManifestRawSource) && source !is IWidevineSource
|
||||
fun isDownloadable(source: IAudioSource) = (source is IAudioUrlSource || source is IHLSManifestAudioSource || source is JSDashManifestRawAudioSource) && source !is IWidevineSource
|
||||
|
||||
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? {
|
||||
|
|
|
@ -8,6 +8,7 @@ import com.futo.platformplayer.constructs.Event2
|
|||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.models.HistoryVideo
|
||||
import com.futo.platformplayer.models.ImportCache
|
||||
import com.futo.platformplayer.states.StatePlaylists.Companion
|
||||
import com.futo.platformplayer.stores.FragmentedStorage
|
||||
import com.futo.platformplayer.stores.db.ManagedDBStore
|
||||
import com.futo.platformplayer.stores.db.types.DBHistory
|
||||
|
@ -89,12 +90,14 @@ class StateHistory {
|
|||
if(isUserAction && _lastHistoryBroadcast != historyBroadcastSig) {
|
||||
_lastHistoryBroadcast = historyBroadcastSig;
|
||||
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||
if(StateSync.instance.hasAtLeastOneOnlineDevice()) {
|
||||
try {
|
||||
Logger.i(TAG, "SyncHistory playback broadcasted (${liveObj.name}: ${position})");
|
||||
StateSync.instance.broadcastJsonData(
|
||||
GJSyncOpcodes.syncHistory,
|
||||
listOf(historyVideo)
|
||||
);
|
||||
} catch (e: Throwable) {
|
||||
Logger.e(StatePlaylists.TAG, "Failed to broadcast sync history", e)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -227,31 +227,50 @@ class StatePlaylists {
|
|||
|
||||
private fun broadcastWatchLater(orderOnly: Boolean = false) {
|
||||
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||
StateSync.instance.broadcastJsonData(GJSyncOpcodes.syncWatchLater, SyncWatchLaterPackage(
|
||||
if(orderOnly) listOf() else getWatchLater(),
|
||||
if(orderOnly) mapOf() else _watchLaterAdds.all(),
|
||||
if(orderOnly) mapOf() else _watchLaterRemovals.all(),
|
||||
getWatchLaterLastReorderTime().toEpochSecond(),
|
||||
_watchlistOrderStore.values.toList()));
|
||||
try {
|
||||
StateSync.instance.broadcastJsonData(
|
||||
GJSyncOpcodes.syncWatchLater, SyncWatchLaterPackage(
|
||||
if (orderOnly) listOf() else getWatchLater(),
|
||||
if (orderOnly) mapOf() else _watchLaterAdds.all(),
|
||||
if (orderOnly) mapOf() else _watchLaterRemovals.all(),
|
||||
getWatchLaterLastReorderTime().toEpochSecond(),
|
||||
_watchlistOrderStore.values.toList()
|
||||
)
|
||||
);
|
||||
} catch (e: Throwable) {
|
||||
Logger.w(TAG, "Failed to broadcast watch later", e)
|
||||
}
|
||||
};
|
||||
}
|
||||
private fun broadcastWatchLaterAddition(video: SerializedPlatformVideo, time: OffsetDateTime) {
|
||||
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||
StateSync.instance.broadcastJsonData(GJSyncOpcodes.syncWatchLater, SyncWatchLaterPackage(
|
||||
listOf(video),
|
||||
mapOf(Pair(video.url, time.toEpochSecond())),
|
||||
mapOf(),
|
||||
try {
|
||||
StateSync.instance.broadcastJsonData(
|
||||
GJSyncOpcodes.syncWatchLater, SyncWatchLaterPackage(
|
||||
listOf(video),
|
||||
mapOf(Pair(video.url, time.toEpochSecond())),
|
||||
mapOf(),
|
||||
|
||||
))
|
||||
)
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
Logger.w(TAG, "Failed to broadcast watch later addition", e)
|
||||
}
|
||||
};
|
||||
}
|
||||
private fun broadcastWatchLaterRemoval(url: String, time: OffsetDateTime) {
|
||||
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||
StateSync.instance.broadcastJsonData(GJSyncOpcodes.syncWatchLater, SyncWatchLaterPackage(
|
||||
listOf(),
|
||||
mapOf(),
|
||||
mapOf(Pair(url, time.toEpochSecond()))
|
||||
))
|
||||
try {
|
||||
StateSync.instance.broadcastJsonData(
|
||||
GJSyncOpcodes.syncWatchLater, SyncWatchLaterPackage(
|
||||
listOf(),
|
||||
mapOf(),
|
||||
mapOf(Pair(url, time.toEpochSecond()))
|
||||
)
|
||||
)
|
||||
} catch (e: Throwable) {
|
||||
Logger.w(TAG, "Failed to broadcast watch later removal", e)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -300,12 +319,14 @@ class StatePlaylists {
|
|||
|
||||
private fun broadcastSyncPlaylist(playlist: Playlist){
|
||||
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||
if(StateSync.instance.hasAtLeastOneOnlineDevice()) {
|
||||
try {
|
||||
Logger.i(StateSubscriptionGroups.TAG, "SyncPlaylist (${playlist.name})");
|
||||
StateSync.instance.broadcastJsonData(
|
||||
GJSyncOpcodes.syncPlaylists,
|
||||
SyncPlaylistsPackage(listOf(playlist), mapOf())
|
||||
);
|
||||
} catch (e: Throwable) {
|
||||
Logger.e(TAG, "Failed to broadcast sync playlist", e)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -319,12 +340,14 @@ class StatePlaylists {
|
|||
_playlistRemoved.setAndSave(playlist.id, OffsetDateTime.now());
|
||||
|
||||
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||
if(StateSync.instance.hasAtLeastOneOnlineDevice()) {
|
||||
try {
|
||||
Logger.i(StateSubscriptionGroups.TAG, "SyncPlaylist (${playlist.name})");
|
||||
StateSync.instance.broadcastJsonData(
|
||||
GJSyncOpcodes.syncPlaylists,
|
||||
SyncPlaylistsPackage(listOf(), mapOf(Pair(playlist.id, OffsetDateTime.now().toEpochSecond())))
|
||||
);
|
||||
} catch (e: Throwable) {
|
||||
Logger.e(TAG, "Failed to broadcast sync playlists", e)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -79,12 +79,14 @@ class StateSubscriptionGroups {
|
|||
onGroupsChanged.emit();
|
||||
if(!preventSync) {
|
||||
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||
if(StateSync.instance.hasAtLeastOneOnlineDevice()) {
|
||||
try {
|
||||
Logger.i(TAG, "SyncSubscriptionGroup (${subGroup.name})");
|
||||
StateSync.instance.broadcastJsonData(
|
||||
GJSyncOpcodes.syncSubscriptionGroups,
|
||||
SyncSubscriptionGroupsPackage(listOf(subGroup), mapOf())
|
||||
);
|
||||
} catch (e: Throwable) {
|
||||
Logger.e(TAG, "Failed to broadcast update subscription group", e)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -98,12 +100,14 @@ class StateSubscriptionGroups {
|
|||
if(isUserInteraction) {
|
||||
_groupsRemoved.setAndSave(id, OffsetDateTime.now());
|
||||
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||
if(StateSync.instance.hasAtLeastOneOnlineDevice()) {
|
||||
try {
|
||||
Logger.i(TAG, "SyncSubscriptionGroup delete (${group.name})");
|
||||
StateSync.instance.broadcastJsonData(
|
||||
GJSyncOpcodes.syncSubscriptionGroups,
|
||||
SyncSubscriptionGroupsPackage(listOf(), mapOf(Pair(id, OffsetDateTime.now().toEpochSecond())))
|
||||
);
|
||||
} catch (e: Throwable) {
|
||||
Logger.e(TAG, "Failed to delete subscription group", e)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -65,6 +65,12 @@ class StateSync {
|
|||
val deviceRemoved: Event1<String> = Event1()
|
||||
val deviceUpdatedOrAdded: Event2<String, SyncSession> = Event2()
|
||||
|
||||
fun hasAuthorizedDevice(): Boolean {
|
||||
synchronized(_sessions) {
|
||||
return _sessions.any{ it.value.connected && it.value.isAuthorized };
|
||||
}
|
||||
}
|
||||
|
||||
fun start() {
|
||||
if (_started) {
|
||||
Logger.i(TAG, "Already started.")
|
||||
|
@ -216,6 +222,11 @@ class StateSync {
|
|||
return _sessions.values.toList()
|
||||
};
|
||||
}
|
||||
fun getAuthorizedSessions(): List<SyncSession> {
|
||||
return synchronized(_sessions) {
|
||||
return _sessions.values.filter { it.isAuthorized }.toList()
|
||||
};
|
||||
}
|
||||
|
||||
fun getSyncSessionData(key: String): SyncSessionData {
|
||||
return _syncSessionData.get(key) ?: SyncSessionData(key);
|
||||
|
@ -349,8 +360,12 @@ class StateSync {
|
|||
scope.launch(Dispatchers.Main) {
|
||||
UIDialogs.showConfirmationDialog(activity, "Allow connection from ${remotePublicKey}?", action = {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
session!!.authorize(s)
|
||||
Logger.i(TAG, "Connection authorized for ${remotePublicKey} by confirmation")
|
||||
try {
|
||||
session!!.authorize(s)
|
||||
Logger.i(TAG, "Connection authorized for $remotePublicKey by confirmation")
|
||||
} catch (e: Throwable) {
|
||||
Logger.e(TAG, "Failed to send authorize", e)
|
||||
}
|
||||
}
|
||||
}, cancelAction = {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
|
@ -404,11 +419,9 @@ class StateSync {
|
|||
broadcast(opcode, subOpcode, data.toByteArray(Charsets.UTF_8));
|
||||
}
|
||||
fun broadcast(opcode: UByte, subOpcode: UByte, data: ByteArray) {
|
||||
for(session in getSessions()) {
|
||||
for(session in getAuthorizedSessions()) {
|
||||
try {
|
||||
if (session.isAuthorized && session.connected) {
|
||||
session.send(opcode, subOpcode, data);
|
||||
}
|
||||
session.send(opcode, subOpcode, data);
|
||||
}
|
||||
catch(ex: Exception) {
|
||||
Logger.w(TAG, "Failed to broadcast (opcode = ${opcode}, subOpcode = ${subOpcode}) to ${session.remotePublicKey}: ${ex.message}}", ex);
|
||||
|
@ -450,17 +463,6 @@ class StateSync {
|
|||
return session
|
||||
}
|
||||
|
||||
fun hasAtLeastOneDevice(): Boolean {
|
||||
synchronized(_authorizedDevices) {
|
||||
return _authorizedDevices.values.isNotEmpty()
|
||||
}
|
||||
}
|
||||
fun hasAtLeastOneOnlineDevice(): Boolean {
|
||||
synchronized(_sessions) {
|
||||
return _sessions.any{ it.value.connected && it.value.isAuthorized };
|
||||
}
|
||||
}
|
||||
|
||||
fun getAll(): List<String> {
|
||||
synchronized(_authorizedDevices) {
|
||||
return _authorizedDevices.values.toList()
|
||||
|
|
|
@ -398,7 +398,6 @@ class SyncSession : IAuthorizable {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
inline fun <reified T> sendJsonData(subOpcode: UByte, data: T) {
|
||||
send(Opcode.DATA.value, subOpcode, Json.encodeToString<T>(data));
|
||||
}
|
||||
|
@ -409,12 +408,29 @@ class SyncSession : IAuthorizable {
|
|||
send(opcode, subOpcode, data.toByteArray(Charsets.UTF_8));
|
||||
}
|
||||
fun send(opcode: UByte, subOpcode: UByte, data: ByteArray) {
|
||||
val sock = _socketSessions.firstOrNull();
|
||||
if(sock != null){
|
||||
sock.send(opcode, subOpcode, ByteBuffer.wrap(data));
|
||||
val socketSessions = synchronized(_socketSessions) {
|
||||
_socketSessions.toList()
|
||||
}
|
||||
|
||||
if (socketSessions.isEmpty()) {
|
||||
Logger.v(TAG, "Packet was not sent (opcode = ${opcode}, subOpcode = ${subOpcode}) due to no connected sockets")
|
||||
return
|
||||
}
|
||||
|
||||
var sent = false
|
||||
for (socketSession in socketSessions) {
|
||||
try {
|
||||
socketSession.send(opcode, subOpcode, ByteBuffer.wrap(data))
|
||||
sent = true
|
||||
break
|
||||
} catch (e: Throwable) {
|
||||
Logger.w(TAG, "Packet failed to send (opcode = ${opcode}, subOpcode = ${subOpcode})", e)
|
||||
}
|
||||
}
|
||||
|
||||
if (!sent) {
|
||||
throw Exception("Packet was not sent (opcode = ${opcode}, subOpcode = ${subOpcode}) due to send errors and no remaining candidates")
|
||||
}
|
||||
else
|
||||
throw IllegalStateException("Session has no active sockets");
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
|
|
@ -300,6 +300,8 @@ class SyncSocketSession {
|
|||
}
|
||||
|
||||
private fun handlePacket(opcode: UByte, subOpcode: UByte, data: ByteBuffer) {
|
||||
Logger.i(TAG, "Handle packet (opcode = ${opcode}, subOpcode = ${subOpcode})")
|
||||
|
||||
when (opcode) {
|
||||
Opcode.PING.value -> {
|
||||
send(Opcode.PONG.value)
|
||||
|
|
|
@ -400,7 +400,7 @@
|
|||
<string name="allow_under_cutout_description">Allow video to go underneath the screen cutout in full screen.\nMay require restart</string>
|
||||
<string name="autoplay">Enable autoplay by default</string>
|
||||
<string name="autoplay_description">Autoplay will be enabled by default whenever you watch a video</string>
|
||||
<string name="allow_full_screen_portrait">Allow full-screen portrait</string>
|
||||
<string name="allow_full_screen_portrait">Allow full-screen portrait when watching horizontal videos</string>
|
||||
<string name="delete_watchlist_on_finish">Delete from WatchLater when watched</string>
|
||||
<string name="delete_watchlist_on_finish_description">After you leave a video that you mostly watched, it will be removed from watch later.</string>
|
||||
<string name="background_switch_audio">Switch to Audio in Background</string>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 764316618b245d184b948bfe109447f649f12618
|
||||
Subproject commit 72d297735a267427e0839ff328dae08b1143d961
|
|
@ -1 +1 @@
|
|||
Subproject commit 764316618b245d184b948bfe109447f649f12618
|
||||
Subproject commit 72d297735a267427e0839ff328dae08b1143d961
|
Loading…
Add table
Reference in a new issue