Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

streaming with X-Emby-Token #459

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
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
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ object Versions {

const val kotlinVersion = "1.9.10"
const val androidPluginVersion = "8.0.0"
const val minSdkVersion = 27
const val minSdkVersion = 21
const val targetSdkVersion = 33

const val appversion = "2.1.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class MediaContainerAdaptor {
return MediaContainer()
}

fun createVideoList(videos: List<Item>): IMediaContainer {
fun createVideoList(videos: List<Item>, token: String): IMediaContainer {
val mediaContainer = MediaContainer()
val serenityVideos = ArrayList<Video>()
mediaContainer.size = videos.size
Expand Down Expand Up @@ -168,7 +168,7 @@ class MediaContainerAdaptor {
} else {
item.container
}
video.directPlayUrl = "emby/Videos/${item.mediaSources?.get(0)?.id ?: item.id}/stream.$container?static=true"
video.directPlayUrl = "emby/Videos/${item.mediaSources?.get(0)?.id ?: item.id}/stream.$container?X-Emby-Token=$token"

if (item.runTimeTicks != null) {
val milliseconds = convertTicksToMilliseconds(item.runTimeTicks)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,13 @@ class EmbyAPIClient(val context: Context, baseUrl: String = "http://localhost:80
return "$baseUrl/Users/$userId/Images/Primary"
}

fun authenticate(userName: String, password: String = ""): AuthenticationResult {
val authenticationResul = AuthenticateUserByName(userName, "", password, password)
fun authenticate(userName: String, password: String): AuthenticationResult {
val authenticationResul = AuthenticateUserByName(userName, password)
val call = usersService.authenticate(authenticationResul, headerMap())
val response = call.execute()
if (response.isSuccessful) {
val body = response.body()
accessToken = body!!.accesToken
accessToken = body!!.accessToken
serverId = body.serverId
userId = body.userInfo.id!!

Expand Down Expand Up @@ -125,12 +125,12 @@ class EmbyAPIClient(val context: Context, baseUrl: String = "http://localhost:80
val result = fetchItem(itemId)
return when (result.type) {
"Series" -> MediaContainerAdaptor().createSeriesList(listOf(result))
else -> MediaContainerAdaptor().createVideoList(listOf(result))
else -> MediaContainerAdaptor().createVideoList(listOf(result), fetchAccessToken()!!)
}
} catch (ex: Exception) {
ex.printStackTrace()
}
return MediaContainerAdaptor().createVideoList(emptyList())
return MediaContainerAdaptor().createVideoList(emptyList(), fetchAccessToken()!!)
}

fun fetchItem(id: String): Item {
Expand Down Expand Up @@ -171,23 +171,32 @@ class EmbyAPIClient(val context: Context, baseUrl: String = "http://localhost:80
val allUsers = ArrayList<SerenityUser>()
for (user in allPublicUsers) {
val builder = us.nineworlds.serenity.common.rest.impl.SerenityUser.builder()
val sernityUser = builder.userName(user.name)
val serenityUser = builder.userName(user.name)
.userId(user.id)
.hasPassword(user.hasPassword)
.build()
allUsers.add(sernityUser)
allUsers.add(serenityUser)
}

val u = defaultUsername()
val p = defaultPassword()
if (!u.isNullOrEmpty() && !p.isNullOrEmpty()) {
val builder = us.nineworlds.serenity.common.rest.impl.SerenityUser.builder()
val user = builder.userName(u)
.password(p)
.build()
allUsers.add(user)
}

return allUsers
}

override fun authenticateUser(user: SerenityUser): SerenityUser {
val authenticatedUser = authenticate(user.userName)

val authenticatedUser = authenticate(user.userName, user.password())
return us.nineworlds.serenity.common.rest.impl.SerenityUser.builder()
.accessToken(authenticatedUser.accesToken)
.accessToken(authenticatedUser.accessToken)
.userName(user.userName)
.userId(user.userId)
.hasPassword(user.hasPassword())
.password(user.password())
.build()
}

Expand Down Expand Up @@ -247,7 +256,7 @@ class EmbyAPIClient(val context: Context, baseUrl: String = "http://localhost:80
val call = usersService.fetchSimilarItemById(headerMap(), itemId = itemId, userId = userId!!, includeItemType = "Movie")

val results = call.execute().body()
return MediaContainerAdaptor().createVideoList(results!!.items)
return MediaContainerAdaptor().createVideoList(results!!.items, fetchAccessToken()!!)
}

override fun retrieveItemByIdCategory(key: String, category: String, types: Types, startIndex: Int, limit: Int?): IMediaContainer {
Expand Down Expand Up @@ -302,7 +311,7 @@ class EmbyAPIClient(val context: Context, baseUrl: String = "http://localhost:80
}

val results = call.execute().body()
return MediaContainerAdaptor().createVideoList(results!!.items)
return MediaContainerAdaptor().createVideoList(results!!.items, fetchAccessToken()!!)
}

override fun retrieveItemByCategories(key: String, category: String, secondaryCategory: String): IMediaContainer {
Expand Down Expand Up @@ -344,7 +353,7 @@ class EmbyAPIClient(val context: Context, baseUrl: String = "http://localhost:80

val results = call.execute().body()

return MediaContainerAdaptor().createVideoList(results!!.items)
return MediaContainerAdaptor().createVideoList(results!!.items, fetchAccessToken()!!)
}

override fun retrieveMovieMetaData(key: String): IMediaContainer {
Expand Down Expand Up @@ -373,7 +382,7 @@ class EmbyAPIClient(val context: Context, baseUrl: String = "http://localhost:80

val itemResults = itemCall.execute().body()

return MediaContainerAdaptor().createVideoList(itemResults!!.items)
return MediaContainerAdaptor().createVideoList(itemResults!!.items, fetchAccessToken()!!)
}

override fun searchEpisodes(key: String, query: String): IMediaContainer? {
Expand Down Expand Up @@ -459,16 +468,16 @@ class EmbyAPIClient(val context: Context, baseUrl: String = "http://localhost:80
if (offset > 0) {
startOffset = offset.toLong().times(10000)
}

return "${baseUrl}emby/Videos/$id/stream.mkv?DeviceId=$deviceId&AudioCodec=aac&VideoCodec=h264&CopyTimeStamps=true&EnableAutoStreamCopy=true&StartTimeTicks=$startOffset&PlaySessionId=$playSessionId"
val token = fetchAccessToken()!!
return "${baseUrl}emby/Videos/$id/stream.mkv?DeviceId=$deviceId&AudioCodec=aac&VideoCodec=h264&CopyTimeStamps=true&EnableAutoStreamCopy=true&StartTimeTicks=$startOffset&PlaySessionId=$playSessionId&X-Emby-Token=$token"
}

override fun reinitialize() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}

override fun createUserImageUrl(user: SerenityUser, width: Int, height: Int): String {
return "$baseUrl/Emby/Users/${user.userId}/Images/Primary?Width=$width&Height=$height"
return "$baseUrl/emby/Users/${user.userId}/Images/Primary?Width=$width&Height=$height"
}

override fun startPlaying(itemId: String) {
Expand Down Expand Up @@ -534,5 +543,9 @@ class EmbyAPIClient(val context: Context, baseUrl: String = "http://localhost:80

fun fetchAccessToken() = prefs.getString("embyAccessToken", "")

fun defaultUsername() = prefs.getString("username", "")

fun defaultPassword() = prefs.getString("password", "")

override fun supportsMultipleUsers(): Boolean = true
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,11 @@ data class PublicUserInfo(

data class AuthenticateUserByName(
@Json(name = "Username") val username: String,
@Json(name = "PassowrdMd5") val passwordMD5: String,
@Json(name = "Password") val password: String,
@Json(name = "Pw") val pw: String
)

data class AuthenticationResult(
@Json(name = "User") val userInfo: PublicUserInfo,
@Json(name = "AccessToken") val accesToken: String,
@Json(name = "AccessToken") val accessToken: String,
@Json(name = "ServerId") val serverId: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class EmbyAPIClientTest {
val authenticateResult = authenticate()

assertThat(authenticateResult).isNotNull()
assertThat(authenticateResult.accesToken).isNotBlank()
assertThat(authenticateResult.accessToken).isNotBlank()
assertThat(client.serverId).isNotBlank()
assertThat(client.accessToken).isNotBlank()
assertThat(client.userId).isNotBlank()
Expand Down
1 change: 1 addition & 0 deletions serenity-app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

</activity>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,13 @@ public interface IConfiguration {
*/
public abstract void setPort(String port);

public abstract String getUsername();

public abstract void setUsername(String username);


public abstract String getPassword();

public abstract void setPassword(String password);

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ public class ServerConfig extends BaseInjector implements IConfiguration {
private String serveraddress;
private String serverport;
private String discoveredServers;
private String username;
private String password;


@Inject protected SharedPreferences preferences;

Expand All @@ -56,6 +59,8 @@ private ServerConfig(Context context) {
serveraddress = preferences.getString("server", "");
discoveredServers = preferences.getString("discoveredServer", "");
serverport = preferences.getString("serverport", "32400");
username = preferences.getString("username", "");
password = preferences.getString("password", "");
}

@Override public String getHost() {
Expand All @@ -77,6 +82,26 @@ private ServerConfig(Context context) {
serverport = port;
}

@Override
public String getUsername() {
return username;
}

@Override
public void setUsername(String username) {
this.username = username;
}

@Override
public String getPassword() {
return password;
}

@Override
public void setPassword(String password) {
this.password = password;
}

public static IConfiguration getInstance(Context context) {
if (config == null) {
config = new ServerConfig(context);
Expand Down Expand Up @@ -131,6 +156,16 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin
storeServerAddress();
return;
}

if ("username".equals(key)) {
username = sharedPreferences.getString(key, "");
return;
}

if ("password".equals(key)) {
password = sharedPreferences.getString(key, "");
return;
}
}

/**
Expand Down
2 changes: 2 additions & 0 deletions serenity-app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -246,4 +246,6 @@
<string name="select_a_user_profile">Select a User Profile</string>
<string name="refresh_servers">Refresh Servers</string>
<string name="manual_server_entry">Manual Server Entry</string>
<string name="user_name">Username</string>
<string name="password">Password</string>
</resources>
10 changes: 10 additions & 0 deletions serenity-app/src/main/res/xml/manual_server_preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@
android:key="serverport"
android:summary="@string/default_port_summary"
android:title="@string/server_port"/>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably want to enable this on the login screen when the user is selected if the user is returned by Emby saying they require a username and password to access the server. This won't work for multiple profiles unfortunately.

I'll create a issue and refer back to this pr for reference. If get around to implementing I'll make sure your credited as the author.

<EditTextPreference
android:defaultValue=""
android:key="username"
android:title="@string/user_name"/>

<EditTextPreference
android:defaultValue=""
android:key="password"
android:title="@string/password"/>
</PreferenceCategory>

</PreferenceScreen>
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public interface SerenityUser {

String getAccessToken();

boolean hasPassword();
String password();

Server getUserServer();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ public class SerenityUser implements us.nineworlds.serenity.common.rest.Serenity

private final String userName;
private final String userId;
private final boolean hasPassword;
private final String password;
private final Server serverInfo;
private final String accessToken;

private SerenityUser(Builder builder) {
this.userName = builder.userName;
this.userId = builder.userId;
this.hasPassword = builder.hasPassword;
this.password = builder.password;
this.serverInfo = builder.serverInfo;
this.accessToken = builder.accessToken;
}
Expand All @@ -34,8 +34,8 @@ public static Builder builder() {
return accessToken;
}

@Override public boolean hasPassword() {
return hasPassword;
@Override public String password() {
return password;
}

@Override public Server getUserServer() {
Expand All @@ -45,7 +45,7 @@ public static Builder builder() {
public static class Builder {
private String userName;
private String userId;
private boolean hasPassword;
private String password;
private Server serverInfo;
private String accessToken;

Expand All @@ -59,8 +59,8 @@ public Builder userId(String userId) {
return this;
}

public Builder hasPassword(boolean hasPassword) {
this.hasPassword = hasPassword;
public Builder password(String password) {
this.password = password;
return this;
}

Expand Down