Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
429b6601d6 | ||
|
|
4ebc3e1f50 | ||
|
|
9a41127a4d | ||
|
|
4010c2e839 | ||
|
|
d8af891489 | ||
|
|
b4753ebeae | ||
|
|
e300dd4187 | ||
|
|
f7dee09fd3 |
@@ -6,8 +6,10 @@ import androidx.lifecycle.LifecycleObserver
|
||||
import androidx.lifecycle.OnLifecycleEvent
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import androidx.work.*
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.acra.config.dialog
|
||||
import org.acra.config.httpSender
|
||||
import org.acra.config.limiter
|
||||
@@ -87,6 +89,7 @@ class JellyfinApplication : TvApp(), LifecycleObserver {
|
||||
/**
|
||||
* Called from the StartupActivity when the user session is started.
|
||||
*/
|
||||
@DelicateCoroutinesApi
|
||||
suspend fun onSessionStart() {
|
||||
val workManager by inject<WorkManager>()
|
||||
val autoBitrate by inject<AutoBitrate>()
|
||||
@@ -104,9 +107,7 @@ class JellyfinApplication : TvApp(), LifecycleObserver {
|
||||
).await()
|
||||
|
||||
// Detect auto bitrate
|
||||
withContext(Dispatchers.IO) {
|
||||
autoBitrate.detect()
|
||||
}
|
||||
GlobalScope.launch(Dispatchers.IO) { autoBitrate.detect() }
|
||||
}
|
||||
|
||||
override fun attachBaseContext(base: Context?) {
|
||||
|
||||
@@ -51,7 +51,7 @@ class ApiBinder(
|
||||
api.EnableAutomaticNetworking(ServerInfo().apply {
|
||||
id = session.serverId.toString()
|
||||
name = server.name
|
||||
address = server.address
|
||||
address = server.address.removeSuffix("/")
|
||||
userId = session.userId.toString()
|
||||
accessToken = session.accessToken
|
||||
})
|
||||
|
||||
@@ -219,7 +219,8 @@ class AuthenticationRepositoryImpl(
|
||||
)
|
||||
|
||||
authenticationStore.putUser(server.id, userId, updatedUser)
|
||||
accountManagerHelper.putAccount(AccountManagerAccount(userId, server.id, updatedUser.name, result.accessToken))
|
||||
val accountManagerAccount = AccountManagerAccount(userId, server.id, updatedUser.name, result.accessToken)
|
||||
accountManagerHelper.putAccount(accountManagerAccount)
|
||||
|
||||
val user = PrivateUser(
|
||||
id = userId,
|
||||
@@ -230,8 +231,15 @@ class AuthenticationRepositoryImpl(
|
||||
imageTag = userInfo.primaryImageTag,
|
||||
lastUsed = Date().time,
|
||||
)
|
||||
setActiveSession(user, server)
|
||||
emit(AuthenticatedState)
|
||||
|
||||
// We just added the account so it should activate properly although in rare cases it doesn't.
|
||||
// this is often caused by issues in the platforms account manager
|
||||
if (setActiveSession(user, server)) emit(AuthenticatedState)
|
||||
else {
|
||||
// Try to remove the account and ask for sign in
|
||||
accountManagerHelper.removeAccount(accountManagerAccount)
|
||||
emit(RequireSignInState)
|
||||
}
|
||||
}
|
||||
|
||||
override fun logout(user: User) {
|
||||
|
||||
@@ -31,7 +31,7 @@ interface ServerRepository {
|
||||
suspend fun refreshServerInfo(server: Server): Boolean
|
||||
|
||||
companion object {
|
||||
val minimumServerVersion = Jellyfin.minimumVersion
|
||||
val minimumServerVersion = Jellyfin.minimumVersion.copy(build = null)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,7 @@ import android.content.Context
|
||||
import android.view.KeyEvent
|
||||
import androidx.preference.PreferenceManager
|
||||
import org.acra.ACRA
|
||||
import org.jellyfin.androidtv.preference.constant.AppTheme
|
||||
import org.jellyfin.androidtv.preference.constant.AudioBehavior
|
||||
import org.jellyfin.androidtv.preference.constant.ClockBehavior
|
||||
import org.jellyfin.androidtv.preference.constant.NextUpBehavior
|
||||
import org.jellyfin.androidtv.preference.constant.PreferredVideoPlayer
|
||||
import org.jellyfin.androidtv.preference.constant.RatingType
|
||||
import org.jellyfin.androidtv.preference.constant.WatchedIndicatorBehavior
|
||||
import org.jellyfin.androidtv.preference.constant.defaultAudioBehavior
|
||||
import org.jellyfin.androidtv.preference.constant.*
|
||||
import org.jellyfin.androidtv.util.DeviceUtils
|
||||
|
||||
/**
|
||||
@@ -87,7 +80,7 @@ class UserPreferences(context: Context) : SharedPreferenceStore(
|
||||
/**
|
||||
* Preferred video player.
|
||||
*/
|
||||
var videoPlayer = Preference.enum("video_player", PreferredVideoPlayer.AUTO)
|
||||
var videoPlayer = Preference.enum("video_player", PreferredVideoPlayer.EXOPLAYER)
|
||||
|
||||
/**
|
||||
* Enable refresh rate switching when device supports it
|
||||
|
||||
@@ -79,6 +79,7 @@ public class PlaybackController {
|
||||
private StreamInfo mCurrentStreamInfo;
|
||||
private List<SubtitleStreamInfo> mSubtitleStreams;
|
||||
|
||||
@Nullable
|
||||
private IPlaybackOverlayFragment mFragment;
|
||||
private Boolean spinnerOff = false;
|
||||
|
||||
@@ -227,7 +228,8 @@ public class PlaybackController {
|
||||
Utils.showToast(TvApp.getApplication(), TvApp.getApplication().getString(R.string.too_many_errors));
|
||||
mPlaybackState = PlaybackState.ERROR;
|
||||
stop();
|
||||
mFragment.finish();
|
||||
|
||||
if (mFragment != null) mFragment.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -631,7 +633,7 @@ public class PlaybackController {
|
||||
// get subtitle info
|
||||
mSubtitleStreams = response.GetSubtitleProfiles(false, apiClient.getValue().getApiUrl(), apiClient.getValue().getAccessToken());
|
||||
|
||||
mFragment.updateDisplay();
|
||||
if (mFragment != null) mFragment.updateDisplay();
|
||||
|
||||
// when using VLC if source is stereo or we're on the Fire platform with AC3 - use most compatible output
|
||||
if (!mVideoManager.isNativeMode() &&
|
||||
@@ -711,7 +713,7 @@ public class PlaybackController {
|
||||
play(mCurrentPosition);
|
||||
burningSubs = false;
|
||||
} else {
|
||||
mFragment.addManualSubtitles(null);
|
||||
if (mFragment != null) mFragment.addManualSubtitles(null);
|
||||
mVideoManager.disableSubs();
|
||||
}
|
||||
return;
|
||||
@@ -741,7 +743,7 @@ public class PlaybackController {
|
||||
break;
|
||||
case Embed:
|
||||
if (!mVideoManager.isNativeMode()) {
|
||||
mFragment.addManualSubtitles(null); // in case these were on
|
||||
if (mFragment != null) mFragment.addManualSubtitles(null); // in case these were on
|
||||
if (!mVideoManager.setSubtitleTrack(index, getCurrentlyPlayingItem().getMediaStreams())) {
|
||||
// error selecting internal subs
|
||||
Utils.showToast(TvApp.getApplication(), TvApp.getApplication().getResources().getString(R.string.msg_unable_load_subs));
|
||||
@@ -750,9 +752,9 @@ public class PlaybackController {
|
||||
}
|
||||
// not using vlc - fall through to external handling
|
||||
case External:
|
||||
mFragment.addManualSubtitles(null);
|
||||
if (mFragment != null) mFragment.addManualSubtitles(null);
|
||||
mVideoManager.disableSubs();
|
||||
mFragment.showSubLoadingMsg(true);
|
||||
if (mFragment != null) mFragment.showSubLoadingMsg(true);
|
||||
stream.setDeliveryMethod(SubtitleDeliveryMethod.External);
|
||||
stream.setDeliveryUrl(String.format("%1$s/Videos/%2$s/%3$s/Subtitles/%4$s/0/Stream.JSON", apiClient.getValue().getApiUrl(), mCurrentStreamInfo.getItemId(), mCurrentStreamInfo.getMediaSourceId(), String.valueOf(stream.getIndex())));
|
||||
apiClient.getValue().getSubtitles(stream.getDeliveryUrl(), new Response<SubtitleTrackInfo>() {
|
||||
@@ -762,11 +764,11 @@ public class PlaybackController {
|
||||
|
||||
if (info != null) {
|
||||
Timber.d("Adding json subtitle track to player");
|
||||
mFragment.addManualSubtitles(info);
|
||||
if (mFragment != null) mFragment.addManualSubtitles(info);
|
||||
} else {
|
||||
Timber.e("Empty subtitle result");
|
||||
Utils.showToast(TvApp.getApplication(), TvApp.getApplication().getResources().getString(R.string.msg_unable_load_subs));
|
||||
mFragment.showSubLoadingMsg(false);
|
||||
if (mFragment != null) mFragment.showSubLoadingMsg(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -774,7 +776,7 @@ public class PlaybackController {
|
||||
public void onError(Exception ex) {
|
||||
Timber.e(ex, "Error downloading subtitles");
|
||||
Utils.showToast(TvApp.getApplication(), TvApp.getApplication().getResources().getString(R.string.msg_unable_load_subs));
|
||||
mFragment.showSubLoadingMsg(false);
|
||||
if (mFragment != null) mFragment.showSubLoadingMsg(false);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -865,8 +867,8 @@ public class PlaybackController {
|
||||
DisplayPreferences cachedPrefs = TvApp.getApplication() != null ?
|
||||
TvApp.getApplication().getCachedDisplayPrefs("usersettings", "emby") : null;
|
||||
|
||||
int skipMS = cachedPrefs != null && cachedPrefs.getCustomPrefs().get("skipBackwardLength") != null ?
|
||||
Integer.parseInt(cachedPrefs.getCustomPrefs().get("skipBackwardLength")) : 10000;
|
||||
int skipMS = cachedPrefs != null && cachedPrefs.getCustomPrefs().get("skipBackLength") != null ?
|
||||
Integer.parseInt(cachedPrefs.getCustomPrefs().get("skipBackLength")) : 10000;
|
||||
|
||||
skip(-skipMS);
|
||||
}
|
||||
@@ -926,7 +928,7 @@ public class PlaybackController {
|
||||
if (currentSkipPos < 0) currentSkipPos = 0;
|
||||
Timber.d("Duration reported as: %s current pos: %s",mVideoManager.getDuration(), mVideoManager.getCurrentPosition());
|
||||
if (currentSkipPos > mVideoManager.getDuration()) currentSkipPos = mVideoManager.getDuration() - 1000;
|
||||
mFragment.setCurrentTime(currentSkipPos);
|
||||
if (mFragment != null) mFragment.setCurrentTime(currentSkipPos);
|
||||
if (getPlaybackMethod().equals(PlayMethod.DirectPlay)) {
|
||||
seek(currentSkipPos);
|
||||
currentSkipPos = 0;
|
||||
@@ -954,7 +956,7 @@ public class PlaybackController {
|
||||
channel.setCurrentProgram(program);
|
||||
mCurrentProgramEndTime = channel.getEndDate() != null ? TimeUtils.convertToLocalDate(channel.getEndDate()).getTime() : 0;
|
||||
mCurrentProgramStartTime = channel.getPremiereDate() != null ? TimeUtils.convertToLocalDate(channel.getPremiereDate()).getTime() : 0;
|
||||
mFragment.updateDisplay();
|
||||
if (mFragment != null) mFragment.updateDisplay();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1006,7 +1008,7 @@ public class PlaybackController {
|
||||
}
|
||||
|
||||
long currentTime = isLiveTv ? getTimeShiftedProgress() : mVideoManager.getCurrentPosition();
|
||||
if (isLiveTv && !directStreamLiveTv) {
|
||||
if (isLiveTv && !directStreamLiveTv && mFragment != null) {
|
||||
mFragment.setSecondaryTime(getRealTimeProgress());
|
||||
}
|
||||
|
||||
@@ -1082,7 +1084,7 @@ public class PlaybackController {
|
||||
// Show "Next Up" fragment
|
||||
spinnerOff = false;
|
||||
mediaManager.getValue().setCurrentVideoQueue(mItems);
|
||||
mFragment.showNextUp(nextItem.getId());
|
||||
if (mFragment != null) mFragment.showNextUp(nextItem.getId());
|
||||
} else {
|
||||
mCurrentIndex++;
|
||||
play(0);
|
||||
@@ -1090,7 +1092,7 @@ public class PlaybackController {
|
||||
} else {
|
||||
// exit activity
|
||||
Timber.d("Last item completed. Finishing activity.");
|
||||
mFragment.finish();
|
||||
if (mFragment != null) mFragment.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1106,7 +1108,7 @@ public class PlaybackController {
|
||||
Utils.showToast(TvApp.getApplication(), TvApp.getApplication().getString(R.string.msg_error_live_stream));
|
||||
directStreamLiveTv = false;
|
||||
PlaybackHelper.retrieveAndPlay(getCurrentlyPlayingItem().getId(), false, TvApp.getApplication());
|
||||
mFragment.finish();
|
||||
if (mFragment != null) mFragment.finish();
|
||||
} else {
|
||||
String msg = TvApp.getApplication().getString(R.string.video_error_unknown_error);
|
||||
Timber.e("Playback error - %s", msg);
|
||||
@@ -1188,10 +1190,10 @@ public class PlaybackController {
|
||||
updateTvProgramInfo();
|
||||
}
|
||||
final Long currentTime = isLiveTv && mCurrentProgramStartTime > 0 ? getRealTimeProgress() : mVideoManager.getCurrentPosition();
|
||||
mFragment.setCurrentTime(currentTime);
|
||||
if (mFragment != null) mFragment.setCurrentTime(currentTime);
|
||||
//if (isLiveTv && !directStreamLiveTv) mFragment.setSecondaryTime(getRealTimeProgress());
|
||||
mCurrentPosition = currentTime;
|
||||
mFragment.updateSubtitles(currentTime);
|
||||
if (mFragment != null) mFragment.updateSubtitles(currentTime);
|
||||
}
|
||||
|
||||
updateProgress = continueUpdate;
|
||||
|
||||
@@ -144,6 +144,12 @@ public class CustomPlaybackTransportControlGlue extends PlaybackTransportControl
|
||||
return vh;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressBarClicked(PlaybackTransportRowPresenter.ViewHolder vh) {
|
||||
CustomPlaybackTransportControlGlue controlglue = CustomPlaybackTransportControlGlue.this;
|
||||
controlglue.onActionClicked(controlglue.playPauseAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) {
|
||||
super.onBindRowViewHolder(vh, item);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.jellyfin.androidtv.util
|
||||
|
||||
import org.jellyfin.sdk.api.client.KtorClient
|
||||
import org.jellyfin.sdk.api.client.exception.ApiClientException
|
||||
import org.jellyfin.sdk.api.client.extensions.detectBitrate
|
||||
import org.jellyfin.sdk.api.operations.MediaInfoApi
|
||||
import timber.log.Timber
|
||||
@@ -17,9 +18,12 @@ class AutoBitrate(
|
||||
private set
|
||||
|
||||
suspend fun detect() {
|
||||
val measurement = mediaInfoApi.detectBitrate()
|
||||
bitrate = measurement.bitrate
|
||||
|
||||
Timber.i("Auto bitrate set to: %d", bitrate)
|
||||
try {
|
||||
val measurement = mediaInfoApi.detectBitrate()
|
||||
bitrate = measurement.bitrate
|
||||
Timber.i("Auto bitrate set to: %d", bitrate)
|
||||
} catch (err: ApiClientException) {
|
||||
Timber.e(err, "Failed to detect bitrate")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user