import groovy.json.JsonSlurper

import groovy.json.JsonSlurper

import org.serviio.library.metadata.*
import org.serviio.library.online.*
import org.serviio.util.*

/**
 * WebResource extractor plugin for tunein.com.
 * 
 * @author Petr Nejedly
 * Version3 20/03/2013 Jerome Avenel: modified stationMatcher following modification of TuneIn website
 *
 */
class TuneIn extends WebResourceUrlExtractor {
    
    final VALID_FEED_URL = '^(?:https?://)?(?:www\\.)?tunein\\.com/radio/.+'
    
    String getExtractorName() {
        return 'Tune In'
    }
    
    boolean extractorMatches(URL feedUrl) {
        return feedUrl ==~ VALID_FEED_URL
    }
    
    int getVersion() {
        3
    }

    WebResourceContainer extractItems(URL resourceUrl, int maxItems) {
        String html = resourceUrl.getText()
        
        def titleMatcher = html =~ '(?s)<h1>(.+?)<'
        String pageTitle = titleMatcher[0][1].trim()
        
        List<WebResourceItem> items = []
        
        def stationMatcher = html =~ '(?s)<li class="clearfix play (?:odd)?">\\s*<a href="([a-zA-Z0-9-/\\(\\)]+)".*?title="(.*?)"'
        def itemsAdded = 0;
        for( int i = 0; i < stationMatcher.size() && (maxItems == -1 || itemsAdded < maxItems); i++ ) {
            String stationUrl = stationMatcher[i][1].trim()
            String stationTitle = stationMatcher[i][2].trim()            
            WebResourceItem item = new WebResourceItem(title: stationTitle, additionalInfo: ['stationUrl':stationUrl])
            items << item
            itemsAdded++            
        }
            
        return new WebResourceContainer(title: pageTitle, items: items)
    }
    
    ContentURLContainer extractUrl(WebResourceItem item, PreferredQuality requestedQuality) {        
        String stationUrl = item.getAdditionalInfo()['stationUrl']
        assert stationUrl != null
        
        String playerHtml = new URL("http://tunein.com$stationUrl").getText()
        
        def jsonMatcher = playerHtml =~ '(?s)RadioTime.tuneData =.*?(\\{.+?)RadioTime\\.'
        String jsonCode = jsonMatcher[0][1].trim()
        
        def json = new JsonSlurper().parseText(jsonCode)
        Map jsonResult = (Map) json;
        // thubmnail
        String thumbnailUrl = jsonResult['Logo']
        // streams, get required quality
        String streamUrl = getStreamUrl(jsonResult)
        if(streamUrl == null) {
            // try to load external stream URLs
            String extStreamsUrl = jsonResult['StreamUrl']
            if(extStreamsUrl != null) {
                String streamsDef = new URL(extStreamsUrl).getText()
                def jsonMatcher2 = streamsDef =~ '(?s)(\\{.*?)\\);'
                String jsonCode2 = jsonMatcher2[0][1].trim()
                def json2 = new JsonSlurper().parseText(jsonCode2)
                Map jsonResult2 = (Map) json2;
                streamUrl = getStreamUrl(jsonResult2)
            }
            if(streamUrl == null) {
                log('No suitable streams found')
                return null
            }
        }
        return new ContentURLContainer(fileType: MediaFileType.AUDIO, contentUrl: streamUrl, thumbnailUrl: thumbnailUrl, live: true)
    }
    
    private String getStreamUrl(Map jsonResult) {
        List streams = jsonResult['Streams']
        // only get streams that are represented as playlists and are live streams
        streams = streams.findAll { it -> it['Type'] == 'Live' && ['MP3','Windows','AAC'].contains(it['MediaType']) && it['Url'].indexOf('adType') == -1 }
        if( streams.size() > 0 ) {
            // ignore quality, audio bitrate is too low to make any difference, deliver the best available
            streams = streams.sort { it -> it['Bandwidth'].toInteger() }
            Map selectedStream = streams.last()
            String streamUrl = selectedStream['Url']
            boolean hasPlaylist = Boolean.valueOf (selectedStream['HasPlaylist'])
            if(hasPlaylist) {
                return getUrlFromPlaylist(new URL(streamUrl))
            } else {
                return streamUrl
            }
        } else {
            return null
        }
    }
    
    /**
     * Supports m3u playlists ATM
     */
    protected getUrlFromPlaylist(URL playlistUrl) {
        assert playlistUrl != null
        
        String playlist = playlistUrl.getText()
        if(playlist.toLowerCase().startsWith("<asx")) {
            // asx playlists not supported
            return null            
        } else {
            // assume m3u playlist
            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
        }
    }
    
    static void main(args) {
        // this is just to test
        TuneIn extractor = new TuneIn()
                
        assert extractor.extractorMatches( new URL("http://tunein.com/radio/France-r101240/?qlc=1") )
        assert !extractor.extractorMatches( new URL("http://google.com/feeds/api/standardfeeds/top_rated?time=today") )
        
        WebResourceContainer container = extractor.extractItems( new URL("http://tunein.com/radio/France-r101240/?qlc=1"), -1) // with embedded streams
        //WebResourceContainer container = extractor.extractItems( new URL("http://tunein.com/radio/search/05301/"), -1) // with external streams
        
        println container
        
        ContentURLContainer result = extractor.extractUrl(container.getItems()[14], PreferredQuality.MEDIUM)
        print result
        
        //println extractor.getUrlFromPlaylist (new URL("http://stream.radiotime.com/listen.stream?streamId=1045956&rti=dihyG20zJlYQXB09HBdHXE8gQV4QHBwvSwUAV11OJg0ZThZ7ZFBYDFEDJn8QRlU0CwdPUFNDRG5FEFF0Ek5dEFYSXXRqGAVEBUUEUlhRYQ0%3d%7e%7e%7e"))
    }
}