#!/usr/bin/env python

from json import loads
import hashlib
import base64
import os
import binascii

try:
    from Crypto.Cipher import AES
    import xml.etree.ElementTree as ET
    has_crypto = True
except ImportError:
    has_crypto = False

from ..common import *
from ..extractor import VideoExtractor
from ..util import log

def netease_hymn():
    return """
    player's Game Over,
    u can abandon.
    u get pissed,
    get pissed,
    Hallelujah my King!
    errr oh! fuck ohhh!!!!
    """

def encrypted_id(dfsId):
    x = [ord(i[0]) for i in netease_hymn().split()]
    y = ''.join([chr(i - 61) if i > 96 else chr(i + 32) for i in x])
    byte1 = bytearray(y, encoding='ascii')
    byte2 = bytearray(str(dfsId), encoding='ascii')
    for i in range(len(byte2)):
        byte2[i] ^= byte1[i % len(byte1)]
    m = hashlib.md5()
    m.update(byte2)
    result = base64.b64encode(m.digest()).decode('ascii')
    result = result.replace('/', '_')
    result = result.replace('+', '-')
    return result

def make_url(songNet, dfsId):
    encId = encrypted_id(dfsId)
    mp3_url = "http://%s/%s/%s.mp3" % (songNet, encId, dfsId)
    return mp3_url

# for http://open.163.com/movie/2014/12/I/9/MAD7EMDVE_MAD7K95I9.html
keys = ["4fxGZqoGmesXqg2o", "3fxVNqoPmesAqg2o"]
def decrypto_video_url(data, whichkey):
    key = keys[whichkey - 1]
    cipher = AES.new(key, mode=AES.MODE_ECB)
    ciphertext = binascii.a2b_hex(data)
    cleartext = cipher.decrypt(ciphertext)
    padding = cleartext[-1]
    cleartext = cleartext[:-padding]
    return cleartext.decode('ascii')

class NetEase(VideoExtractor):
    # test URLs:
    # http://live.ws.126.net/movie/I/9/2_MAD7EMDVE_MAD7K95I9.xml
    # http://live.ws.126.net/movie/V/H/2_MB3M6LDG1_MB3OBKTVH.xml
    name = '网易'

    if has_crypto:
        stream_types = [
            {'id': 'SHD', 'video_profile': '超清'},
            {'id': 'HD',  'video_profile': '高清'},
            {'id': 'SD',  'video_profile': '标清'},
        ]
    else:
        stream_types = [
            {'id': 'default'},
        ]

    def prepare(self, **kwargs):
        # compatibility for _cloud_music_prepare
        self.output_dir = kwargs.get('output_dir')
        self.info_only = kwargs.get('info_only')

        self.subs = []
        self.lyrics = None

        url = self.url
        if "163.fm" in url:
            url = get_location(url)

        if "music.163.com" in url:
            self._cloud_music_prepare(url)
        elif has_crypto:
            self._crypto_prepare(url)
        else:
            log.w('PyCrypto not found, '
                  'high resolution videos may be unavailable.')
            self._legacy_prepare(url)

    def _crypto_prepare(self, url):
        if url.startswith('http://swf.ws.126.net/openplayer/'):
            video_id = url.split('-')[2]
            assert video_id.startswith('2_')
            video_id = video_id[2:]
        else:
            # http://open.163.com/movie/2015/10/V/H/MB3M6LDG1_MB3OBKTVH.html
            video_id = url.split('/')[-1].split('.')[0]
        xml = self._get_xml_for_video_id(video_id)
        encrypt_key = int(xml.find('encrypt').text)
        playurl = xml.find('playurl_origin')
        if len(playurl) == 0:
            playurl = xml.find('playurl')
        streams = {}
        for stream in self.stream_types:
            e = playurl.find('./%s/mp4' % stream['id'])
            if e is not None:
                url = decrypto_video_url(e.text, encrypt_key)
                streams[stream['id']] = {
                    'url': url,
                    'video_profile': stream['video_profile'],
                    'size': url_size(url),
                }
        self.streams = streams

        for sub in xml.findall('subs/*'):
            name = sub.find('name').text
            url = sub.find('url').text
            self.subs.append((name, url))

    def _legacy_prepare(self, url):
        if url.startswith('http://swf.ws.126.net/openplayer/'):
            video_id = url.split('-')[2]
            assert video_id.startswith('2_')
            video_id = video_id[2:]
            xml = self._get_xml_for_video_id(video_id)
            url = xml.find('pageUrl').text

        html = get_decoded_html(url)
        title = match1(html, "movieDescription='([^']+)'") or \
                match1(html, '<title>(.+)</title>')
        self.title = title.strip()

        src = match1(html, r'<source src="([^"]+)"') or \
                match1(html, r'<source type="[^"]+" src="([^"]+)"')
        if src:
            url = src
        else:
            url = (match1(html, r'["\'](.+)-list.m3u8["\']') or
                   match1(html, r'["\'](.+).m3u8["\']')) + ".mp4"

        self.streams['default'] = {
            'url': url,
        }

    def _cloud_music_prepare(self, url):
        rid = match1(url, r'id=(.*)')
        output_dir = self.output_dir
        info_only = self.info_only

        if rid is None:
            rid = match1(url, r'/(\d+)/?$')
        if "album" in url:
            # FIXME: only last is processed
            j = loads(get_content("http://music.163.com/api/album/%s?id=%s&csrf_token=" % (rid, rid), headers={"Referer": "http://music.163.com/"}))

            artist_name = j['album']['artists'][0]['name']
            album_name = j['album']['name']
            new_dir = output_dir + '/' + "%s - %s" % (artist_name, album_name)
            if not os.path.exists(new_dir):
                os.mkdir(new_dir)
            if not info_only:
                cover_url = j['album']['picUrl']
                download_urls([cover_url], "cover", "jpg", 0, new_dir)

            for i in j['album']['songs']:
                self._song_prepare(i)
                try: # download lyrics
                    assert kwargs['caption']
                    l = loads(get_content("http://music.163.com/api/song/lyric/?id=%s&lv=-1&csrf_token=" % i['id'], headers={"Referer": "http://music.163.com/"}))
                    self._lyrics_prepare(i, l["lrc"]["lyric"])
                except: pass

        elif "playlist" in url:
            # FIXME: only last is processed
            j = loads(get_content("http://music.163.com/api/playlist/detail?id=%s&csrf_token=" % rid, headers={"Referer": "http://music.163.com/"}))

            new_dir = output_dir + '/' + j['result']['name']
            if not os.path.exists(new_dir):
                os.mkdir(new_dir)
            if not info_only:
                cover_url = j['result']['coverImgUrl']
                download_urls([cover_url], "cover", "jpg", 0, new_dir)

            for i in j['result']['tracks']:
                self._song_prepare(i)
                try: # download lyrics
                    assert kwargs['caption']
                    l = loads(get_content("http://music.163.com/api/song/lyric/?id=%s&lv=-1&csrf_token=" % i['id'], headers={"Referer": "http://music.163.com/"}))
                    self._lyrics_prepare(i, l["lrc"]["lyric"])
                except: pass

        elif "song" in url:
            j = loads(get_content("http://music.163.com/api/song/detail/?id=%s&ids=[%s]&csrf_token=" % (rid, rid), headers={"Referer": "http://music.163.com/"}))
            self._song_prepare(j["songs"][0])
            try: # download lyrics
                l = loads(get_content("http://music.163.com/api/song/lyric/?id=%s&lv=-1&csrf_token=" % rid, headers={"Referer": "http://music.163.com/"}))
                self._lyrics_prepare(j["songs"][0], l["lrc"]["lyric"])
            except:
                pass

        elif "mv" in url:
            j = loads(get_content("http://music.163.com/api/mv/detail/?id=%s&ids=[%s]&csrf_token=" % (rid, rid), headers={"Referer": "http://music.163.com/"}))
            self._video_prepare(j['data'])

    def _song_prepare(self, song):
        # test URL: http://music.163.com/#/song?id=29043459
        self.title = "%s. %s" % (song['position'], song['name'])
        songNet = 'p' + song['mp3Url'].split('/')[2][1:]

        s = self.streams
        if 'hMusic' in song and song['hMusic'] is not None:
            s['hMusic'] = {'url': make_url(songNet, song['hMusic']['dfsId'])}
        if 'mp3Url' in song:
            s['mp3Url'] = {'url': song['mp3Url']}
        if 'bMusic' in song:
            s['bMusic'] = {'url': make_url(songNet, song['bMusic']['dfsId'])}
        self.stream_types = [
            {'id': x} for x in ['hMusic', 'mp3Url', 'bMusic']
        ]

    def _video_prepare(self, vinfo):
        # test URL: http://music.163.com/#/mv/343100/
        self.title = "%s - %s" % (vinfo['name'], vinfo['artistName'])
        s = self.streams
        for bitrate, url in vinfo['brs'].items():
            s[bitrate] = {'url': url}
        self.stream_types = [
            {'id': x} for x in sorted(s, key=int, reverse=True)
        ]

    def _lyrics_prepare(self, song, lyrics):
        # test URL: http://music.163.com/#/song?id=29043459
        title = "%s. %s" % (song['position'], song['name'])
        filename = '%s.lrc' % get_filename(title)
        self.plain_files.append({
            'filename': filename,
            'content': lyrics,
        })

    def _get_xml_for_video_id(self, vid):
        xml_url = 'http://live.ws.126.net/movie/%s/%s/2_%s.xml' % (
            vid[-2], vid[-1], vid)
        xml = get_content(xml_url)
        e = ET.fromstring(xml)
        self.title = e.find('title').text
        return e

    def extract(self, **kwargs):
        for i in self.streams:
            s = self.streams[i]
            _, s['container'], s['size'] = url_info(s['url'])
            s['src'] = [s['url']]

        for name, url in self.subs:
            self.caption_tracks[name] = get_content(url)

site = NetEase()
download = site.download_by_url
