import groovy.json.JsonSlurper
import org.serviio.library.metadata.*
import org.serviio.library.online.*
import org.serviio.util.*

/**
 * WebResource extractor plugin for billboardTop20.
 * 
 * @author jhb50
 * Version 1 - Sep 21, 2012
 * Version 2 - Oct 21, 2012 - Fix YouTube change, add 'for' parameter
 * Version 3 - Dec  9, 2012 - Fix for YouTube change
 * Version 4 - Dec 20, 2012 - Support revised YouTube format
 * Version 5 - Feb  3, 2013 - Add YouTube Expiry Date
 * Version 6 - Feb 16, 2013 - Support new YouTube format

 *
 * Usage:
 *  http://www.billboardtop20.com
 *   
 *  with optional parameters
 *
 *  for codec codec=8 for web8 or 264 for h264
 *    default is 264
 *
 *  for resolution res=nnnn where nnnn = 1080, 720, 480, 360, 240
 *    default is console quality value
 *
 *  for starting video start=nnn where nnn=1 to 20
 *    default is 1
 *
 *  for number of videos to display starting at 'start'  for=nnn where nnn=1 to 21-start
 *    default is 20
 *
 * for xbox [nnn] numbering nform=xbox
 *
 */
class BillboardTop20 extends WebResourceUrlExtractor {
	
	final VALID_FEED_URL = '^http://www\\.billboardtop20\\.com.*?'

	String getExtractorName() {
		return 'BillboardTop20'
	}

	int getVersion() {
		return 6
	}

	boolean extractorMatches(URL feedUrl) {
		return feedUrl ==~ VALID_FEED_URL
	}

    Boolean streamNotSupported(
        String htmlText, 
        String streamNotSupportedChecker
    ) {        
        def notsupported_check = htmlText =~ streamNotSupportedChecker
        if (notsupported_check.count > 0) {
            return true
        }
        return false
    }
	
	WebResourceContainer extractItems(URL resourceUrl, int maxItems) {
	
		log("Parsing with BillboardTop20 V6")

		List<WebResourceItem> items = []
		def itemsAdded = 0
		String pageTitle = ""
		String pageThumb = ""
		String videoUrl = ""
		String videoTitle = ""
		String thumbUrl = ""
		Short Start = 0 
		Short For = 100
		Boolean ForSet = false
		Short Res = 0
		Short Codec = 264
		String Type = "weekly"
		String Nform = ""
		
		def parmMatcher = resourceUrl =~ '^http://www\\.billboardtop20\\.com.*?start=([0-9]+)'
		def parmMatch = resourceUrl ==~ '^http://www\\.billboardtop20\\.com.*?start=[0-9]+.*?'
		if (parmMatch){
			Start = parmMatcher[0][1].trim().toShort() - 1
			if(Start < 0 || Start > 19) Start = 0
		}

		parmMatcher = resourceUrl =~ '^http://www\\.billboardtop20\\.com.*?for=([0-9]+)'
		parmMatch = resourceUrl ==~ '^http://www\\.billboardtop20\\.com.*?for=[0-9]+.*?'
		if (parmMatch){
			For = parmMatcher[0][1].trim().toShort()
			ForSet = true
			if(For < 1 || For > 20) For = 20
		}
		parmMatcher = resourceUrl =~ '^http://www\\.billboardtop20\\.com.*?res=([0-9]+)'
		parmMatch = resourceUrl ==~ '^http://www\\.billboardtop20\\.com.*?res=[0-9]+.*?'
		if (parmMatch){
			Res = parmMatcher[0][1].trim().toShort() 
			if(Res != 1080 && Res != 720 && Res != 480 && Res != 360 && Res != 240) Res = 0
		}

		parmMatcher = resourceUrl =~ '^http://www\\.billboardtop20\\.com.*?codec=([0-9]+)'
		parmMatch = resourceUrl ==~ '^http://www\\.billboardtop20\\.com.*?codec=[0-9]+.*?'
		if (parmMatch){
			Codec = parmMatcher[0][1].trim().toShort() 
			if(Codec != 8 && Codec != 264) Codec = 264
		}
		
		parmMatcher = resourceUrl =~ '^http://www\\.billboardtop20\\.com.*?nform=([a-z]+)'
		parmMatch = resourceUrl ==~ '^http://www\\.billboardtop20\\.com.*?nform=[a-z]+.*?'
		if (parmMatch){
			Nform = parmMatcher[0][1].trim()
			if(Nform != "xbox" ) Nform = ""
		}

		String html 

/*		html = new URL("http://www.youtube.com/playlist?list=MCUS.yyyymmdd").getText()
		def titleMatcher = html =~ '(?s)playlist.list=BB(.*?)&.*?'
		String listDate = titleMatcher[0][1].trim()
*/

		long curTimea = System.currentTimeMillis()/60000
		String listDate = new Date(System.currentTimeMillis()).format("yyyyMMdd").trim()	 

		pageTitle = "Billboard Top 20"
		
		int j
		for( j = 1; j < 15; j++ ) {

			html = new URL("http://www.youtube.com/playlist?list=BB"+listDate).getText("utf-8")
			
			if (streamNotSupported(html, 'The playlist does not exist.*?')
				|| streamNotSupported(html, 'This playlist has no videos.*?')                    
				) { 
				listDate = new Date(System.currentTimeMillis()).minus(j).format("yyyyMMdd").trim()

			}
			else break
		}
		
		if (j == 15) return null

	    if (html.count('<li class="playlist-video-item') > 0 ){

	 
			
			def videoMatcher = html =~ '(?s)<li.class="playlist-video-item.*?<img.(.*?)width.*?<a href="(.+?)&amp.*?<span.class="title.video-title.*?>(.*?)<'
			
			if (ForSet) maxItems = For

			for( int i = Start; i < videoMatcher.size() && (maxItems == -1 || itemsAdded < maxItems) ; i++ ) {
				videoUrl = "http://www.youtube.com" + videoMatcher[i][2].trim()
				
				String Nformat = i+1
				if(Nform == "xbox"){
					if(i<9) Nformat = "00" + Nformat
					else
					if(i<99) Nformat = "0" + Nformat
				}
				
				videoTitle = "[" + Nformat + "] " + videoMatcher[i][3].trim()
				
				videoTitle = videoTitle.replaceAll("&#39;","'")
				videoTitle = videoTitle.replaceAll("&amp;","&")
				videoTitle = videoTitle.replaceAll("&quot;",'"')
				
				thumbUrl = videoMatcher[i][1].trim()
				def thumbMatcher = thumbUrl =~ '(?s)src="(.*?)"'
				if(thumbUrl.contains("data-thumb=")) thumbMatcher = thumbUrl =~ '(?s).*?data-thumb="(.*?)"'
				thumbUrl = "http:" + thumbMatcher[0][1]
				
				
				WebResourceItem item = new WebResourceItem(title: videoTitle, additionalInfo: ['videoUrl':videoUrl,'thumbUrl':thumbUrl, itemNum: i, 'Res': Res, 'Codec': Codec])
				
				items << item
				itemsAdded++
			}
		}
		return new WebResourceContainer(title: pageTitle, thumbnailUrl: pageThumb, items: items)
	}

	
	ContentURLContainer extractUrl(WebResourceItem item, PreferredQuality requestedQuality) {		
		println "\r\nEXTRACT"
		String videoTitle = item.title
		String videoUrl = item.getAdditionalInfo()['videoUrl']
		String thumbnailUrl = item.getAdditionalInfo()['thumbUrl']
		String itemNum = item.getAdditionalInfo()['itemNum'] + 1
		Short Res = item.getAdditionalInfo()['Res']
		Short Codec = item.getAdditionalInfo()['Codec']
		
		String videohtml = new URL(videoUrl).getText()
		//def flashMatch = videohtml ==~ '(?s).*?url_encoded_fmt_stream_map=.*?'
		def flashMatch = videohtml ==~ '(?s).*?url_encoded_fmt_stream_map":.*?'
		if (!flashMatch){
			log("NO ENTRIES FOR THIS ITEM - \'$videoTitle\'") 
			return null
		}

		
		def videoMatch = videohtml =~ '(?s)stream_map":.*?"(.*?)".*?'
		
		String fmt
		String linkUrl
	/*
	high quality
	'38',4096x3072 MP4-AVC/AAC
    '46',1920x1080 WEBM-VP8/Vorbis
	'37',1920x1080 MP4-AVC/AAC
	'45',1280x720  WEBM-VP8/Vorbis
	'22',1280x720  MP4-AVC/AAC
	medium quality
	'44 , 854x480  WEBM-VP8/Vorbis 
	'35', 854x480  flv-AVC/AAC   
	'43', 640x360  WEBM-VP8/Vorbis 
	'18', 640x360  MP4-AVC/AAC
	'34', 640x360  flv-AVC/AAC
	lowquality
    '6',  480x270  flv-h263/MP3
	'5',  400x240  flv-h263/MP3
	'36'  320x240  3gp-MPEG4/AAC
	'17', 176x144  3gp-MPEG4/AAC  2Mbps
	'13'  176x144  3gp-MPEG4/AAC .5Mbps

	*/    
 		String high8_1080Formats    = "46, 37, 45, 22, 44, 35, 43, 18, 34, 6, 5"
		String high8_720Formats     =         "45, 22, 44, 35, 43, 18, 34, 6, 5"
		String high264_1080Formats  = "37, 22, 35, 18, 34, 6, 5"
 		String high264_720Formats   =     "22, 35, 18, 34, 6, 5"
        String medium8_480Formats   = "35, 44, 18, 34, 43, 6, 5"
        String medium8_360Formats   =         "18, 34, 43, 6, 5"
		String medium264_480Formats = "35, 18, 34, 6, 5"
		String medium264_360Formats =     "18, 34, 6, 5"
		
        String lowFormats = "6, 5"

		def videoMatcher = videoMatch[0][1] =~ '(?s)(.*?)itag%3D(\\d\\d?)(.*?),'

		int i
		for( i = 0; i < videoMatcher.size(); i++ ) {

			fmt = videoMatcher[i][2].trim()

			
			if (Res == 0){
				if (Codec == 8) { 
					if(requestedQuality == PreferredQuality.HIGH && high8_1080Formats.contains(fmt)) break
					else
					if(requestedQuality == PreferredQuality.MEDIUM && medium8_480Formats.contains(fmt))	break
				}
				else
				if (Codec == 264) {
					if(requestedQuality == PreferredQuality.HIGH && high264_1080Formats.contains(fmt)) break
					else
					if(requestedQuality == PreferredQuality.MEDIUM && medium264_480Formats.contains(fmt)) break
				}	
				else if(requestedQuality == PreferredQuality.LOW && lowFormats.contains(fmt)) break
			}
			else if(Res == 1080) {
				if (Codec == 8 && high8_1080Formats.contains(fmt)) break
				else
				if (Codec == 264 && high264_1080Formats.contains(fmt)) break
			}
			else if(Res == 720) {
				if (Codec == 8 && high8_720Formats.contains(fmt)) break
				else
				if (Codec == 264 && high264_720Formats.contains(fmt)) break
			}
			else if(Res == 480) {
				if (Codec == 8 && medium8_480Formats.contains(fmt)) break
				else
				if (Codec == 264 && medium264_480Formats.contains(fmt)) break
			}
			else if(Res == 360) {
				if (Codec == 8 && medium8_360Formats.contains(fmt)) break
				else
				if (Codec == 264 && medium264_360Formats.contains(fmt)) break
			}
			else if(Res == 240) {
				if (lowFormats.contains(fmt)) break
			}
		}
		
		linkUrl = videoMatcher[i][1] + "itag%3D" + videoMatcher[i][2] + videoMatcher[i][3] + "&,"
		
		linkUrl = linkUrl.replaceAll(".u0026","&")

		videoMatcher = linkUrl =~ '(.*?)url=(.*?),'
		linkUrl = videoMatcher[0][2] + videoMatcher[0][1] + ","

		videoMatcher = linkUrl =~ '(.*?)type=.*?&(.*?),'
		linkUrl = videoMatcher[0][1] + videoMatcher[0][2] + ","

		videoMatcher = linkUrl =~ '(.*?)fallback_host=.*?&(.*?),'
		linkUrl = videoMatcher[0][1] + videoMatcher[0][2] + ","

		videoMatcher = linkUrl =~ '(.*?)itag=.*?&(.*?),'
		linkUrl = videoMatcher[0][1] + videoMatcher[0][2] + ","
		
		linkUrl = linkUrl.replaceFirst("sig","signature")

		linkUrl = linkUrl.replaceFirst("&,","")
		println linkUrl

		videoMatcher = linkUrl =~ 'expire%3D(.*?)[%&]'
		def expiryDate = new Date(videoMatcher[0][1].trim().toLong()*1000)


		linkUrl = URLDecoder.decode(linkUrl)
		linkUrl = URLDecoder.decode(linkUrl)
		linkUrl = URLDecoder.decode(linkUrl)

		def cacheKey = videoUrl  + "_" +  fmt
	
		return new ContentURLContainer(fileType: MediaFileType.VIDEO, contentUrl: linkUrl, thumbnailUrl: thumbnailUrl, cacheKey: cacheKey, expiresOn: expiryDate, expiresImmediately: false)
	}
	
	static void main(args) {
		BillboardTop20 extractor = new BillboardTop20()
				
		assert extractor.extractorMatches( new URL("http://www.billboardtop20.com") )
		assert !extractor.extractorMatches( new URL("http://google.com/feeds/api/standardfeeds/top_rated?time=today") )
		WebResourceContainer container = extractor.extractItems( new URL("http://www.billboardtop20.com?res=720&codec=264&start=1&for=20"), -1)    
		println container
		
		//ContentURLContainer result = extractor.extractUrl(container.getItems()[0], PreferredQuality.HIGH)
		//print result
		
		container.getItems().each {
            ContentURLContainer result = extractor.extractUrl(it, PreferredQuality.MEDIUM)
            println result
        }

		println "Finished"
	}
}
