mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	9anime by Stormunblessed + minor code changes
This commit is contained in:
		
							parent
							
								
									73bcb4145e
								
							
						
					
					
						commit
						428e97ab1c
					
				
					 7 changed files with 331 additions and 43 deletions
				
			
		|  | @ -84,6 +84,7 @@ object APIHolder { | |||
|         KdramaHoodProvider(), | ||||
|         AkwamProvider(), | ||||
|         AnimePaheProvider(), | ||||
|         NineAnimeProvider(), | ||||
|     ) | ||||
| 
 | ||||
|     val restrictedApis = arrayListOf( | ||||
|  |  | |||
|  | @ -87,13 +87,13 @@ class AllAnimeProvider : MainAPI() { | |||
|         @JsonProperty("data") val data: Data | ||||
|     ) | ||||
| 
 | ||||
|     override suspend fun search(query: String): ArrayList<SearchResponse> { | ||||
|     override suspend fun search(query: String): List<SearchResponse> { | ||||
|         val link = | ||||
|             """$mainUrl/graphql?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Afalse%2C%22query%22%3A%22$query%22%7D%2C%22limit%22%3A26%2C%22page%22%3A1%2C%22translationType%22%3A%22sub%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229343797cc3d9e3f444e2d3b7db9a84d759b816a4d84512ea72d079f85bb96e98%22%7D%7D""" | ||||
|         var res = app.get(link).text | ||||
|         if (res.contains("PERSISTED_QUERY_NOT_FOUND")) { | ||||
|             res = app.get(link).text | ||||
|             if (res.contains("PERSISTED_QUERY_NOT_FOUND")) return ArrayList() | ||||
|             if (res.contains("PERSISTED_QUERY_NOT_FOUND")) return emptyList() | ||||
|         } | ||||
|         val response = mapper.readValue<AllAnimeQuery>(res) | ||||
| 
 | ||||
|  | @ -102,7 +102,7 @@ class AllAnimeProvider : MainAPI() { | |||
|             !(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0) | ||||
|         } | ||||
| 
 | ||||
|         return ArrayList(results.map { | ||||
|         return results.map { | ||||
|             AnimeSearchResponse( | ||||
|                 it.name, | ||||
|                 "$mainUrl/anime/${it.Id}", | ||||
|  | @ -115,7 +115,7 @@ class AllAnimeProvider : MainAPI() { | |||
|                 it.availableEpisodes?.dub, | ||||
|                 it.availableEpisodes?.sub | ||||
|             ) | ||||
|         }) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private data class AvailableEpisodesDetail( | ||||
|  | @ -154,11 +154,11 @@ class AllAnimeProvider : MainAPI() { | |||
| 
 | ||||
|         val episodes = showData.availableEpisodes.let { | ||||
|             if (it == null) return@let Pair(null, null) | ||||
|             Pair(if (it.sub != 0) ArrayList((1..it.sub).map { epNum -> | ||||
|             Pair(if (it.sub != 0) ((1..it.sub).map { epNum -> | ||||
|                 AnimeEpisode( | ||||
|                     "$mainUrl/anime/${showData.Id}/episodes/sub/$epNum", episode = epNum | ||||
|                 ) | ||||
|             }) else null, if (it.dub != 0) ArrayList((1..it.dub).map { epNum -> | ||||
|             }) else null, if (it.dub != 0) ((1..it.dub).map { epNum -> | ||||
|                 AnimeEpisode( | ||||
|                     "$mainUrl/anime/${showData.Id}/episodes/dub/$epNum", episode = epNum | ||||
|                 ) | ||||
|  | @ -251,10 +251,9 @@ class AllAnimeProvider : MainAPI() { | |||
|     private fun getM3u8Qualities( | ||||
|         m3u8Link: String, | ||||
|         referer: String, | ||||
|         qualityName: String | ||||
|     ): ArrayList<ExtractorLink> { | ||||
|         return ArrayList( | ||||
|             hlsHelper.m3u8Generation(M3u8Helper.M3u8Stream(m3u8Link, null), true).map { stream -> | ||||
|         qualityName: String, | ||||
|     ): List<ExtractorLink> { | ||||
|         return hlsHelper.m3u8Generation(M3u8Helper.M3u8Stream(m3u8Link, null), true).map { stream -> | ||||
|             val qualityString = if ((stream.quality ?: 0) == 0) "" else "${stream.quality}p" | ||||
|             ExtractorLink( | ||||
|                 this.name, | ||||
|  | @ -265,7 +264,7 @@ class AllAnimeProvider : MainAPI() { | |||
|                 true, | ||||
|                 stream.headers | ||||
|             ) | ||||
|             }) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun loadLinks( | ||||
|  |  | |||
|  | @ -27,12 +27,12 @@ class AnimeFlickProvider : MainAPI() { | |||
|         TvType.OVA | ||||
|     ) | ||||
| 
 | ||||
|     override suspend fun search(query: String): ArrayList<SearchResponse> { | ||||
|     override suspend fun search(query: String): List<SearchResponse> { | ||||
|         val link = "https://animeflick.net/search.php?search=$query" | ||||
|         val html = app.get(link).text | ||||
|         val doc = Jsoup.parse(html) | ||||
| 
 | ||||
|         return ArrayList(doc.select(".row.mt-2").map { | ||||
|         return doc.select(".row.mt-2").map { | ||||
|             val href = mainUrl + it.selectFirst("a").attr("href") | ||||
|             val title = it.selectFirst("h5 > a").text() | ||||
|             val poster = mainUrl + it.selectFirst("img").attr("src").replace("70x110", "225x320") | ||||
|  | @ -45,7 +45,7 @@ class AnimeFlickProvider : MainAPI() { | |||
|                 null, | ||||
|                 EnumSet.of(DubStatus.Subbed), | ||||
|             ) | ||||
|         }) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun load(url: String): LoadResponse { | ||||
|  |  | |||
|  | @ -0,0 +1,274 @@ | |||
| package com.lagradost.cloudstream3.animeproviders | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.parseJson | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.loadExtractor | ||||
| import org.jsoup.Jsoup | ||||
| import java.util.* | ||||
| 
 | ||||
| class NineAnimeProvider : MainAPI() { | ||||
|     override val mainUrl = "https://9anime.center" | ||||
|     override val name = "9Anime" | ||||
|     override val hasMainPage = true | ||||
|     override val hasChromecastSupport = true | ||||
|     override val hasDownloadSupport = true | ||||
|     override val supportedTypes = setOf(TvType.Anime) | ||||
| 
 | ||||
|     override suspend fun getMainPage(): HomePageResponse { | ||||
|         val items = listOf( | ||||
|             Pair("$mainUrl/ajax/home/widget?name=trending", "Trending"), | ||||
|             Pair("$mainUrl/ajax/home/widget?name=updated_all", "All"), | ||||
|             Pair("$mainUrl/ajax/home/widget?name=updated_sub&page=1", "Recently Updated (SUB)"), | ||||
|             Pair("$mainUrl/ajax/home/widget?name=updated_dub&page=1", "Recently Updated (DUB)"), | ||||
|             Pair( | ||||
|                 "$mainUrl/ajax/home/widget?name=updated_chinese&page=1", | ||||
|                 "Recently Updated (Chinese)" | ||||
|             ), | ||||
|             Pair("$mainUrl/ajax/home/widget?name=random", "Random"), | ||||
|         ).map { (url, name) -> | ||||
|             val home = Jsoup.parse( | ||||
|                 app.get( | ||||
|                     url | ||||
|                 ).mapped<Response>().html | ||||
|             ).select("ul.anime-list li").map { | ||||
|                 val title = it.selectFirst("a.name").text() | ||||
|                 val link = it.selectFirst("a").attr("href") | ||||
|                 val poster = it.selectFirst("a.poster img").attr("src") | ||||
|                 AnimeSearchResponse( | ||||
|                     title, | ||||
|                     link, | ||||
|                     this.name, | ||||
|                     TvType.Anime, | ||||
|                     poster, | ||||
|                     null, | ||||
|                     if (title.contains("(DUB)") || title.contains("(Dub)")) EnumSet.of( | ||||
|                         DubStatus.Dubbed | ||||
|                     ) else EnumSet.of(DubStatus.Subbed), | ||||
|                 ) | ||||
|             } | ||||
| 
 | ||||
|             HomePageList(name, home) | ||||
|         } | ||||
| 
 | ||||
|         return HomePageResponse(items) | ||||
|     } | ||||
| 
 | ||||
|     //Credits to https://github.com/jmir1 | ||||
|     private val key = "0wMrYU+ixjJ4QdzgfN2HlyIVAt3sBOZnCT9Lm7uFDovkb/EaKpRWhqXS5168ePcG" | ||||
| 
 | ||||
|     private fun getVrf(id: String): String? { | ||||
|         val reversed = ue(encode(id) + "0000000").slice(0..5).reversed() | ||||
| 
 | ||||
|         return reversed + ue(je(reversed, encode(id) ?: return null)).replace( | ||||
|             """=+$""".toRegex(), | ||||
|             "" | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     private fun getLink(url: String): String? { | ||||
|         val i = url.slice(0..5) | ||||
|         val n = url.slice(6..url.lastIndex) | ||||
|         return decode(je(i, ze(n))) | ||||
|     } | ||||
| 
 | ||||
|     private fun ue(input: String): String { | ||||
|         if (input.any { it.code >= 256 }) throw Exception("illegal characters!") | ||||
|         var output = "" | ||||
|         for (i in input.indices step 3) { | ||||
|             val a = intArrayOf(-1, -1, -1, -1) | ||||
|             a[0] = input[i].code shr 2 | ||||
|             a[1] = (3 and input[i].code) shl 4 | ||||
|             if (input.length > i + 1) { | ||||
|                 a[1] = a[1] or (input[i + 1].code shr 4) | ||||
|                 a[2] = (15 and input[i + 1].code) shl 2 | ||||
|             } | ||||
|             if (input.length > i + 2) { | ||||
|                 a[2] = a[2] or (input[i + 2].code shr 6) | ||||
|                 a[3] = 63 and input[i + 2].code | ||||
|             } | ||||
|             for (n in a) { | ||||
|                 if (n == -1) output += "=" | ||||
|                 else { | ||||
|                     if (n in 0..63) output += key[n] | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return output; | ||||
|     } | ||||
| 
 | ||||
|     private fun je(inputOne: String, inputTwo: String): String { | ||||
|         val arr = IntArray(256) { it } | ||||
|         var output = "" | ||||
|         var u = 0 | ||||
|         var r: Int | ||||
|         for (a in arr.indices) { | ||||
|             u = (u + arr[a] + inputOne[a % inputOne.length].code) % 256 | ||||
|             r = arr[a] | ||||
|             arr[a] = arr[u] | ||||
|             arr[u] = r | ||||
|         } | ||||
|         u = 0 | ||||
|         var c = 0 | ||||
|         for (f in inputTwo.indices) { | ||||
|             c = (c + f) % 256 | ||||
|             u = (u + arr[c]) % 256 | ||||
|             r = arr[c] | ||||
|             arr[c] = arr[u] | ||||
|             arr[u] = r | ||||
|             output += (inputTwo[f].code xor arr[(arr[c] + arr[u]) % 256]).toChar() | ||||
|         } | ||||
|         return output | ||||
|     } | ||||
| 
 | ||||
|     private fun ze(input: String): String { | ||||
|         val t = if (input.replace("""[\t\n\f\r]""".toRegex(), "").length % 4 == 0) { | ||||
|             input.replace("""/==?$/""".toRegex(), "") | ||||
|         } else input | ||||
|         if (t.length % 4 == 1 || t.contains("""[^+/0-9A-Za-z]""".toRegex())) throw Exception("bad input") | ||||
|         var i: Int | ||||
|         var r = "" | ||||
|         var e = 0 | ||||
|         var u = 0 | ||||
|         for (o in t.indices) { | ||||
|             e = e shl 6 | ||||
|             i = key.indexOf(t[o]) | ||||
|             e = e or i | ||||
|             u += 6 | ||||
|             if (24 == u) { | ||||
|                 r += ((16711680 and e) shr 16).toChar() | ||||
|                 r += ((65280 and e) shr 8).toChar() | ||||
|                 r += (255 and e).toChar() | ||||
|                 e = 0 | ||||
|                 u = 0 | ||||
|             } | ||||
|         } | ||||
|         return if (12 == u) { | ||||
|             e = e shr 4 | ||||
|             r + e.toChar() | ||||
|         } else { | ||||
|             if (18 == u) { | ||||
|                 e = e shr 2 | ||||
|                 r += ((65280 and e) shr 8).toChar() | ||||
|                 r += (255 and e).toChar() | ||||
|             } | ||||
|             r | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun encode(input: String): String? = java.net.URLEncoder.encode(input, "utf-8") | ||||
| 
 | ||||
|     private fun decode(input: String): String? = java.net.URLDecoder.decode(input, "utf-8") | ||||
| 
 | ||||
|     override suspend fun search(query: String): List<SearchResponse> { | ||||
|         val url = "$mainUrl/filter?sort=title%3Aasc&keyword=$query" | ||||
| 
 | ||||
|         return app.get(url).document.select("ul.anime-list li").mapNotNull { | ||||
|             val title = it.selectFirst("a.name").text() | ||||
|             val href = | ||||
|                 fixUrlNull(it.selectFirst("a").attr("href"))?.replace(Regex("(\\?ep=(\\d+)\$)"), "") | ||||
|                     ?: return@mapNotNull null | ||||
|             val image = it.selectFirst("a.poster img").attr("src") | ||||
|             AnimeSearchResponse( | ||||
|                 title, | ||||
|                 href, | ||||
|                 this.name, | ||||
|                 TvType.Anime, | ||||
|                 image, | ||||
|                 null, | ||||
|                 if (title.contains("(DUB)") || title.contains("(Dub)")) EnumSet.of( | ||||
|                     DubStatus.Dubbed | ||||
|                 ) else EnumSet.of(DubStatus.Subbed), | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     data class Response( | ||||
|         @JsonProperty("html") val html: String | ||||
|     ) | ||||
| 
 | ||||
|     override suspend fun load(url: String): LoadResponse? { | ||||
|         val urlclean = url.substringAfter("watch/") | ||||
|         val regexID = Regex("(\\.[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") | ||||
|         val animeid = regexID.find(urlclean)?.value?.replace(".", "") ?: return null | ||||
|         val animeidencoded = encode(getVrf(animeid) ?: return null) | ||||
| 
 | ||||
|         val doc = app.get(url).document | ||||
|         val poster = doc.selectFirst("aside.main div.thumb div img").attr("src") | ||||
|         val title = doc.selectFirst(".info .title").text() | ||||
|         val description = doc.selectFirst("div.info p").text().replace("Ver menos", "").trim() | ||||
|         val episodes = Jsoup.parse( | ||||
|             app.get( | ||||
|                 "$mainUrl/ajax/anime/servers?ep=1&id=${animeid}&vrf=$animeidencoded&ep=8&episode=&token=" | ||||
|             ).mapped<Response>().html | ||||
|         )?.select("ul.episodes li a")?.mapNotNull { | ||||
|             val link = it?.attr("href") ?: return@mapNotNull null | ||||
|             val epnum = it.attr("data-base")?.toIntOrNull() | ||||
|             AnimeEpisode(link, episode = epnum) | ||||
|         } ?: return null | ||||
| 
 | ||||
|         return newAnimeLoadResponse(title, url, TvType.Anime) { | ||||
|             posterUrl = poster | ||||
|             addEpisodes(DubStatus.Subbed, episodes) | ||||
|             plot = description | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     data class Links( | ||||
|         @JsonProperty("url") val url: String | ||||
|     ) | ||||
| 
 | ||||
|     data class Servers( | ||||
|         @JsonProperty("28") val mcloud: String?, | ||||
|         @JsonProperty("35") val mp4upload: String?, | ||||
|         @JsonProperty("40") val streamtape: String?, | ||||
|         @JsonProperty("41") val vidstream: String?, | ||||
|         @JsonProperty("43") val videovard: String? | ||||
|     ) | ||||
| 
 | ||||
|     override suspend fun loadLinks( | ||||
|         data: String, | ||||
|         isCasting: Boolean, | ||||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         val urlclean = data.substringAfter("watch/") | ||||
|         val regexID = Regex("(\\.[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)") | ||||
|         val animeid = regexID.find(urlclean)?.value?.replace(".", "") ?: return false | ||||
| 
 | ||||
|         val animeidencoded = encode(getVrf(animeid) ?: return false) | ||||
| 
 | ||||
|         Jsoup.parse( | ||||
|             app.get( | ||||
|                 "$mainUrl/ajax/anime/servers?&id=${animeid}&vrf=$animeidencoded&episode=&token=" | ||||
|             ).mapped<Response>().html | ||||
|         ).select("div.body").map { element -> | ||||
|             val jsonregex = Regex("(\\{.+\\}.*$data)") | ||||
|             val servers = jsonregex.find(element.toString())?.value?.replace( | ||||
|                 Regex("(\".*data-base=.*href=\"$data)"), | ||||
|                 "" | ||||
|             )?.replace(""", "\"") ?: return@map | ||||
| 
 | ||||
|             val jsonservers = parseJson<Servers?>(servers) ?: return@map | ||||
|             listOfNotNull( | ||||
|                 jsonservers.vidstream, | ||||
|                 jsonservers.mcloud, | ||||
|                 jsonservers.mp4upload, | ||||
|                 jsonservers.streamtape | ||||
|             ).mapNotNull { | ||||
|                 val epserver = app.get("$mainUrl/ajax/anime/episode?id=$it").text | ||||
|                 (if (epserver.contains("url")) { | ||||
|                     parseJson<Links>(epserver) | ||||
|                 } else null)?.url?.let { it1 -> getLink(it1.replace("=", "")) } | ||||
|                     ?.replace("/embed/", "/e/") | ||||
|             }.apmap { url -> | ||||
|                 loadExtractor( | ||||
|                     url, data, callback | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return true | ||||
|     } | ||||
| } | ||||
|  | @ -1,12 +1,14 @@ | |||
| package com.lagradost.cloudstream3.extractors | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
| import com.lagradost.cloudstream3.app | ||||
| import com.fasterxml.jackson.annotation.JsonProperty | ||||
| import com.lagradost.cloudstream3.USER_AGENT | ||||
| import com.lagradost.cloudstream3.apmap | ||||
| import com.lagradost.cloudstream3.app | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.parseJson | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.utils.ExtractorApi | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.M3u8Helper | ||||
| import com.lagradost.cloudstream3.utils.getQualityFromName | ||||
| 
 | ||||
| open class Mcloud : ExtractorApi() { | ||||
|     override val name = "Mcloud" | ||||
|  | @ -30,6 +32,11 @@ open class Mcloud : ExtractorApi() { | |||
|         val link = url.replace("$mainUrl/e/","$mainUrl/info/") | ||||
|         val response = app.get(link, headers = headers).text | ||||
| 
 | ||||
|         if(response.startsWith("<!DOCTYPE html>")) { | ||||
|             // TODO decrypt html for link | ||||
|             return emptyList() | ||||
|         } | ||||
| 
 | ||||
|         data class Sources ( | ||||
|             @JsonProperty("file") val file: String | ||||
|         ) | ||||
|  | @ -43,7 +50,7 @@ open class Mcloud : ExtractorApi() { | |||
|             @JsonProperty("media") val media: Media, | ||||
|         ) | ||||
| 
 | ||||
|         val mapped = response.let { parseJson<JsonMcloud>(it) } | ||||
|         val mapped = parseJson<JsonMcloud>(response) | ||||
|         val sources = mutableListOf<ExtractorLink>() | ||||
| 
 | ||||
|         if (mapped.success) | ||||
|  |  | |||
|  | @ -6,7 +6,11 @@ import com.lagradost.cloudstream3.app | |||
| import com.lagradost.cloudstream3.mapper | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
| 
 | ||||
| class WcoStream : ExtractorApi() { | ||||
| class Vidstreamz : WcoStream() { | ||||
|     override val mainUrl: String = "https://vidstreamz.online" | ||||
| } | ||||
| 
 | ||||
| open class WcoStream : ExtractorApi() { | ||||
|     override val name = "VidStream" //Cause works for animekisa and wco | ||||
|     override val mainUrl = "https://vidstream.pro" | ||||
|     override val requiresReferer = false | ||||
|  | @ -16,8 +20,8 @@ class WcoStream : ExtractorApi() { | |||
|         val baseUrl = url.split("/e/")[0] | ||||
| 
 | ||||
|         val html = app.get(url, headers = mapOf("Referer" to "https://wcostream.cc/")).text | ||||
|         val (Id) = "/e/(.*?)?domain".toRegex().find(url)!!.destructured | ||||
|         val (skey) = """skey\s=\s['"](.*?)['"];""".toRegex().find(html)!!.destructured | ||||
|         val (Id) = ("/e/(.*?)?domain".toRegex().find(url)?.destructured ?: Regex("""/e/(.*)""").find(url)?.destructured) ?: return emptyList() | ||||
|         val (skey) = """skey\s=\s['"](.*?)['"];""".toRegex().find(html)?.destructured ?: return emptyList() | ||||
| 
 | ||||
|         val apiLink = "$baseUrl/info/$Id?domain=wcostream.cc&skey=$skey" | ||||
|         val referrer = "$baseUrl/e/$Id?domain=wcostream.cc" | ||||
|  | @ -44,8 +48,10 @@ class WcoStream : ExtractorApi() { | |||
|         if (mapped.success) { | ||||
|             mapped.media.sources.forEach { | ||||
|                 if (it.file.contains("m3u8")) { | ||||
|                     hlsHelper.m3u8Generation(M3u8Helper.M3u8Stream(it.file, null), true).forEach { stream -> | ||||
|                         val qualityString = if ((stream.quality ?: 0) == 0) "" else "${stream.quality}p" | ||||
|                     hlsHelper.m3u8Generation(M3u8Helper.M3u8Stream(it.file, null), true) | ||||
|                         .forEach { stream -> | ||||
|                             val qualityString = | ||||
|                                 if ((stream.quality ?: 0) == 0) "" else "${stream.quality}p" | ||||
|                             sources.add( | ||||
|                                 ExtractorLink( | ||||
|                                     name, | ||||
|  |  | |||
|  | @ -94,6 +94,7 @@ suspend fun loadExtractor(url: String, referer: String? = null, callback: (Extra | |||
| val extractorApis: Array<ExtractorApi> = arrayOf( | ||||
|     //AllProvider(), | ||||
|     WcoStream(), | ||||
|     Vidstreamz(), | ||||
|     Mp4Upload(), | ||||
|     StreamTape(), | ||||
|     MixDrop(), | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue