import groovy.json.JsonSlurper
import org.serviio.library.metadata.*
import org.serviio.library.online.*
/**
 * WebResource extractor plugin for tunein.com.
 * 
 * @author Petr Nejedly
 * Fixed In-com Software - 18.10.2020
 * 
 */
class TuneIn extends WebResourceUrlExtractor {
	
	final VALID_FEED_URL = '^(?:https?://)?(?:www\\.)?tunein\\.com/(radio/|search/\\?query=).+'
	final USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0'
	
	String getExtractorName() {
		return getClass().getName()
	}
	
	boolean extractorMatches(URL feedUrl) {
		return feedUrl ==~ VALID_FEED_URL
	}
	
	int getVersion() {
		10
	}
	
	WebResourceContainer extractItems(URL resourceUrl, int maxItems) {
		String html = openURL(resourceUrl, USER_AGENT)
		def titleMatcher = html =~ '(?s)<h1.*?>(.+?)<'
		String pageTitle = titleMatcher[0][1].trim()
		List<WebResourceItem> items = []
		int initPos = html.indexOf('INITIAL_STATE=') + 14
		def json = new JsonSlurper().parseText(html.substring(initPos, html.indexOf(';</script>', initPos)))
		def containerNode = null
		if(!json.categories.isEmpty()) {
			containerNode = json.categories.iterator().next().value
		} else if(!json.search.isEmpty()) {
			containerNode = json.search.searchResults
		} else if(!json.profiles.isEmpty()) {
			containerNode = json.profiles.iterator().next().value
		}
		if(containerNode) {
			def stationsNode = containerNode.containerItems.find { it.containerType == 'Stations' || it.containerType == 'TrendingStations' || it.containerType == 'LocalStations' || it.containerType == 'LiveStations' }
			if (stationsNode != null) {
				def stations = stationsNode.children
				def itemsAdded = 0
				for (int i = 0; i < stations.size() && (maxItems == -1 || itemsAdded < maxItems); i++) {
					def stationNode = stations.get(i)
					String stationTitle = stationNode.title.trim()
					String imageUrl = stationNode.image
					String guideId = stationNode.guideId
					WebResourceItem item = new WebResourceItem(title: stationTitle, additionalInfo: ['guideId': guideId, 'imageUrl': imageUrl])
					items << item
					itemsAdded++
				}
				return new WebResourceContainer(title: pageTitle, items: items)
			}
		}
		return new WebResourceContainer(title: pageTitle, items: [])
	}
	
	ContentURLContainer extractUrl(WebResourceItem item, PreferredQuality requestedQuality) {
		String guideId = item.getAdditionalInfo()['guideId']
		assert guideId != null
		String jsonUrl = "https://opml.radiotime.com/Tune.ashx?id=$guideId&render=json&itemToken=BgcHAAEAAQABAAIACxEMT-IAAAMHBwUTAwAAABMWAAAA&formats=mp3,aac,ogg,flash,html,hls&type=station&us_privacy=1YNY&paln=&gdpr=0&serial=ad460bb5-b5c4-40f7-b322-70ab1ab5f195&partnerId=RadioTime&version=4.2801&itemUrlScheme=secure&reqAttempt=1"
		String playerJson = openURL(new URL(jsonUrl), USER_AGENT)
		def jsonResult = new JsonSlurper().parseText(playerJson)
		String streamUrl = getStreamUrl(jsonResult)
		if(streamUrl == null || streamUrl == "") {
			log('No suitable streams found')
			return null
		}
		def live = true
		return new ContentURLContainer(fileType: MediaFileType.AUDIO, contentUrl: streamUrl, thumbnailUrl: item.getAdditionalInfo()['imageUrl'], live: live)
	}
	
	private String getStreamUrl(Map jsonResult) {
		List streams = jsonResult['body']
		List directStreams = streams.findAll { it['is_direct'] == true && ['mp3','aac','ogg','html','hls'].contains(it['media_type']) }
		String bestStreamUrl = findBestStream(directStreams)
		if(bestStreamUrl == null) {
			List proxyStreams = streams.findAll { it['is_direct'] == false && ['mp3','aac','ogg','html','hls'].contains(it['media_type']) }
			return findBestStream(proxyStreams)
		} else {
			return bestStreamUrl
		}
	}
	
	private String findBestStream(List streams) {
		if( streams.size() > 0 ) {
			streams = streams.sort { it -> it['bitrate'].toInteger() }
			Map selectedStream = streams.last()
			String streamUrl = selectedStream['url']
			boolean isProxy = selectedStream['is_direct'] == false
			if(isProxy && streamUrl.startsWith("http")) {
				return getProxyStreamUrl(streamUrl)
			} else {
				return streamUrl
			}
		} else {
			return null
		}
	}
	
	private String getProxyStreamUrl(String proxyUrl) {
		String playerJson = openURL(new URL(proxyUrl), USER_AGENT)
		def jsonResult = new JsonSlurper().parseText(playerJson)
		List streams = jsonResult['Streams']
		streams = streams.findAll { it -> it['Type'] == 'Live' && ['MP3','Windows','AAC','Flash','HTML5'].contains(it['MediaType']) && it['Url'].indexOf('adType') == -1 }
		if( streams.size() > 0 ) {
			streams = streams.sort { it -> it['Bandwidth'].toInteger() }
			Map selectedStream = streams.last()
			String streamUrl = selectedStream['Url']
			boolean hasPlaylist = Boolean.valueOf(selectedStream['HasPlaylist'])
			if(hasPlaylist && streamUrl.startsWith("http")) {
				return getUrlFromPlaylist(new URL(streamUrl))
			} else {
				return streamUrl
			}
		} else {
			return null
		}
	}
	
	protected getUrlFromPlaylist(URL playlistUrl) {
		assert playlistUrl != null
		String playlist = playlistUrl.getText()
		if(playlist.toLowerCase().startsWith("<asx")) {
			return null
		} else {
		    List lines = playlist.readLines()
			String url = lines[0]
			if(url.startsWith ('[playlist]') ) {
				for(String line : lines) {
					if(line.startsWith('File1')) {
						url = line.substring(line.indexOf ('=') + 1).trim()
						break
					}
				}
			}
			if (url.endsWith('.')) {
				url = url.substring(0, url.size()-1)
			}
			if(url.indexOf('.m3u') > -1 || url.indexOf('.pls') > -1) {
				return getUrlFromPlaylist(new URL(url))
			}
			return url
		}
	}
}
