package fr.mobdev.peertubelive.manager import android.content.Context import android.os.Bundle import fr.mobdev.peertubelive.R import fr.mobdev.peertubelive.objects.ChannelData import fr.mobdev.peertubelive.objects.OAuthData import fr.mobdev.peertubelive.objects.StreamData import fr.mobdev.peertubelive.objects.StreamSettings import org.json.JSONObject import java.lang.Exception import java.util.* import kotlin.collections.ArrayList import kotlin.collections.HashMap object InstanceManager { private val oauthManager : OAuthManager = OAuthManager() private const val BASE_API_ENDPOINT: String = "/api/v1" private const val REGISTER_CLIENT_ENDPOINT: String = "/oauth-clients/local" private const val GET_USER_CLIENT_ENDPOINT: String = "/users/token" private const val GET_USER_INFO_ENDPOINT: String = "/users/me" private const val CREATE_LIVE_ENDPOINT: String = "/videos/live" private const val GET_CATEGORY_ENDPOINT: String = "/videos/categories" private const val GET_PRIVACY_ENDPOINT: String = "/videos/privacies" private const val GET_LICENCE_ENDPOINT: String = "/videos/licences" private const val GET_LANGUAGES_ENDPOINT: String = "/videos/languages" private const val GET_VIDEOS: String = "/users/me/videos" internal const val EXTRA_DATA: String = "EXTRA_DATA" private const val CONTENT_TYPE: String = "CONTENT_TYPE" private const val CONTENT_DATA: String = "CONTENT_DATA" private const val VIDEO_CHANNEL: String = "videoChannels" private const val CHANNEL_ID: String = "id" private const val CHANNEL_NAME: String = "displayName" private const val VIDEO: String = "video" private const val UUID: String = "uuid" private const val RTMP_URL: String = "rtmpUrl" private const val STREAM_KEY: String = "streamKey" fun registerAccount(context: Context, url: String, username: String, password: String, listener: InstanceListener) { val registerUrl = url + BASE_API_ENDPOINT+ REGISTER_CLIENT_ENDPOINT val internalListener: InstanceListener = object : InstanceListener { override fun onSuccess(args: Bundle?) { val oauthData: OAuthData? = args?.getParcelable(EXTRA_DATA) oauthData?.baseUrl = url if (oauthData != null) getUserToken(context, url, username, password,oauthData, listener) else listener.onError(context.getString(R.string.unknwon_error)) } override fun onError(error: String?) { listener.onError(error) } override fun onUpdateOAuthData(oauthData: OAuthData) { listener.onUpdateOAuthData(oauthData) } } oauthManager.register(context,registerUrl,internalListener) } fun getUserToken(context: Context, url: String, username: String, password: String, oauthData: OAuthData, listener: InstanceListener) { val userAccess = url + BASE_API_ENDPOINT + GET_USER_CLIENT_ENDPOINT oauthManager.getUserToken(context, userAccess, username, password, oauthData, listener) } private fun refreshToken(context: Context, url: String, oauthData: OAuthData, listener: InstanceListener) { val registerUrl = url + BASE_API_ENDPOINT+ GET_USER_CLIENT_ENDPOINT oauthManager.refreshToken(context, registerUrl, oauthData, listener) } fun createLive(context: Context, url: String, oauthData: OAuthData, streamSettings: StreamSettings, listener: InstanceListener) { if(oauthData.expires < Calendar.getInstance().timeInMillis) { refreshToken(context,url,oauthData,object: InstanceListener { override fun onSuccess(args: Bundle?) { val oauth: OAuthData? = args?.getParcelable(InstanceManager.EXTRA_DATA) if (oauth != null) { DatabaseManager.updateCredentials(context,oauth) listener.onUpdateOAuthData(oauth) createLiveImpl(context, url,oauth,streamSettings,listener) } } override fun onError(error: String?) { listener.onError(error) } override fun onUpdateOAuthData(oauthData: OAuthData) { listener.onUpdateOAuthData(oauthData) } }) } else { createLiveImpl(context, url, oauthData,streamSettings,listener) } } private fun getStreamKey(context: Context, url: String, oauthData: OAuthData, liveId: String, listener: InstanceListener) { val liveInfo = "$url$BASE_API_ENDPOINT$CREATE_LIVE_ENDPOINT/$liveId" val internalListener = object: InstanceListener { override fun onSuccess(args: Bundle?) { val response = args?.getString(EXTRA_DATA, null) if (response == null) { listener.onError(context.getString(R.string.unknwon_error)) return } val streamData = extractStreamData(response) if (streamData == null) { listener.onError(context.getString(R.string.json_error)) return } val results = Bundle() results.putParcelable(EXTRA_DATA,streamData) listener.onSuccess(results) } override fun onError(error: String?) { listener.onError(error) } override fun onUpdateOAuthData(oauthData: OAuthData) { listener.onUpdateOAuthData(oauthData) } } oauthManager.get(context,liveInfo,oauthData,internalListener) } private fun createLiveImpl(context: Context, url: String, oauthData: OAuthData, streamSettings: StreamSettings, listener: InstanceListener) { // val comments: Boolean, val download: Boolean, val nsfw: Boolean, val saveReplay: Boolean val createLiveUrl = url + BASE_API_ENDPOINT + CREATE_LIVE_ENDPOINT val data = Bundle() val boundary = "45fcc22" var formData: String = prepareFormData(boundary,"channelId",streamSettings.channel.toString(),false) formData += prepareFormData(boundary,"name",streamSettings.title,false) formData += prepareFormData(boundary,"privacy",streamSettings.privacy.toString(),false) if(streamSettings.category != null) formData += prepareFormData(boundary,"category",streamSettings.category.toString(),false) if(streamSettings.language != null) formData += prepareFormData(boundary,"language",streamSettings.language.toString(),false) if(streamSettings.description != null) formData += prepareFormData(boundary,"description",streamSettings.description.toString(),false) if(streamSettings.licence != null) formData += prepareFormData(boundary,"licence",streamSettings.licence.toString(),false) formData += prepareFormData(boundary,"commentsEnabled",streamSettings.comments.toString(),false) formData += prepareFormData(boundary,"nsfw",streamSettings.nsfw.toString(),false) formData += prepareFormData(boundary,"downloadEnabled",streamSettings.download.toString(),false) formData += prepareFormData(boundary,"saveReplay",streamSettings.saveReplay.toString(),true) data.putString(CONTENT_TYPE,"multipart/form-data; boundary=$boundary") data.putString(CONTENT_DATA,formData) val internalListener = object: InstanceListener { override fun onSuccess(args: Bundle?) { val response = args?.getString(EXTRA_DATA, null) if (response == null) { listener.onError(context.getString(R.string.unknwon_error)) return } val liveId = extractLiveId(response) if (liveId == null) { listener.onError(context.getString(R.string.json_error)) return } getStreamKey(context,url,oauthData,liveId,listener) } override fun onError(error: String?) { listener.onError(error) } override fun onUpdateOAuthData(oauthData: OAuthData) { listener.onUpdateOAuthData(oauthData) } } oauthManager.post(context,createLiveUrl,oauthData,data,internalListener) } fun getUserChannelList(context: Context, url: String, oauthData: OAuthData, listener: InstanceListener) { if(oauthData.expires < Calendar.getInstance().timeInMillis) { refreshToken(context,url,oauthData,object: InstanceListener { override fun onSuccess(args: Bundle?) { val oauth: OAuthData? = args?.getParcelable(EXTRA_DATA) if (oauth != null) { listener.onUpdateOAuthData(oauth) DatabaseManager.updateCredentials(context,oauth) getUserChannelListImpl(context, url,oauth,listener) } } override fun onError(error: String?) { listener.onError(error) } override fun onUpdateOAuthData(oauthData: OAuthData) { listener.onUpdateOAuthData(oauthData) } }) } else { getUserChannelListImpl(context, url, oauthData, listener) } } private fun getUserChannelListImpl(context: Context, url: String, oauthData: OAuthData, listener: InstanceListener) { val userInfoUrl: String = url + BASE_API_ENDPOINT + GET_USER_INFO_ENDPOINT val internalListener : InstanceListener = object : InstanceListener { override fun onSuccess(args: Bundle?) { val response = args?.getString(EXTRA_DATA, null) if (response == null) { listener.onError(context.getString(R.string.unknwon_error)) return } val channelList = extractChannelData(response) if (channelList == null) { listener.onError(context.getString(R.string.json_error)) return } args.putParcelableArrayList(EXTRA_DATA, channelList) listener.onSuccess(args) } override fun onError(error: String?) { listener.onError(error) } override fun onUpdateOAuthData(oauthData: OAuthData) { listener.onUpdateOAuthData(oauthData) } } oauthManager.get(context,userInfoUrl,oauthData,internalListener) } fun getCategoryList(context: Context, url: String, listener: InstanceListener) { val userInfoUrl: String = url + BASE_API_ENDPOINT + GET_CATEGORY_ENDPOINT val internalListener : InstanceListener = object : InstanceListener { override fun onSuccess(args: Bundle?) { val response = args?.getString(EXTRA_DATA, null) if (response == null) { listener.onError(context.getString(R.string.unknwon_error)) return } val categoryList = extractMapData(response,0) if (categoryList == null) { listener.onError(context.getString(R.string.json_error)) return } categoryList[""]=0 args.putSerializable(EXTRA_DATA, categoryList) listener.onSuccess(args) } override fun onError(error: String?) { listener.onError(error) } override fun onUpdateOAuthData(oauthData: OAuthData) { listener.onUpdateOAuthData(oauthData) } } oauthManager.get(context,userInfoUrl,null,internalListener) } fun getPrivacyList(context: Context, url: String, listener: InstanceListener) { val userInfoUrl: String = url + BASE_API_ENDPOINT + GET_PRIVACY_ENDPOINT val internalListener : InstanceListener = object : InstanceListener { override fun onSuccess(args: Bundle?) { val response = args?.getString(EXTRA_DATA, null) if (response == null) { listener.onError(context.getString(R.string.unknwon_error)) return } val privacyList = extractMapData(response,0) if (privacyList == null) { listener.onError(context.getString(R.string.json_error)) return } args.putSerializable(EXTRA_DATA, privacyList) listener.onSuccess(args) } override fun onError(error: String?) { listener.onError(error) } override fun onUpdateOAuthData(oauthData: OAuthData) { listener.onUpdateOAuthData(oauthData) } } oauthManager.get(context,userInfoUrl,null,internalListener) } fun getLicencesList(context: Context, url: String, listener: InstanceListener) { val userInfoUrl: String = url + BASE_API_ENDPOINT + GET_LICENCE_ENDPOINT val internalListener : InstanceListener = object : InstanceListener { override fun onSuccess(args: Bundle?) { val response = args?.getString(EXTRA_DATA, null) if (response == null) { listener.onError(context.getString(R.string.unknwon_error)) return } val licencesList = extractMapData(response,0) if (licencesList == null) { listener.onError(context.getString(R.string.json_error)) return } licencesList[""]=0 args.putSerializable(EXTRA_DATA, licencesList) listener.onSuccess(args) } override fun onError(error: String?) { listener.onError(error) } override fun onUpdateOAuthData(oauthData: OAuthData) { listener.onUpdateOAuthData(oauthData) } } oauthManager.get(context,userInfoUrl,null,internalListener) } fun getLanguageList(context: Context, url: String, listener: InstanceListener) { val userInfoUrl: String = url + BASE_API_ENDPOINT + GET_LANGUAGES_ENDPOINT val internalListener : InstanceListener = object : InstanceListener { override fun onSuccess(args: Bundle?) { val response = args?.getString(EXTRA_DATA, null) if (response == null) { listener.onError(context.getString(R.string.unknwon_error)) return } val languageList = extractMapData(response,"") if (languageList == null) { listener.onError(context.getString(R.string.json_error)) return } languageList[""]="" args.putSerializable(EXTRA_DATA, languageList) listener.onSuccess(args) } override fun onError(error: String?) { listener.onError(error) } override fun onUpdateOAuthData(oauthData: OAuthData) { listener.onUpdateOAuthData(oauthData) } } oauthManager.get(context,userInfoUrl,null,internalListener) } private fun extractChannelData(response: String): ArrayList? { try { val json = JSONObject(response) if (json.has(VIDEO_CHANNEL)) { val channelList: ArrayList = ArrayList() val channels = json.getJSONArray(VIDEO_CHANNEL) for (i: Int in 0 until channels.length()) { val channel = channels.getJSONObject(i) if (channel.has(CHANNEL_NAME) && channel.has(CHANNEL_ID)) { val name = channel.getString(CHANNEL_NAME) val id = channel.getLong(CHANNEL_ID) val channelData = ChannelData(id, name) channelList.add(channelData) } else { return null } } return channelList } else { return null } } catch (e: Exception) { e.printStackTrace() return null } } private fun extractLiveId(response: String): String? { try { val json = JSONObject(response) return if(json.has(VIDEO)) { val video = json.getJSONObject(VIDEO) if (video.has(UUID)) { video.getString(UUID) } else { null } } else { null } } catch (e: Exception) { e.printStackTrace() return null } } private fun extractStreamData(response: String): StreamData? { try { val json = JSONObject(response) return if (json.has(RTMP_URL) && json.has(STREAM_KEY)) { val rtmp = json.getString(RTMP_URL) val key = json.getString(STREAM_KEY) StreamData(rtmp,key) } else { null } } catch (e: Exception) { e.printStackTrace() return null } } private fun extractMapData(response: String, type: T): HashMap? { return try { val json = JSONObject(response) val map = HashMap() for(key in json.keys()) { if (type is Int) map[json.getString(key)] = key.toInt() as T else if (type is String) map[json.getString(key)] = key as T } map } catch (e: Exception) { e.printStackTrace() null } } private fun prepareFormData(boundary: String, propertyName: String, property: String, lastData: Boolean): String { val crlf = "\r\n" var formData = "--$boundary$crlf" formData+="Content-Disposition: form-data; name=\"$propertyName\"$crlf$crlf" formData+="$property$crlf" if(lastData) { formData += "$crlf--$boundary--$crlf" } return formData } interface InstanceListener{ fun onSuccess(args: Bundle?) fun onError(error: String?) fun onUpdateOAuthData(oauthData: OAuthData) } }