import groovy.json.JsonSlurper
import org.serviio.library.metadata.*
import org.serviio.library.online.*
import org.serviio.util.*

/**
 * WebResource extractor plugin for YouTubeTop100.
 * 
 * @author jhb50
 * Version 10 - Aug 5, 2012
 * V1 - start parameter - start=nn
 * V2 - Added res and codec parameters - res=1080,720,480,360  - codec=264,8
 * V3 - Add format code to cachekey to fix transcoding issue
 * V4 - Removed 'live' attribute
 * V5 - Updated to ignore new YT 3D formats, enclose video number in []
 * V6 - Remove 640 option, add daily option - type=daily
 * v7 - Decode HTML characters, add genre option  - type=nnn
 * v8 - Add Number Format Option - nform=xbox
 * v9 - Correct Display of Extended Characters in Song Titles
 * v10 - Added res=240 and Usage comments - Remove 404 test so Serviio 1.0 will not timeout
 *
 * Usage:
 *  http://www.youtubetop100.com
 *   
 *  with optional parameters
 *
 *  for list type type=weekly, daily or nnn 
 *  where nnn is genre number
 *				// 474 Alternative, 455 Blues, 471 Comedy/Spoken, 454 Country, 454 Easy Listening, 466 Electronic, 456 Folk, 
 *				// 462 International, 460 Jazz, 476 K-Pop, 465 Latin, 475 Metal, 458 New Age, 472 Pop, 470 R&B, 451 Rap, 
 * 				// 461 Reggae, 465 Religious, 473 Rock, 463 Stage & Screen, 459 Vocal
 *
 *  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 999
 *    default is 1
 *
 * for xbox [nnn] numbering nform=xbox
 *
 */
class YouTubeTop100 extends WebResourceUrlExtractor {
	
	final VALID_FEED_URL = '^http://www\\.youtubetop100\\.com.*?'
	
	String getExtractorName() {
		return 'YouTubeTop100'
	}
	
	Boolean URLExists(URL fileURL){
		if(((HttpURLConnection) fileURL.openConnection()).getResponseCode() == 404){
			return false
		}
		return true
	}

	boolean extractorMatches(URL feedUrl) {
		return feedUrl ==~ VALID_FEED_URL
	}
	
	WebResourceContainer extractItems(URL resourceUrl, int maxItems) {
	
		log("Extracting with Top100 V10 Test")
	
		List<WebResourceItem> items = []
		def itemsAdded = 0
		String pageTitle = ""
		String pageThumb = ""
		String videoUrl = ""
		String videoTitle = ""
		String thumbUrl = ""
		Short Start = 0 
		Short Res = 0
		Short Codec = 264
		String Type = "weekly"
		String Nform = ""
		
		def parmMatcher = resourceUrl =~ '^http://www\\.youtubetop100\\.com.*?start=([0-9]+)'
		def parmMatch = resourceUrl ==~ '^http://www\\.youtubetop100\\.com.*?start=[0-9]+.*?'
		if (parmMatch){
			Start = parmMatcher[0][1].trim().toShort() - 1
			if(Start < 0 || Start > 99) Start = 0
		}

		parmMatcher = resourceUrl =~ '^http://www\\.youtubetop100\\.com.*?type=([a-z0-9]+)'
		parmMatch = resourceUrl ==~ '^http://www\\.youtubetop100\\.com.*?type=[a-z]+.*?'
		if (parmMatch){
			Type = parmMatcher[0][1].trim()
			if(Type == "daily") Type = "."
			else Type = "weekly"
		}
		else{
			parmMatch = resourceUrl ==~ '^http://www\\.youtubetop100\\.com.*?type=[0-9]+.*?'
			if (parmMatch){
				Type = parmMatcher[0][1].trim()
				// 474 Alternative, 455 Blues, 471 Comedy/Spoken, 454 Country, 454 Easy Listening, 466 Electronic, 456 Folk, 
				// 462 International, 460 Jazz, 476 K-Pop, 465 Latin, 475 Metal, 458 New Age, 472 Pop, 470 R&B, 451 Rap, 
				// 461 Reggae, 465 Religious, 473 Rock, 463 Stage & Screen, 459 Vocal
				String ValidCodes = "451,453,454,455,456,458,459,460,461,462,463,464,465,466,470,471,472,473,474,475,476"	
				if(ValidCodes.contains(Type)) Type = ".." + Type
				else Type = "0"
			}
		}

		parmMatcher = resourceUrl =~ '^http://www\\.youtubetop100\\.com.*?res=([0-9]+)'
		parmMatch = resourceUrl ==~ '^http://www\\.youtubetop100\\.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\\.youtubetop100\\.com.*?codec=([0-9]+)'
		parmMatch = resourceUrl ==~ '^http://www\\.youtubetop100\\.com.*?codec=[0-9]+.*?'
		if (parmMatch){
			Codec = parmMatcher[0][1].trim().toShort() 
			if(Codec != 8 && Codec != 264) Codec = 264
		}
		
		parmMatcher = resourceUrl =~ '^http://www\\.youtubetop100\\.com.*?nform=([a-z]+)'
		parmMatch = resourceUrl ==~ '^http://www\\.youtubetop100\\.com.*?nform=[a-z]+.*?'
		if (parmMatch){
			Nform = parmMatcher[0][1].trim()
			if(Nform != "xbox" ) Nform = ""
		}

		String html 

		if (Type == "weekly"){
			html = new URL("http://www.youtube.com/music").getText()
			def titleMatcher = html =~ '(?s)<div id="weekly-hits".*?list=MCUS.(.+?)"'
			String listDate = titleMatcher[0][1].trim()

			pageTitle = "You Tube Weekly Top Tracks"
			
			html = new URL("http://www.youtube.com/playlist?list=MCUS."+listDate).getText("utf-8")
		}
		else {
			pageTitle = "You Tube Daily Top Tracks"
			
			html = new URL("http://www.youtube.com/playlist?list=MCUS" + Type).getText()
		}

		log("Top 100 items = " + html.count('<li class="playlist-video-item'))

		
	    if (html.count('<li class="playlist-video-item') > 0 ){

	 
			
			def videoMatcher = html =~ '(?s)<li.class="playlist-video-item.*?<a href="(.+?)&amp.*?<img src(.*?)width.*?<span.class="title.video-title.*?>(.*?)<'

			for( int i = Start; i < videoMatcher.size() && (maxItems == -1 || itemsAdded < maxItems); i++ ) {
				videoUrl = "http://www.youtube.com" + videoMatcher[i][1].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][2].trim()
				def thumbMatcher = thumbUrl =~ '(?s)="(.*?)"'
				if(thumbUrl.contains("data-thumb=")) thumbMatcher = thumbUrl =~ '(?s).*?data-thumb="(.*?)"'
				thumbUrl = "http:" + thumbMatcher[0][1]
				
				log("Added item: $videoTitle")

				
				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) {		
		
		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=.*?'
		if (!flashMatch){
			log("NO ENTRIES FOR THIS ITEM - \'$videoTitle\'") 
			return null
		}

		log("Getting item: $videoTitle")
		
		
		String videostart = videohtml.minus(~'(?s).*?url_encoded_fmt_stream_map=.*?')
		String videolinks = videostart.replaceFirst(~'(?s).u0026amp.*?</html>',"")+"%2C"
		
		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 = videolinks =~ '(?s).*?url%3D(.*?)itag%253D(.*?)%25(.*?)%26quality'

		for( int i = 0; i < videoMatcher.size(); i++ ) {
			fmt = videoMatcher[i][2].trim()
			
			linkUrl = videoMatcher[i][1].trim()+"itag%253D"+videoMatcher[i][2].trim()+"%25"+videoMatcher[i][3].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 = URLDecoder.decode(linkUrl)
		linkUrl = URLDecoder.decode(linkUrl)
		linkUrl = URLDecoder.decode(linkUrl)
		linkUrl = linkUrl.replaceAll(",","%2C")

		def cacheKey = videoUrl  + "_" +  fmt
	
		return new ContentURLContainer(fileType: MediaFileType.VIDEO, contentUrl: linkUrl, thumbnailUrl: thumbnailUrl, cacheKey: cacheKey, expiresImmediately: true)
	}
	
	static void main(args) {
		YouTubeTop100 extractor = new YouTubeTop100()
				
		assert extractor.extractorMatches( new URL("http://www.youtubetop100.com") )
		assert !extractor.extractorMatches( new URL("http://google.com/feeds/api/standardfeeds/top_rated?time=today") )
		WebResourceContainer container = extractor.extractItems( new URL("http://www.youtubetop100.com?res=720&codec=264&start=4"), 5)    
		println container
		
		ContentURLContainer result = extractor.extractUrl(container.getItems()[1], PreferredQuality.HIGH)
		print result
	}
}
