Add extension: Animeunity (#1188)

Closes https://github.com/jmir1/aniyomi-extensions/issues/769
This commit is contained in:
Secozzi
2023-01-19 13:59:46 +01:00
committed by GitHub
parent 23d1cb94d4
commit 15d62d5105
12 changed files with 1500 additions and 0 deletions

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="eu.kanade.tachiyomi.animeextension" />

View File

@ -0,0 +1,13 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'AnimeUnity'
pkgNameSuffix = 'it.animeunity'
extClass = '.AnimeUnity'
extVersionCode = 1
libVersion = '13'
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

View File

@ -0,0 +1,443 @@
package eu.kanade.tachiyomi.animeextension.it.animeunity
import android.annotation.SuppressLint
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.AnimesPage
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import okhttp3.Headers
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.lang.Exception
import java.text.SimpleDateFormat
class AnimeUnity : ConfigurableAnimeSource, AnimeHttpSource() {
override val name = "AnimeUnity"
override val baseUrl = "https://www.animeunity.tv"
private val workerUrl = "https://scws.work"
override val lang = "it"
override val supportsLatest = true
private val json: Json by injectLazy()
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// ============================== Popular ===============================
override fun popularAnimeParse(response: Response): AnimesPage {
val parsed = json.decodeFromString<AnimeResponse>(
response.body!!.string().substringAfter("top-anime animes=\"").substringBefore("\"></top-anime>").replace("&quot;", "\"")
)
val animeList = parsed.data.map { ani ->
SAnime.create().apply {
title = ani.title_eng
url = "${ani.id}-${ani.slug}"
thumbnail_url = ani.imageurl ?: ""
}
}
return AnimesPage(animeList, parsed.current_page < parsed.last_page)
}
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/top-anime?popular=true&page=$page", headers = headers)
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/?anime=$page", headers = headers)
override fun latestUpdatesParse(response: Response): AnimesPage {
val document = response.asJsoup()
val animeList = document.select("div.home-wrapper-body > div.row > div.latest-anime-container").map {
SAnime.create().apply {
title = it.select("a > strong").text()
url = it.selectFirst("a").attr("href").substringAfter("/anime/")
thumbnail_url = it.select("img").attr("src")
}
}
val hasNextPage = document.select("ul.pagination > li.active ~ li").first() != null
return AnimesPage(animeList, hasNextPage)
}
// =============================== Search ===============================
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> {
val params = AnimeUnityFilters.getSearchParameters(filters)
return client.newCall(searchAnimeRequest(page, query, params))
.asObservableSuccess()
.map { response ->
searchAnimeParse(response, page)
}
}
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = throw Exception("Not used")
private fun searchAnimeRequest(page: Int, query: String, filters: AnimeUnityFilters.FilterSearchParams): Request {
val archivioResponse = client.newCall(
GET("$baseUrl/archivio", headers = headers)
).execute()
val document = archivioResponse.asJsoup()
val crsfToken = document.select("meta[name=csrf-token]").attr("content")
var newHeadersBuilder = headers.newBuilder()
for (cookie in archivioResponse.headers) {
if (cookie.first == "set-cookie" && cookie.second.startsWith("XSRF-TOKEN")) {
newHeadersBuilder.add("X-XSRF-TOKEN", cookie.second.substringAfter("=").substringBefore(";").replace("%3D", "="))
}
if (cookie.first == "set-cookie" && cookie.second.startsWith("animeunity_session")) {
newHeadersBuilder.add("Cookie", cookie.second.substringBefore(";").replace("%3D", "="))
}
}
newHeadersBuilder.add("X-CSRF-TOKEN", crsfToken)
.add("Accept-Language", "en-US,en;q=0.5")
.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0")
if (filters.top.isNotEmpty()) {
val topHeaders = newHeadersBuilder.add("X-CSRF-TOKEN", crsfToken)
.add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
.add("Referer", "$baseUrl/${filters.top}")
return GET("$baseUrl/${filters.top}", headers = topHeaders.build())
}
val searchHeaders = newHeadersBuilder
.add("Accept", "application/json, text/plain, */*")
.add("Content-Type", "application/json;charset=utf-8")
.add("Origin", baseUrl)
.add("Referer", archivioResponse.request.url.toString())
.add("X-Requested-With", "XMLHttpRequest")
.build()
val body = """
{
"title": ${query.falseIfEmpty()},
"type": ${filters.type.falseIfEmpty()},
"year": ${filters.year.falseIfEmpty()},
"order": ${filters.order.falseIfEmpty()},
"status": ${filters.state.falseIfEmpty()},
"genres": ${filters.genre.ifEmpty { "false" }},
"offset": ${(page - 1) * 30},
"dubbed": ${if (filters.dub.isEmpty()) "false" else "true"},
"season": ${filters.season.falseIfEmpty()}
}
""".trimIndent().toRequestBody("application/json".toMediaType())
return POST("$baseUrl/archivio/get-animes", body = body, headers = searchHeaders)
}
override fun searchAnimeParse(response: Response): AnimesPage = throw Exception("Not used")
private fun searchAnimeParse(response: Response, page: Int): AnimesPage {
return if (response.request.method == "POST") {
val data = json.decodeFromString<SearchResponse>(
response.body!!.string()
)
val animeList = data.records.map {
SAnime.create().apply {
title = it.title_eng
thumbnail_url = it.imageurl
url = "${it.id}-${it.slug}"
}
}
AnimesPage(animeList, data.tot - page * 30 >= 30 && data.tot > 30)
} else {
popularAnimeParse(response)
}
}
override fun getFilterList(): AnimeFilterList = AnimeUnityFilters.filterList
// =========================== Anime Details ============================
override fun animeDetailsRequest(anime: SAnime): Request {
return GET("$baseUrl/anime/${anime.url}")
}
override fun animeDetailsParse(response: Response): SAnime {
val anime = SAnime.create()
val document = response.asJsoup()
val videoPlayer = document.selectFirst("video-player[episodes_count]")
val animeDetails = json.decodeFromString<AnimeInfo>(
videoPlayer.attr("anime").replace("&quot;", "\"")
)
anime.title = animeDetails.title_eng
anime.status = parseStatus(animeDetails.status)
anime.artist = animeDetails.studio
anime.genre = animeDetails.genres.joinToString(", ") { it.name }
var description = animeDetails.plot + "\n"
description += "\nTipo: ${animeDetails.type}"
description += "\nStagione: ${animeDetails.season} ${animeDetails.date}"
description += "\nValutazione: ★${animeDetails.score ?: "-"}"
anime.description = description
return anime
}
// ============================== Episodes ==============================
override fun episodeListRequest(anime: SAnime): Request {
return GET("$baseUrl/anime/${anime.url}", headers = headers)
}
override fun episodeListParse(response: Response): List<SEpisode> {
val episodeList = mutableListOf<SEpisode>()
val document = response.asJsoup()
val crsfToken = document.select("meta[name=csrf-token]").attr("content")
var newHeadersBuilder = headers.newBuilder()
for (cookie in response.headers) {
if (cookie.first == "set-cookie" && cookie.second.startsWith("XSRF-TOKEN")) {
newHeadersBuilder.add("X-XSRF-TOKEN", cookie.second.substringAfter("=").substringBefore(";").replace("%3D", "="))
}
if (cookie.first == "set-cookie" && cookie.second.startsWith("animeunity_session")) {
newHeadersBuilder.add("Cookie", cookie.second.substringBefore(";").replace("%3D", "="))
}
}
newHeadersBuilder.add("X-CSRF-TOKEN", crsfToken)
.add("Content-Type", "application/json")
.add("Referer", response.request.url.toString())
.add("Accept", "application/json, text/plain, */*")
.add("Accept-Language", "en-US,en;q=0.5")
.add("X-Requested-With", "XMLHttpRequest")
val newHeaders = newHeadersBuilder.build()
val videoPlayer = document.selectFirst("video-player[episodes_count]")
val episodeCount = videoPlayer.attr("episodes_count").toInt()
val animeId = response.request.url.toString().substringAfter("/anime/").substringBefore("-")
val episodes = json.decodeFromString<List<Episode>>(
videoPlayer.attr("episodes").replace("&quot;", "\"")
)
episodeList.addAll(
episodes.filter {
it.scws_id != null && it.file_name != null
}.map {
SEpisode.create().apply {
name = "Episode ${it.number}"
url = LinkData(it.scws_id.toString(), it.file_name!!).toJsonString()
date_upload = parseDate(it.created_at)
episode_number = it.number.split("-")[0].toFloatOrNull() ?: 0F
}
}
)
if (episodeCount > 120) {
var start = 121
var end = 240
while (end < episodeCount) {
episodeList.addAll(
addFromApi(start, end, animeId, newHeaders)
)
start += 120
end += 120
}
if (episodeCount >= start) {
episodeList.addAll(
addFromApi(start, episodeCount, animeId, newHeaders)
)
}
}
return episodeList.sortedBy { it.episode_number }.reversed()
}
// ============================ Video Links =============================
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> {
val newHeaders = Headers.headersOf(
"Accept", "*/*",
"Accept-Language", "en-US,en;q=0.5",
"Host", "scws.work",
"Origin", baseUrl,
"Referer", "$baseUrl/",
"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:101.0) Gecko/20100101 Firefox/101.0"
)
val mediaId = json.decodeFromString<LinkData>(episode.url)
val videoList = mutableListOf<Video>()
val serverJson = json.decodeFromString<ServerResponse>(
client.newCall(GET("https://scws.work/videos/${mediaId.id}", headers = newHeaders)).execute().body!!.string()
)
val appJs = client.newCall(GET("$baseUrl/js/app.js", headers = headers)).execute().body!!.string()
val tokenRegex = """(\d+),(?:\w+)\.client_ip,"(\w+)"""".toRegex()
val (multiplier, key) = tokenRegex.find(appJs)!!.destructured
val pKeyRegex = """(\d+),u,"(\w+)"""".toRegex()
val (pMultiplier, pKey) = pKeyRegex.find(appJs)!!.destructured
val playListToken = getToken(multiplier.toInt(), serverJson.client_ip, key)
val downloadToken = getToken(pMultiplier.toInt(), serverJson.client_ip, pKey)
val masterPlaylist = client.newCall(
GET("$workerUrl/master/${mediaId.id}?token=$playListToken", headers = headers)
).execute().body!!.string()
val qualities = mutableListOf<String>()
masterPlaylist.substringAfter("#EXT-X-STREAM-INF:").split("#EXT-X-STREAM-INF:").forEach {
qualities.add(
it.substringAfter("\n").substringBefore("\n").substringBefore("p/").substringAfterLast("/")
)
}
qualities.forEach {
val url = "https://au-d1-0" +
serverJson.proxy_download +
".scws-content.net/download/" +
serverJson.storage_download.number +
"/" +
serverJson.folder_id +
"/" +
it +
"p.mp4?token=" +
downloadToken +
"&filename=" +
mediaId.file_name.replace("&", ".")
videoList.add(
Video(
"https://au-d1-0${serverJson.proxy_download}.scws-content.net",
"${it}p",
url,
headers = headers
)
)
}
return Observable.just(videoList.sort())
}
override fun videoListRequest(episode: SEpisode): Request = throw Exception("Not used")
override fun videoListParse(response: Response): List<Video> = throw Exception("Not used")
// ============================= Utilities ==============================
private fun parseStatus(statusString: String): Int {
return when (statusString) {
"In Corso" -> SAnime.ONGOING
"Terminato" -> SAnime.COMPLETED
else -> SAnime.UNKNOWN
}
}
private fun addFromApi(start: Int, end: Int, animeId: String, headers: Headers): List<SEpisode> {
val response = client.newCall(
GET("$baseUrl/info_api/$animeId/1?start_range=$start&end_range=$end", headers = headers)
).execute()
val json = json.decodeFromString<ApiResponse>(response.body!!.string())
return json.episodes.filter {
it.scws_id != null && it.file_name != null
}.map {
SEpisode.create().apply {
name = "Episode ${it.number}"
url = LinkData(it.scws_id.toString(), it.file_name!!).toJsonString()
date_upload = parseDate(it.created_at)
episode_number = it.number.split("-")[0].toFloatOrNull() ?: 0F
}
}
}
private fun String.falseIfEmpty(): String {
return if (this.isEmpty()) {
"false"
} else {
"\"${this}\""
}
}
private fun LinkData.toJsonString(): String {
return json.encodeToString(this)
}
@SuppressLint("SimpleDateFormat")
private fun parseDate(date: String): Long {
val knownPatterns: MutableList<SimpleDateFormat> = ArrayList()
knownPatterns.add(SimpleDateFormat("yyyy-MM-dd hh:mm:ss"))
for (pattern in knownPatterns) {
try {
// Take a try
return pattern.parse(date)!!.time
} catch (e: Throwable) {
// Loop on
}
}
return System.currentTimeMillis()
}
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString("preferred_quality", "1080")!!
return this.sortedWith(
compareBy { it.quality.contains(quality) }
).reversed()
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val videoQualityPref = ListPreference(screen.context).apply {
key = "preferred_quality"
title = "Preferred quality"
entries = arrayOf("1080p", "720p", "480p", "360p", "240p", "80p")
entryValues = arrayOf("1080", "720", "480", "360", "240", "80")
setDefaultValue("1080")
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
val selected = newValue as String
val index = findIndexOfValue(selected)
val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit()
}
}
screen.addPreference(videoQualityPref)
}
}

View File

@ -0,0 +1,204 @@
package eu.kanade.tachiyomi.animeextension.it.animeunity
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
object AnimeUnityFilters {
open class QueryPartFilter(
displayName: String,
val vals: Array<Pair<String, String>>
) : AnimeFilter.Select<String>(
displayName,
vals.map { it.first }.toTypedArray()
) {
fun toQueryPart() = vals[state].second
}
open class CheckBoxFilterList(name: String, values: List<CheckBox>) : AnimeFilter.Group<AnimeFilter.CheckBox>(name, values)
private class CheckBoxVal(name: String, state: Boolean = false) : AnimeFilter.CheckBox(name, state)
private inline fun <reified R> AnimeFilterList.asQueryPart(): String {
return this.filterIsInstance<R>().joinToString("") {
(it as QueryPartFilter).toQueryPart()
}
}
class TopFilter : QueryPartFilter("Top Anime", AnimeUnityFiltersData.top)
class GenreFilter : CheckBoxFilterList(
"Genere",
AnimeUnityFiltersData.genere.map { CheckBoxVal(it.first, false) }
)
class YearFilter : QueryPartFilter("Anno", AnimeUnityFiltersData.year)
class OrderFilter : QueryPartFilter("Ordina", AnimeUnityFiltersData.order)
class StateFilter : QueryPartFilter("Stato", AnimeUnityFiltersData.state)
class TypeFilter : QueryPartFilter("Tipo", AnimeUnityFiltersData.type)
class SeasonFilter : QueryPartFilter("Stagione", AnimeUnityFiltersData.season)
class DubFilter : QueryPartFilter("Dub ITA", AnimeUnityFiltersData.dub)
val filterList = AnimeFilterList(
AnimeFilter.Header("Le migliori pagine di anime"),
AnimeFilter.Header("Nota: ignora altri filtri"),
TopFilter(),
AnimeFilter.Separator(),
GenreFilter(),
YearFilter(),
OrderFilter(),
StateFilter(),
TypeFilter(),
SeasonFilter(),
DubFilter()
)
data class FilterSearchParams(
val top: String = "",
val genre: String = "",
val year: String = "",
val order: String = "",
val state: String = "",
val type: String = "",
val season: String = "",
val dub: String = ""
)
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
if (filters.isEmpty()) return FilterSearchParams()
val genre: String = filters.filterIsInstance<GenreFilter>()
.first()
.state.mapNotNull { format ->
if (format.state) {
"{\"id\":" +
AnimeUnityFiltersData.genere.find { it.first == format.name }!!.second +
",\"name\":\"" +
AnimeUnityFiltersData.genere.find { it.first == format.name }!!.first +
"\"}"
} else { null }
}.joinToString(",")
return FilterSearchParams(
filters.asQueryPart<TopFilter>(),
if (genre.isEmpty()) "" else "[$genre]",
filters.asQueryPart<YearFilter>(),
filters.asQueryPart<OrderFilter>(),
filters.asQueryPart<StateFilter>(),
filters.asQueryPart<TypeFilter>(),
filters.asQueryPart<SeasonFilter>(),
filters.asQueryPart<DubFilter>(),
)
}
private object AnimeUnityFiltersData {
val any = Pair("Any", "")
val top = arrayOf(
Pair("Nessuno", ""),
Pair("Tutto", "top-anime"),
Pair("In corso", "top-anime?status=In Corso"),
Pair("In arrivo", "top-anime?status=In uscita prossimamente"),
Pair("TV", "top-anime?type=TV"),
Pair("Movie", "top-anime?type=Movie"),
Pair("OVA", "top-anime?type=OVA"),
Pair("ONA", "top-anime?type=ONA"),
Pair("Special", "top-anime?type=Special"),
Pair("Popolari", "top-anime?popular=true"),
Pair("Preferiti", "top-anime?order=favorites"),
Pair("Più visti", "top-anime?order=most_viewed")
)
val genere = arrayOf(
Pair("Action", "51"),
Pair("Adventure", "21"),
Pair("Cars", "29"),
Pair("Comedy", "37"),
Pair("Dementia", "43"),
Pair("Demons", "13"),
Pair("Drama", "22"),
Pair("Ecchi", "5"),
Pair("Fantasy", "9"),
Pair("Game", "44"),
Pair("Harem", "15"),
Pair("Hentai", "4"),
Pair("Historical", "30"),
Pair("Horror", "3"),
Pair("Josei", "45"),
Pair("Kids", "14"),
Pair("Magic", "23"),
Pair("Martial Arts", "Martial 31"),
Pair("Mecha", "38"),
Pair("Military", "46"),
Pair("Music", "16"),
Pair("Mystery", "24"),
Pair("Parody", "32"),
Pair("Police", "39"),
Pair("Psychological", "47"),
Pair("Romance", "17"),
Pair("Samurai", "25"),
Pair("School", "33"),
Pair("Sci-fi", "Sci-40"),
Pair("Seinen", "49"),
Pair("Shoujo", "18"),
Pair("Shoujo Ai", "Shoujo 26"),
Pair("Shounen", "34"),
Pair("Shounen Ai", "Shounen 41"),
Pair("Slice of Life", "Slice of 50"),
Pair("Space", "19"),
Pair("Splatter", "52"),
Pair("Sports", "27"),
Pair("Super Power", "Super 35"),
Pair("Supernatural", "42"),
Pair("Thriller", "48"),
Pair("Vampire", "20"),
Pair("Yaoi", "28"),
Pair("Yuri", "36")
)
val order = arrayOf(
any,
Pair("Lista A-Z", "Lista A-Z"),
Pair("Lista Z-A", "Lista Z-A"),
Pair("Valutazione", "Valutazione"),
)
val state = arrayOf(
any,
Pair("In Corso", "In Corso"),
Pair("Terminato", "Terminato"),
Pair("In Uscita", "In Uscita"),
Pair("Droppato", "Droppato"),
)
val type = arrayOf(
any,
Pair("TV", "TV"),
Pair("OVA", "OVA"),
Pair("ONA", "ONA"),
Pair("Special", "Special"),
Pair("Movie", "Movie"),
)
val season = arrayOf(
any,
Pair("Inverno", "Inverno"),
Pair("Primavera", "Primavera"),
Pair("Estate", "Estate"),
Pair("Autunno", "Autunno")
)
val dub = arrayOf(
Pair("No", ""),
Pair("", "true")
)
val year = arrayOf(any) + (1969..2024).map {
Pair(it.toString(), it.toString())
}.reversed().toTypedArray()
}
}

View File

@ -0,0 +1,78 @@
package eu.kanade.tachiyomi.animeextension.it.animeunity
import kotlinx.serialization.Serializable
@Serializable
data class AnimeResponse(
val current_page: Int,
val last_page: Int,
val data: List<Anime>
) {
@Serializable
data class Anime(
val id: Int,
val slug: String,
val title_eng: String,
val imageurl: String? = null,
)
}
@Serializable
data class Episode(
val number: String,
val created_at: String,
val scws_id: Int? = null,
val file_name: String? = null,
)
@Serializable
data class ApiResponse(
val episodes: List<Episode>
)
@Serializable
data class ServerResponse(
val name: String,
val client_ip: String,
val folder_id: String,
val proxy_download: Int,
val storage_download: StorageDownload
) {
@Serializable
data class StorageDownload(
val number: Int
)
}
@Serializable
data class LinkData(
val id: String,
val file_name: String
)
@Serializable
data class AnimeInfo(
val title_eng: String,
val imageurl: String,
val plot: String,
val date: String,
val season: String,
val slug: String,
val id: Int,
val type: String,
val status: String,
val studio: String,
val genres: List<Genre>,
val score: String? = null,
) {
@Serializable
data class Genre(
val name: String
)
}
@Serializable
data class SearchResponse(
val records: List<AnimeInfo>,
val tot: Int
)

View File

@ -0,0 +1,760 @@
package eu.kanade.tachiyomi.animeextension.it.animeunity
import app.cash.quickjs.QuickJs
import kotlin.math.roundToLong
fun getToken(multiplier: Int, ipAdress: String, key: String): String {
val quickJs = QuickJs.create()
val expires = (System.currentTimeMillis() + 3600000 * multiplier / 1000.0).roundToLong().toString()
val r = "$expires$ipAdress $key"
val token = quickJs.evaluate(encodeString(r)).toString() + "&expires=" + expires
quickJs.close()
return token
}
private fun encodeString(string: String) = """
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS = CryptoJS || function(u, p) {
var d = {},
l = d.lib = {},
s = function() {},
t = l.Base = {
extend: function(a) {
s.prototype = this;
var c = new s;
a && c.mixIn(a);
c.hasOwnProperty("init") || (c.init = function() {
c.${'$'}super.init.apply(this, arguments)
});
c.init.prototype = c;
c.${'$'}super = this;
return c
},
create: function() {
var a = this.extend();
a.init.apply(a, arguments);
return a
},
init: function() {},
mixIn: function(a) {
for (var c in a) a.hasOwnProperty(c) && (this[c] = a[c]);
a.hasOwnProperty("toString") && (this.toString = a.toString)
},
clone: function() {
return this.init.prototype.extend(this)
}
},
r = l.WordArray = t.extend({
init: function(a, c) {
a = this.words = a || [];
this.sigBytes = c != p ? c : 4 * a.length
},
toString: function(a) {
return (a || v).stringify(this)
},
concat: function(a) {
var c = this.words,
e = a.words,
j = this.sigBytes;
a = a.sigBytes;
this.clamp();
if (j % 4)
for (var k = 0; k < a; k++) c[j + k >>> 2] |= (e[k >>> 2] >>> 24 - 8 * (k % 4) & 255) << 24 - 8 * ((j + k) % 4);
else if (65535 < e.length)
for (k = 0; k < a; k += 4) c[j + k >>> 2] = e[k >>> 2];
else c.push.apply(c, e);
this.sigBytes += a;
return this
},
clamp: function() {
var a = this.words,
c = this.sigBytes;
a[c >>> 2] &= 4294967295 <<
32 - 8 * (c % 4);
a.length = u.ceil(c / 4)
},
clone: function() {
var a = t.clone.call(this);
a.words = this.words.slice(0);
return a
},
random: function(a) {
for (var c = [], e = 0; e < a; e += 4) c.push(4294967296 * u.random() | 0);
return new r.init(c, a)
}
}),
w = d.enc = {},
v = w.Hex = {
stringify: function(a) {
var c = a.words;
a = a.sigBytes;
for (var e = [], j = 0; j < a; j++) {
var k = c[j >>> 2] >>> 24 - 8 * (j % 4) & 255;
e.push((k >>> 4).toString(16));
e.push((k & 15).toString(16))
}
return e.join("")
},
parse: function(a) {
for (var c = a.length, e = [], j = 0; j < c; j += 2) e[j >>> 3] |= parseInt(a.substr(j,
2), 16) << 24 - 4 * (j % 8);
return new r.init(e, c / 2)
}
},
b = w.Latin1 = {
stringify: function(a) {
var c = a.words;
a = a.sigBytes;
for (var e = [], j = 0; j < a; j++) e.push(String.fromCharCode(c[j >>> 2] >>> 24 - 8 * (j % 4) & 255));
return e.join("")
},
parse: function(a) {
for (var c = a.length, e = [], j = 0; j < c; j++) e[j >>> 2] |= (a.charCodeAt(j) & 255) << 24 - 8 * (j % 4);
return new r.init(e, c)
}
},
x = w.Utf8 = {
stringify: function(a) {
try {
return decodeURIComponent(escape(b.stringify(a)))
} catch (c) {
throw Error("Malformed UTF-8 data");
}
},
parse: function(a) {
return b.parse(unescape(encodeURIComponent(a)))
}
},
q = l.BufferedBlockAlgorithm = t.extend({
reset: function() {
this._data = new r.init;
this._nDataBytes = 0
},
_append: function(a) {
"string" == typeof a && (a = x.parse(a));
this._data.concat(a);
this._nDataBytes += a.sigBytes
},
_process: function(a) {
var c = this._data,
e = c.words,
j = c.sigBytes,
k = this.blockSize,
b = j / (4 * k),
b = a ? u.ceil(b) : u.max((b | 0) - this._minBufferSize, 0);
a = b * k;
j = u.min(4 * a, j);
if (a) {
for (var q = 0; q < a; q += k) this._doProcessBlock(e, q);
q = e.splice(0, a);
c.sigBytes -= j
}
return new r.init(q, j)
},
clone: function() {
var a = t.clone.call(this);
a._data = this._data.clone();
return a
},
_minBufferSize: 0
});
l.Hasher = q.extend({
cfg: t.extend(),
init: function(a) {
this.cfg = this.cfg.extend(a);
this.reset()
},
reset: function() {
q.reset.call(this);
this._doReset()
},
update: function(a) {
this._append(a);
this._process();
return this
},
finalize: function(a) {
a && this._append(a);
return this._doFinalize()
},
blockSize: 16,
_createHelper: function(a) {
return function(b, e) {
return (new a.init(e)).finalize(b)
}
},
_createHmacHelper: function(a) {
return function(b, e) {
return (new n.HMAC.init(a,
e)).finalize(b)
}
}
});
var n = d.algo = {};
return d
}(Math);
(function() {
var u = CryptoJS,
p = u.lib.WordArray;
u.enc.Base64 = {
stringify: function(d) {
var l = d.words,
p = d.sigBytes,
t = this._map;
d.clamp();
d = [];
for (var r = 0; r < p; r += 3)
for (var w = (l[r >>> 2] >>> 24 - 8 * (r % 4) & 255) << 16 | (l[r + 1 >>> 2] >>> 24 - 8 * ((r + 1) % 4) & 255) << 8 | l[r + 2 >>> 2] >>> 24 - 8 * ((r + 2) % 4) & 255, v = 0; 4 > v && r + 0.75 * v < p; v++) d.push(t.charAt(w >>> 6 * (3 - v) & 63));
if (l = t.charAt(64))
for (; d.length % 4;) d.push(l);
return d.join("")
},
parse: function(d) {
var l = d.length,
s = this._map,
t = s.charAt(64);
t && (t = d.indexOf(t), -1 != t && (l = t));
for (var t = [], r = 0, w = 0; w <
l; w++)
if (w % 4) {
var v = s.indexOf(d.charAt(w - 1)) << 2 * (w % 4),
b = s.indexOf(d.charAt(w)) >>> 6 - 2 * (w % 4);
t[r >>> 2] |= (v | b) << 24 - 8 * (r % 4);
r++
} return p.create(t, r)
},
_map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
}
})();
(function(u) {
function p(b, n, a, c, e, j, k) {
b = b + (n & a | ~n & c) + e + k;
return (b << j | b >>> 32 - j) + n
}
function d(b, n, a, c, e, j, k) {
b = b + (n & c | a & ~c) + e + k;
return (b << j | b >>> 32 - j) + n
}
function l(b, n, a, c, e, j, k) {
b = b + (n ^ a ^ c) + e + k;
return (b << j | b >>> 32 - j) + n
}
function s(b, n, a, c, e, j, k) {
b = b + (a ^ (n | ~c)) + e + k;
return (b << j | b >>> 32 - j) + n
}
for (var t = CryptoJS, r = t.lib, w = r.WordArray, v = r.Hasher, r = t.algo, b = [], x = 0; 64 > x; x++) b[x] = 4294967296 * u.abs(u.sin(x + 1)) | 0;
r = r.MD5 = v.extend({
_doReset: function() {
this._hash = new w.init([1732584193, 4023233417, 2562383102, 271733878])
},
_doProcessBlock: function(q, n) {
for (var a = 0; 16 > a; a++) {
var c = n + a,
e = q[c];
q[c] = (e << 8 | e >>> 24) & 16711935 | (e << 24 | e >>> 8) & 4278255360
}
var a = this._hash.words,
c = q[n + 0],
e = q[n + 1],
j = q[n + 2],
k = q[n + 3],
z = q[n + 4],
r = q[n + 5],
t = q[n + 6],
w = q[n + 7],
v = q[n + 8],
A = q[n + 9],
B = q[n + 10],
C = q[n + 11],
u = q[n + 12],
D = q[n + 13],
E = q[n + 14],
x = q[n + 15],
f = a[0],
m = a[1],
g = a[2],
h = a[3],
f = p(f, m, g, h, c, 7, b[0]),
h = p(h, f, m, g, e, 12, b[1]),
g = p(g, h, f, m, j, 17, b[2]),
m = p(m, g, h, f, k, 22, b[3]),
f = p(f, m, g, h, z, 7, b[4]),
h = p(h, f, m, g, r, 12, b[5]),
g = p(g, h, f, m, t, 17, b[6]),
m = p(m, g, h, f, w, 22, b[7]),
f = p(f, m, g, h, v, 7, b[8]),
h = p(h, f, m, g, A, 12, b[9]),
g = p(g, h, f, m, B, 17, b[10]),
m = p(m, g, h, f, C, 22, b[11]),
f = p(f, m, g, h, u, 7, b[12]),
h = p(h, f, m, g, D, 12, b[13]),
g = p(g, h, f, m, E, 17, b[14]),
m = p(m, g, h, f, x, 22, b[15]),
f = d(f, m, g, h, e, 5, b[16]),
h = d(h, f, m, g, t, 9, b[17]),
g = d(g, h, f, m, C, 14, b[18]),
m = d(m, g, h, f, c, 20, b[19]),
f = d(f, m, g, h, r, 5, b[20]),
h = d(h, f, m, g, B, 9, b[21]),
g = d(g, h, f, m, x, 14, b[22]),
m = d(m, g, h, f, z, 20, b[23]),
f = d(f, m, g, h, A, 5, b[24]),
h = d(h, f, m, g, E, 9, b[25]),
g = d(g, h, f, m, k, 14, b[26]),
m = d(m, g, h, f, v, 20, b[27]),
f = d(f, m, g, h, D, 5, b[28]),
h = d(h, f,
m, g, j, 9, b[29]),
g = d(g, h, f, m, w, 14, b[30]),
m = d(m, g, h, f, u, 20, b[31]),
f = l(f, m, g, h, r, 4, b[32]),
h = l(h, f, m, g, v, 11, b[33]),
g = l(g, h, f, m, C, 16, b[34]),
m = l(m, g, h, f, E, 23, b[35]),
f = l(f, m, g, h, e, 4, b[36]),
h = l(h, f, m, g, z, 11, b[37]),
g = l(g, h, f, m, w, 16, b[38]),
m = l(m, g, h, f, B, 23, b[39]),
f = l(f, m, g, h, D, 4, b[40]),
h = l(h, f, m, g, c, 11, b[41]),
g = l(g, h, f, m, k, 16, b[42]),
m = l(m, g, h, f, t, 23, b[43]),
f = l(f, m, g, h, A, 4, b[44]),
h = l(h, f, m, g, u, 11, b[45]),
g = l(g, h, f, m, x, 16, b[46]),
m = l(m, g, h, f, j, 23, b[47]),
f = s(f, m, g, h, c, 6, b[48]),
h = s(h, f, m, g, w, 10, b[49]),
g = s(g, h, f, m,
E, 15, b[50]),
m = s(m, g, h, f, r, 21, b[51]),
f = s(f, m, g, h, u, 6, b[52]),
h = s(h, f, m, g, k, 10, b[53]),
g = s(g, h, f, m, B, 15, b[54]),
m = s(m, g, h, f, e, 21, b[55]),
f = s(f, m, g, h, v, 6, b[56]),
h = s(h, f, m, g, x, 10, b[57]),
g = s(g, h, f, m, t, 15, b[58]),
m = s(m, g, h, f, D, 21, b[59]),
f = s(f, m, g, h, z, 6, b[60]),
h = s(h, f, m, g, C, 10, b[61]),
g = s(g, h, f, m, j, 15, b[62]),
m = s(m, g, h, f, A, 21, b[63]);
a[0] = a[0] + f | 0;
a[1] = a[1] + m | 0;
a[2] = a[2] + g | 0;
a[3] = a[3] + h | 0
},
_doFinalize: function() {
var b = this._data,
n = b.words,
a = 8 * this._nDataBytes,
c = 8 * b.sigBytes;
n[c >>> 5] |= 128 << 24 - c % 32;
var e = u.floor(a /
4294967296);
n[(c + 64 >>> 9 << 4) + 15] = (e << 8 | e >>> 24) & 16711935 | (e << 24 | e >>> 8) & 4278255360;
n[(c + 64 >>> 9 << 4) + 14] = (a << 8 | a >>> 24) & 16711935 | (a << 24 | a >>> 8) & 4278255360;
b.sigBytes = 4 * (n.length + 1);
this._process();
b = this._hash;
n = b.words;
for (a = 0; 4 > a; a++) c = n[a], n[a] = (c << 8 | c >>> 24) & 16711935 | (c << 24 | c >>> 8) & 4278255360;
return b
},
clone: function() {
var b = v.clone.call(this);
b._hash = this._hash.clone();
return b
}
});
t.MD5 = v._createHelper(r);
t.HmacMD5 = v._createHmacHelper(r)
})(Math);
(function() {
var u = CryptoJS,
p = u.lib,
d = p.Base,
l = p.WordArray,
p = u.algo,
s = p.EvpKDF = d.extend({
cfg: d.extend({
keySize: 4,
hasher: p.MD5,
iterations: 1
}),
init: function(d) {
this.cfg = this.cfg.extend(d)
},
compute: function(d, r) {
for (var p = this.cfg, s = p.hasher.create(), b = l.create(), u = b.words, q = p.keySize, p = p.iterations; u.length < q;) {
n && s.update(n);
var n = s.update(d).finalize(r);
s.reset();
for (var a = 1; a < p; a++) n = s.finalize(n), s.reset();
b.concat(n)
}
b.sigBytes = 4 * q;
return b
}
});
u.EvpKDF = function(d, l, p) {
return s.create(p).compute(d,
l)
}
})();
CryptoJS.lib.Cipher || function(u) {
var p = CryptoJS,
d = p.lib,
l = d.Base,
s = d.WordArray,
t = d.BufferedBlockAlgorithm,
r = p.enc.Base64,
w = p.algo.EvpKDF,
v = d.Cipher = t.extend({
cfg: l.extend(),
createEncryptor: function(e, a) {
return this.create(this._ENC_XFORM_MODE, e, a)
},
createDecryptor: function(e, a) {
return this.create(this._DEC_XFORM_MODE, e, a)
},
init: function(e, a, b) {
this.cfg = this.cfg.extend(b);
this._xformMode = e;
this._key = a;
this.reset()
},
reset: function() {
t.reset.call(this);
this._doReset()
},
process: function(e) {
this._append(e);
return this._process()
},
finalize: function(e) {
e && this._append(e);
return this._doFinalize()
},
keySize: 4,
ivSize: 4,
_ENC_XFORM_MODE: 1,
_DEC_XFORM_MODE: 2,
_createHelper: function(e) {
return {
encrypt: function(b, k, d) {
return ("string" == typeof k ? c : a).encrypt(e, b, k, d)
},
decrypt: function(b, k, d) {
return ("string" == typeof k ? c : a).decrypt(e, b, k, d)
}
}
}
});
d.StreamCipher = v.extend({
_doFinalize: function() {
return this._process(!0)
},
blockSize: 1
});
var b = p.mode = {},
x = function(e, a, b) {
var c = this._iv;
c ? this._iv = u : c = this._prevBlock;
for (var d = 0; d < b; d++) e[a + d] ^=
c[d]
},
q = (d.BlockCipherMode = l.extend({
createEncryptor: function(e, a) {
return this.Encryptor.create(e, a)
},
createDecryptor: function(e, a) {
return this.Decryptor.create(e, a)
},
init: function(e, a) {
this._cipher = e;
this._iv = a
}
})).extend();
q.Encryptor = q.extend({
processBlock: function(e, a) {
var b = this._cipher,
c = b.blockSize;
x.call(this, e, a, c);
b.encryptBlock(e, a);
this._prevBlock = e.slice(a, a + c)
}
});
q.Decryptor = q.extend({
processBlock: function(e, a) {
var b = this._cipher,
c = b.blockSize,
d = e.slice(a, a + c);
b.decryptBlock(e, a);
x.call(this,
e, a, c);
this._prevBlock = d
}
});
b = b.CBC = q;
q = (p.pad = {}).Pkcs7 = {
pad: function(a, b) {
for (var c = 4 * b, c = c - a.sigBytes % c, d = c << 24 | c << 16 | c << 8 | c, l = [], n = 0; n < c; n += 4) l.push(d);
c = s.create(l, c);
a.concat(c)
},
unpad: function(a) {
a.sigBytes -= a.words[a.sigBytes - 1 >>> 2] & 255
}
};
d.BlockCipher = v.extend({
cfg: v.cfg.extend({
mode: b,
padding: q
}),
reset: function() {
v.reset.call(this);
var a = this.cfg,
b = a.iv,
a = a.mode;
if (this._xformMode == this._ENC_XFORM_MODE) var c = a.createEncryptor;
else c = a.createDecryptor, this._minBufferSize = 1;
this._mode = c.call(a,
this, b && b.words)
},
_doProcessBlock: function(a, b) {
this._mode.processBlock(a, b)
},
_doFinalize: function() {
var a = this.cfg.padding;
if (this._xformMode == this._ENC_XFORM_MODE) {
a.pad(this._data, this.blockSize);
var b = this._process(!0)
} else b = this._process(!0), a.unpad(b);
return b
},
blockSize: 4
});
var n = d.CipherParams = l.extend({
init: function(a) {
this.mixIn(a)
},
toString: function(a) {
return (a || this.formatter).stringify(this)
}
}),
b = (p.format = {}).OpenSSL = {
stringify: function(a) {
var b = a.ciphertext;
a = a.salt;
return (a ? s.create([1398893684,
1701076831
]).concat(a).concat(b) : b).toString(r)
},
parse: function(a) {
a = r.parse(a);
var b = a.words;
if (1398893684 == b[0] && 1701076831 == b[1]) {
var c = s.create(b.slice(2, 4));
b.splice(0, 4);
a.sigBytes -= 16
}
return n.create({
ciphertext: a,
salt: c
})
}
},
a = d.SerializableCipher = l.extend({
cfg: l.extend({
format: b
}),
encrypt: function(a, b, c, d) {
d = this.cfg.extend(d);
var l = a.createEncryptor(c, d);
b = l.finalize(b);
l = l.cfg;
return n.create({
ciphertext: b,
key: c,
iv: l.iv,
algorithm: a,
mode: l.mode,
padding: l.padding,
blockSize: a.blockSize,
formatter: d.format
})
},
decrypt: function(a, b, c, d) {
d = this.cfg.extend(d);
b = this._parse(b, d.format);
return a.createDecryptor(c, d).finalize(b.ciphertext)
},
_parse: function(a, b) {
return "string" == typeof a ? b.parse(a, this) : a
}
}),
p = (p.kdf = {}).OpenSSL = {
execute: function(a, b, c, d) {
d || (d = s.random(8));
a = w.create({
keySize: b + c
}).compute(a, d);
c = s.create(a.words.slice(b), 4 * c);
a.sigBytes = 4 * b;
return n.create({
key: a,
iv: c,
salt: d
})
}
},
c = d.PasswordBasedCipher = a.extend({
cfg: a.cfg.extend({
kdf: p
}),
encrypt: function(b, c, d, l) {
l = this.cfg.extend(l);
d = l.kdf.execute(d,
b.keySize, b.ivSize);
l.iv = d.iv;
b = a.encrypt.call(this, b, c, d.key, l);
b.mixIn(d);
return b
},
decrypt: function(b, c, d, l) {
l = this.cfg.extend(l);
c = this._parse(c, l.format);
d = l.kdf.execute(d, b.keySize, b.ivSize, c.salt);
l.iv = d.iv;
return a.decrypt.call(this, b, c, d.key, l)
}
})
}();
(function() {
for (var u = CryptoJS, p = u.lib.BlockCipher, d = u.algo, l = [], s = [], t = [], r = [], w = [], v = [], b = [], x = [], q = [], n = [], a = [], c = 0; 256 > c; c++) a[c] = 128 > c ? c << 1 : c << 1 ^ 283;
for (var e = 0, j = 0, c = 0; 256 > c; c++) {
var k = j ^ j << 1 ^ j << 2 ^ j << 3 ^ j << 4,
k = k >>> 8 ^ k & 255 ^ 99;
l[e] = k;
s[k] = e;
var z = a[e],
F = a[z],
G = a[F],
y = 257 * a[k] ^ 16843008 * k;
t[e] = y << 24 | y >>> 8;
r[e] = y << 16 | y >>> 16;
w[e] = y << 8 | y >>> 24;
v[e] = y;
y = 16843009 * G ^ 65537 * F ^ 257 * z ^ 16843008 * e;
b[k] = y << 24 | y >>> 8;
x[k] = y << 16 | y >>> 16;
q[k] = y << 8 | y >>> 24;
n[k] = y;
e ? (e = z ^ a[a[a[G ^ z]]], j ^= a[a[j]]) : e = j = 1
}
var H = [0, 1, 2, 4, 8,
16, 32, 64, 128, 27, 54
],
d = d.AES = p.extend({
_doReset: function() {
for (var a = this._key, c = a.words, d = a.sigBytes / 4, a = 4 * ((this._nRounds = d + 6) + 1), e = this._keySchedule = [], j = 0; j < a; j++)
if (j < d) e[j] = c[j];
else {
var k = e[j - 1];
j % d ? 6 < d && 4 == j % d && (k = l[k >>> 24] << 24 | l[k >>> 16 & 255] << 16 | l[k >>> 8 & 255] << 8 | l[k & 255]) : (k = k << 8 | k >>> 24, k = l[k >>> 24] << 24 | l[k >>> 16 & 255] << 16 | l[k >>> 8 & 255] << 8 | l[k & 255], k ^= H[j / d | 0] << 24);
e[j] = e[j - d] ^ k
} c = this._invKeySchedule = [];
for (d = 0; d < a; d++) j = a - d, k = d % 4 ? e[j] : e[j - 4], c[d] = 4 > d || 4 >= j ? k : b[l[k >>> 24]] ^ x[l[k >>> 16 & 255]] ^ q[l[k >>>
8 & 255]] ^ n[l[k & 255]]
},
encryptBlock: function(a, b) {
this._doCryptBlock(a, b, this._keySchedule, t, r, w, v, l)
},
decryptBlock: function(a, c) {
var d = a[c + 1];
a[c + 1] = a[c + 3];
a[c + 3] = d;
this._doCryptBlock(a, c, this._invKeySchedule, b, x, q, n, s);
d = a[c + 1];
a[c + 1] = a[c + 3];
a[c + 3] = d
},
_doCryptBlock: function(a, b, c, d, e, j, l, f) {
for (var m = this._nRounds, g = a[b] ^ c[0], h = a[b + 1] ^ c[1], k = a[b + 2] ^ c[2], n = a[b + 3] ^ c[3], p = 4, r = 1; r < m; r++) var q = d[g >>> 24] ^ e[h >>> 16 & 255] ^ j[k >>> 8 & 255] ^ l[n & 255] ^ c[p++],
s = d[h >>> 24] ^ e[k >>> 16 & 255] ^ j[n >>> 8 & 255] ^ l[g & 255] ^ c[p++],
t =
d[k >>> 24] ^ e[n >>> 16 & 255] ^ j[g >>> 8 & 255] ^ l[h & 255] ^ c[p++],
n = d[n >>> 24] ^ e[g >>> 16 & 255] ^ j[h >>> 8 & 255] ^ l[k & 255] ^ c[p++],
g = q,
h = s,
k = t;
q = (f[g >>> 24] << 24 | f[h >>> 16 & 255] << 16 | f[k >>> 8 & 255] << 8 | f[n & 255]) ^ c[p++];
s = (f[h >>> 24] << 24 | f[k >>> 16 & 255] << 16 | f[n >>> 8 & 255] << 8 | f[g & 255]) ^ c[p++];
t = (f[k >>> 24] << 24 | f[n >>> 16 & 255] << 16 | f[g >>> 8 & 255] << 8 | f[h & 255]) ^ c[p++];
n = (f[n >>> 24] << 24 | f[g >>> 16 & 255] << 16 | f[h >>> 8 & 255] << 8 | f[k & 255]) ^ c[p++];
a[b] = q;
a[b + 1] = s;
a[b + 2] = t;
a[b + 3] = n
},
keySize: 8
});
u.AES = p._createHelper(d)
})();
var CryptoJSAesJson = {
/**
* Encrypt any value
* @param {*} value
* @param {string} password
* @return {string}
*/
'encrypt': function (value, password) {
return CryptoJS.AES.encrypt(JSON.stringify(value), password, { format: CryptoJSAesJson }).toString()
},
/**
* Decrypt a previously encrypted value
* @param {string} jsonStr
* @param {string} password
* @return {*}
*/
'decrypt': function (jsonStr, password) {
return JSON.parse(CryptoJS.AES.decrypt(jsonStr, password, { format: CryptoJSAesJson }).toString(CryptoJS.enc.Utf8))
},
/**
* Stringify cryptojs data
* @param {Object} cipherParams
* @return {string}
*/
'stringify': function (cipherParams) {
var j = { ct: cipherParams.ciphertext.toString(CryptoJS.enc.Base64) }
if (cipherParams.iv) j.iv = cipherParams.iv.toString()
if (cipherParams.salt) j.s = cipherParams.salt.toString()
return JSON.stringify(j).replace(/\s/g, '')
},
/**
* Parse cryptojs data
* @param {string} jsonStr
* @return {*}
*/
'parse': function (jsonStr) {
var j = JSON.parse(jsonStr)
var cipherParams = CryptoJS.lib.CipherParams.create({ ciphertext: CryptoJS.enc.Base64.parse(j.ct) })
if (j.iv) cipherParams.iv = CryptoJS.enc.Hex.parse(j.iv)
if (j.s) cipherParams.salt = CryptoJS.enc.Hex.parse(j.s)
return cipherParams
}
};
CryptoJS.MD5('$string').toString(CryptoJS.enc.Base64).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_")
"""