using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

//为方便移植,本软件采用单文件独立版
//支持平台:Windows,Linux,MacOs
namespace KgHelper_定制版
{
    /// <summary>
    /// 程序入口类,包含主方法与帮助类
    /// </summary>
    public static class Program
    {
        #region 静态属性
        /// <summary>
        /// 歌手列表
        /// </summary>
        public static List<Singer> lstSinger { get; set; }
        /// <summary>
        /// 歌曲列表
        /// </summary>
        public static List<Song> lstSong { get; set; }
        /// <summary>
        /// 延迟
        /// </summary>
        public static int Delay { get; set; }
        /// <summary>
        /// 文件路径
        /// </summary>
        public static string Path { get; set; }
        /// <summary>
        /// 现行时间
        /// </summary>
        public static DateTime Now { get { return DateTime.Now; } }
        /// <summary>
        /// 现行时间戳
        /// </summary>
        public static int NowStamp { get { return TimeToStamp(Now); } }
        /// <summary>
        /// 暂停
        /// </summary>
        public static bool IsPause { get; set; }
        /// <summary>
        /// 刷量线程
        /// </summary>
        public static List<Thread> BrushThreads { get; set; }
        #endregion
        /// <summary>
        /// 入口方法
        /// </summary>
        /// <param name="args">文件 方法 延迟</param>
        static void Main(string[] args)
        {
            Console.Title = "遗憾迟香全民K歌助手V3.0";
            Console.WriteLine("运行地址:\n{0}", Environment.CurrentDirectory);

            lstSinger = Singer.FromFile("singer.txt");
            foreach (Singer item in lstSinger)
            {
                item.GetAll();
                foreach (Song j in item.SongList)
                {
                    Execute(j.SQL);
                }
            }





            try
            {
                switch (args.Length)
                {
                    #region 0个参数
                    case 0:
                        Console.WriteLine("请输入文件路径(包含后缀名):");
                        Path = Console.ReadLine();
                        if (IsSingerList(Path))
                        {
                            Console.WriteLine("这是一个歌手列表,请选择操作:\n1:全自动下载(防秒删)\n2:下载全部歌手全部作品\n3:导出全部歌手全部作品");
                            ConsoleKey key = Console.ReadKey().Key;
                            switch (key)
                            {
                                case ConsoleKey.D1:
                                case ConsoleKey.NumPad1:
                                    Console.WriteLine("请输入延迟(毫秒):");
                                    Delay = int.Parse(Console.ReadLine());
                                    AutoDown();
                                    break;
                                case ConsoleKey.D2:
                                case ConsoleKey.NumPad2:
                                    DownAllSinger();
                                    break;
                                case ConsoleKey.D3:
                                case ConsoleKey.NumPad3:
                                    ExportAllSong();
                                    break;
                                default:
                                    break;
                            }
                        }
                        else
                        {
                            Console.WriteLine("这是一个歌曲列表,请选择操作:\n1:刷播放量\n2:下载全部全部作品\n3:刷播放量(极限模式(多线程,零延迟))");
                            ConsoleKey key = Console.ReadKey().Key;
                            switch (key)
                            {
                                case ConsoleKey.D1:
                                case ConsoleKey.NumPad1:
                                    Console.WriteLine("请输入延迟(毫秒):");
                                    Delay = int.Parse(Console.ReadLine());
                                    BrushAll();
                                    break;
                                case ConsoleKey.D2:
                                case ConsoleKey.NumPad2:
                                    DownAllSong();
                                    break;
                                case ConsoleKey.D3:
                                case ConsoleKey.NumPad3:
                                    Console.WriteLine();
                                    Console.WriteLine("请输入线程数");
                                    Delay = int.Parse(Console.ReadLine());
                                    CrazyBrush(Delay);
                                    break;
                                default:
                                    break;
                            }
                        }
                        break;
                    #endregion
                    #region 1个参数
                    case 1:
                        Path = args[0];
                        if (IsSingerList(Path))
                        {
                            Console.WriteLine("这是一个歌手列表,请选择操作:\n1:全自动下载(防秒删)\n2:下载全部歌手全部作品\n3:导出全部歌手全部作品");
                            ConsoleKey key = Console.ReadKey().Key;
                            switch (key)
                            {
                                case ConsoleKey.D1:
                                case ConsoleKey.NumPad1:
                                    Console.WriteLine("请输入延迟(毫秒):");
                                    Delay = int.Parse(Console.ReadLine());
                                    AutoDown();
                                    break;
                                case ConsoleKey.D2:
                                case ConsoleKey.NumPad2:
                                    DownAllSinger();
                                    break;
                                case ConsoleKey.D3:
                                case ConsoleKey.NumPad3:
                                    ExportAllSong();
                                    break;
                                default:
                                    break;
                            }
                        }
                        else
                        {
                            Console.WriteLine("这是一个歌曲列表,请选择操作:\n1:刷播放量\n2:下载全部全部作品\n3:刷播放量(极限模式(多线程,零延迟))");
                            ConsoleKey key = Console.ReadKey().Key;
                            switch (key)
                            {
                                case ConsoleKey.D1:
                                case ConsoleKey.NumPad1:
                                    Console.WriteLine("请输入延迟(毫秒):");
                                    Delay = int.Parse(Console.ReadLine());
                                    BrushAll();
                                    break;
                                case ConsoleKey.D2:
                                case ConsoleKey.NumPad2:
                                    DownAllSong();
                                    break;
                                case ConsoleKey.D3:
                                case ConsoleKey.NumPad3:
                                    Console.WriteLine();
                                    Console.WriteLine("请输入线程数");
                                    Delay = int.Parse(Console.ReadLine());
                                    CrazyBrush(Delay);
                                    break;
                                default:
                                    break;
                            }
                        }
                        break;

                    #endregion
                    #region 2个参数
                    case 2:
                        Path = args[0];
                        if (IsSingerList(Path))
                        {
                            switch (int.Parse(args[1]))
                            {
                                case 1:
                                    Console.WriteLine("请输入延迟(毫秒):");
                                    Delay = int.Parse(Console.ReadLine());
                                    AutoDown();
                                    break;
                                case 2:
                                    DownAllSinger();
                                    break;
                                case 3:
                                    ExportAllSong();
                                    break;
                                default:
                                    break;
                            }
                        }
                        else
                        {
                            switch (int.Parse(args[1]))
                            {
                                case 1:
                                    Console.WriteLine("请输入延迟(毫秒):");
                                    Delay = int.Parse(Console.ReadLine());
                                    BrushAll();
                                    break;
                                case 2:
                                    DownAllSong();
                                    break;
                                case 3:
                                    Console.WriteLine();
                                    Console.WriteLine("请输入线程数");
                                    Delay = int.Parse(Console.ReadLine());
                                    CrazyBrush(Delay);
                                    break;
                                default:
                                    break;
                            }
                        }
                        break;
                    #endregion
                    #region 3个参数
                    case 3:
                        Path = args[0];
                        Delay = int.Parse(args[2]);
                        if (IsSingerList(Path))
                        {
                            switch (int.Parse(args[1]))
                            {
                                case 1:
                                    AutoDown();
                                    break;
                                case 2:
                                    DownAllSinger();
                                    break;
                                case 3:
                                    ExportAllSong();
                                    break;
                                default:
                                    break;
                            }
                        }
                        else
                        {
                            switch (int.Parse(args[1]))
                            {
                                case 1:
                                    BrushAll();
                                    break;
                                case 2:
                                    DownAllSong();
                                    break;
                                case 3:

                                    CrazyBrush(Delay);
                                    break;
                                default:
                                    break;
                            }
                        }
                        break;
                    #endregion
                    default:
                        foreach (var item in args)
                        {
                            Console.WriteLine(item);
                        }
                        throw new ArgumentException("参数错误");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                Console.WriteLine("按任意键退出...");
                Console.ReadKey();
            }
        }

        #region 功能
        /// <summary>
        /// 清理内存
        /// </summary>
        public static void Collect()
        {
            foreach (Singer item in lstSinger)
            {
                item.Src = null;
            }
            GC.Collect();
        }
        /// <summary>
        /// 全自动下载
        /// </summary>
        private static void AutoDown()
        {
            lstSinger = Singer.FromFile(Path);
            int cnt = 1;
            while (true)
            {
                Console.Title = "休息中";
                Thread.Sleep(Delay);
                DateTime before = Now;
                for (int i = 0; i < lstSinger.Count; i++)
                {
                    Singer s = lstSinger[i];
                    Console.Title = string.Format("第{0}次监控\t{1}\t{2}/{3}", cnt, s.Name, i + 1, lstSinger.Count);
                    s.Monit();
                }
                cnt++;
                double second = (Now - before).TotalSeconds;
                Console.WriteLine("耗时{0:f2}秒,平均每秒{1:f2}个歌手", second, lstSinger.Count / second);
                GC.Collect();
            }
        }
        /// <summary>
        /// 下载全部歌手全部作品
        /// </summary>
        private static void DownAllSinger()
        {
            lstSinger = Singer.FromFile(Path);
            DateTime before = Now;
            int cnt = 0;
            foreach (Singer i in lstSinger)
            {
                i.GetAll();
                i.DownloadAll();
                cnt += i.Count;
            }
            double min = (Now - before).TotalMinutes;
            Console.WriteLine("下载完成,一共下载了{0}首作品,耗时{1:f2}分", cnt, min);
        }
        /// <summary>
        /// 导出全部歌手全部作品
        /// </summary>
        private static void ExportAllSong()
        {
            lstSinger = Singer.FromFile(Path);
            DateTime before = Now;
            int cnt = 0;
            foreach (Singer i in lstSinger)
            {
                i.GetAll();
                foreach (Song j in i.SongList)
                {
                    Console.WriteLine("正在导出{0}", j);
                    File.AppendAllText("song.txt", j.Export + "\r\n", Encoding.Default);
                }
                cnt += i.Count;
            }
            double min = (Now - before).TotalMinutes;
            Console.WriteLine("已导出至song.txt,一共导出了{0}首作品,耗时{1:f2}分", cnt, min);
        }
        /// <summary>
        /// 下载全部作品
        /// </summary>
        private static void DownAllSong()
        {
            lstSong = Song.FromFile(Path);
            DateTime before = Now;
            foreach (Song i in lstSong)
            {
                i.Download();
            }
            Song.Retry();
            double min = (Now - before).TotalMinutes;
            Console.WriteLine("一共下载了{0}首作品,耗时{1:f2}分", lstSong.Count, min);
        }
        /// <summary>
        /// 刷播放量
        /// </summary>
        private static void BrushAll()
        {
            lstSong = Song.FromFile(Path);
            for (int i = 1; true; i++)
            {
                DateTime before = Now;
                for (int j = 0; j < lstSong.Count; j++)
                {
                    try
                    {
                        Song s = lstSong[j];
                        Console.Write("第{0}次刷量 正在刷:{1} {2}/{3} ", i, s, j + 1, lstSong.Count);
                        Console.Title = string.Format("第{0}次刷量 正在刷:{1} {2}/{3} ", i, s, j + 1, lstSong.Count);
                        if (!s.Brush())
                        {//刷到不能刷为止
                            lstSong.Remove(s);
                            Console.WriteLine("失败");
                        }
                        else
                        {
                            Console.WriteLine(s.Count);
                        }
                        Console.Title = "休息中";
                        Thread.Sleep(Delay);
                    }
                    catch (Exception ex)
                    { Console.WriteLine(ex.Message); }
                }
                double min = (Now - before).TotalMinutes;
                Console.WriteLine("一共刷了{0}首作品,耗时{1:f2}分", lstSong.Count, min);
                if (lstSong.Count == 0)
                    break;
            }
            Console.Title = "已结束,请更换网络或稍后再来";
            Console.WriteLine("已结束,请更换网络或稍后再来");
        }
        /// <summary>
        /// 极限模式
        /// </summary>
        /// <param name="thread">线程数</param>
        private static void CrazyBrush(int thread)
        {
            BrushThreads = new List<Thread>();
            Console.WriteLine("警告!您正在使用极限模式(多线程,零延迟)\n很容易被封ip\n请在在使用代理或短暂蹭网时使用\n刷量失败将自动暂停\n标题为每秒请求的次数,并不代表实际播放量增加速度");
            for (int i = 3; i > 0; i--)
            {
                Console.WriteLine("{0}秒后自动开始", i);
                Thread.Sleep(1000);
            }
            lstSong = Song.FromFile(Path);
            Console.Title = "正在启动线程";
            for (int i = 0; i < thread; i++)
            {
                Thread t = new Thread(CrazyBrush);
                t.Start(i);
                BrushThreads.Add(t);
            }
            Thread check = new Thread(Check);
            check.IsBackground = true;
            check.Start();
            Console.Title = "已开始";
            while (true)
            {
                int c1 = Song.BrushCount;
                Thread.Sleep(1000);
                int c2 = Song.BrushCount;
                Console.Title = string.Format("{0}/S", c2 - c1);
            }
        }

        private static void CrazyBrush(object o)
        {
            while (true)
            {
                foreach (Song i in lstSong)
                {
                    if (IsPause) { Thread.Sleep(100); }
                    else
                    {
                        Console.WriteLine("线程{0}\t正在刷{1} - {2},本首歌已刷了{3}次", (int)o + 1, i.SingerName, i.Title, i.BrushedCount);
                        i.CrazyBrush();
                    }
                }
            }
        }

        private static void Check()
        {
            while (true)
            {
                try
                {
                    Random rd = new Random();
                    Song s = lstSong[rd.Next(0, lstSong.Count)];
                    Console.WriteLine("正在抽检播放量:{0}", s);
                    s.Getdata();
                    int c1 = s.Count;
                    Program.Get(Song.CGI[0] + s.Shareid + Song.CGI[1] + Program.TimeToStamp(DateTime.Now) + Song.CGI[2]);
                    Thread.Sleep(3000);
                    s.Getdata();
                    int c2 = s.Count;
                    if (c2 <= c1)
                    {
                        Console.ForegroundColor = ConsoleColor.Red;
                        IsPause = true;
                        Console.WriteLine("刷量失败,线程已暂停,请更换网络,将在下次抽检时自动继续");
                        Beep();
                    }
                    else
                    {
                        Console.ForegroundColor = ConsoleColor.White;
                        IsPause = false;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    Console.ForegroundColor = ConsoleColor.Red;
                    IsPause = true;
                    Console.WriteLine("程序出现异常刷量失败,线程已暂停,请更换网络,将在下次抽检时自动继续");
                    Beep();
                }
            }
        }
        #endregion

        #region CommonHelper
        /// <summary>
        /// 无查询
        /// </summary>
        /// <param name="sql">SQL语句</param>
        /// <param name="pams">参数</param>
        /// <returns></returns>
        public static bool Execute(string sql, params SqlParameter[] pams)
        {
            SqlConnection conn = new SqlConnection("server=59.110.171.225;database=MusicHelper;uid=sa;pwd=ZUTzut267832");
            try
            {
                conn.Open();
                SqlCommand cmd = new SqlCommand(sql, conn);
                foreach (var item in pams)
                {
                    cmd.Parameters.Add(item);
                }
                return cmd.ExecuteNonQuery() > 0;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
            finally
            {
                if (conn.State == ConnectionState.Open)
                { conn.Close(); }
            }
        }
        /// <summary>
        /// 数组最大值
        /// </summary>
        /// <param name="nums"></param>
        /// <returns></returns>
        public static int ArrayMax(int[] nums)
        {
            int max = int.MinValue;
            for (int i = 0; i < nums.Length; i++)
            {
                if (nums[i] > max)
                {
                    max = nums[i];
                }
            }
            return max;
        }
        /// <summary>
        /// 数组最小值
        /// </summary>
        /// <param name="nums"></param>
        /// <returns></returns>
        public static int ArrayMin(int[] nums)
        {
            int min = int.MaxValue;
            for (int i = 0; i < nums.Length; i++)
            {
                if (nums[i] < min)
                {
                    min = nums[i];
                }
            }
            return min;
        }

        /// <summary>
        /// 警报
        /// </summary>
        public static void Beep()
        {
            for (int i = 0; i < 3; i++)
            {
                Console.Beep();
                Thread.Sleep(50);
            }
        }
        /// <summary>
        /// 检测文本为歌手列表还是歌曲列表
        /// </summary>
        /// <param name="path">路径</param>
        /// <returns></returns>
        public static bool IsSingerList(string path)
        {
            Regex rg = new Regex("6.{1}9.{1}9.{1}8.{1}2.{1}2.{1}3.{1}8");
            MatchCollection matches = rg.Matches(File.ReadAllText(path));
            if (matches.Count > 0)
            { return true; }
            return false;
        }
        class HttpClient : WebClient
        {
            public void GetFile(string path, string url)
            {
                if (url == string.Empty)
                {
                    throw new WebException("下载地址为空");
                }
                base.Headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36 OPR/48.0.2685.52";
                DownloadFile(url, path);
            }
        }

        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="path">目录</param>
        /// <param name="url">下载地址</param>
        public static void Down(string path, string url)
        {
            try
            {
                ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
                new HttpClient().GetFile(path, url);
            }
            catch (Exception ex)
            {
                WriteTxt("void Down(string,string)发生了异常:" + ex.Message, true);
                throw new Exception("void Down(string,string)发生了异常:" + ex.Message);
            }
        }

        /// <summary>
        /// 取出中间字符串
        /// </summary>
        /// <param name="str">源字符串</param>
        /// <param name="left">左</param>
        /// <param name="right">右</param>
        /// <returns>成功返回结果,失败返回空</returns>
        public static string GetMiddleText(string str, string left, string right)
        {
            if (str == null || left == null || right == null) { return ""; }
            if (str == "" || left == "" || right == "") { return ""; }
            int a = str.IndexOf(left, 0);
            int b = str.IndexOf(right, a + 1);
            if (a < 0 || b < 0)
            { return ""; }
            else
            {
                a = a + left.Length;
                b = b - a;
                if (a < 0 || b < 0)
                { return ""; }
                return str.Substring(a, b);
            }
        }
        /// <summary>
        /// 取出右边字符串
        /// </summary>
        /// <param name="str">源字符串</param>
        /// <param name="left">左</param>
        /// <returns>成功返回结果,失败返回空</returns>
        public static string GetRightText(string str, string left)
        {
            if (str == "" || left == "") { return ""; }
            int a = str.IndexOf(left);
            if (a < 0)
            { return ""; }
            return str.Substring(a + left.Length);
        }
        /// <summary>
        /// 批量取出中间字符串
        /// </summary>
        /// <param name="source">源字符串</param>
        /// <param name="startStr">开始字符串</param>
        /// <param name="endStr">结束字符串</param>
        /// <returns>中间字符串</returns>
        public static string[] GetMiddleTextMultiple(string str, string left, string right)
        {
            if (str == null || left == null || right == null) { return null; }
            if (str == "" || left == "" || right == "") { return null; }
            Regex rg = new Regex("(?<=(" + left + "))[.\\s\\S]*?(?=(" + right + "))", RegexOptions.Multiline | RegexOptions.Singleline);
            MatchCollection matches = rg.Matches(str);
            List<string> resList = new List<string>();
            foreach (Match item in matches)
            { resList.Add(item.Value); }
            return resList.ToArray();
        }
        /// <summary>
        /// 字符串转整数
        /// </summary>
        /// <param name="str">字符串</param>
        /// <param name="str">默认值</param>
        /// <returns>成功返回结果,失败返回默认值</returns>
        public static int TriParse(string str, int defultValue)
        {
            try { return int.Parse(str); }
            catch (Exception)
            { return defultValue; }
        }
        /// <summary>  
        /// 时间转时间戳
        /// </summary>  
        /// <param name="time">时间</param>  
        /// <returns></returns>  
        public static int TimeToStamp(DateTime time)
        {
            DateTime startTime = new DateTime(1970, 1, 1, 8, 0, 0, 0);
            return (int)(time - startTime).TotalSeconds;
        }
        /// <summary>        
        /// 时间戳转时间        
        /// </summary>        
        /// <param name=”timeStamp”></param>        
        /// <returns></returns>        
        public static DateTime StampToTime(int stamp)
        {
            DateTime dtStart = new DateTime(1970, 1, 1, 8, 0, 0, 0);
            return dtStart.AddSeconds(stamp);
        }

        /// <summary>
        /// string[]转int[]
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static int[] StringArrayToInt(string[] str)
        {
            try
            {
                List<int> a = new List<int>();
                foreach (string i in str)
                { a.Add(TriParse(i, 0)); }
                return a.ToArray();
            }
            catch (Exception)
            {
                int[] a = { 0 };
                return a;
            }
        }
        /// <summary>
        /// 输出日志
        /// </summary>
        /// <param name="message">消息</param>
        /// <param name="showTime">是否输出时间</param>
        public static void WriteTxt(string message, bool showTime)
        {
            try
            {
                if (showTime)
                { File.AppendAllText("log.txt", string.Format("{0}{1}\n", DateTime.Now, message), Encoding.Default); }
                else { File.AppendAllText("log.txt", message + "\n", Encoding.Default); }
            }
            catch (Exception ex)
            { Console.WriteLine("{0}\n写入文件失败,请检查文件是否被占用或无权访问", ex.Message); }
        }
        /// <summary>
        /// 删除文件名非法字符
        /// </summary>
        public static string RemoveInvalidFile(string fileName)
        {
            string name = fileName;
            name = name.Replace("&nbsp;", " ");
            name = name.Replace("\\", "");
            name = name.Replace("/", "");
            name = name.Replace("\"", "");
            name = name.Replace(":", "");
            name = name.Replace("?", "");
            name = name.Replace("*", "");
            name = name.Replace("<", "");
            name = name.Replace(">", "");
            name = name.Replace("|", "");
            name = name.Replace("\n", "");
            name = name.Replace("\t", "");
            name = name.Replace("\r", "");
            return name;
        }
        /// <summary>
        /// 解决Error"基础连接已经关闭: 未能为SSL/TLS 安全通道建立信任关系。"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="certificate"></param>
        /// <param name="chain"></param>
        /// <param name="sslPolicyErrors"></param>
        /// <returns></returns>
        private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            return true;
        }

        /// <summary>
        /// Get请求
        /// </summary>
        /// <param name="url">url</param>
        /// <returns></returns>
        public static string Get(string url, int timeout = 600000)
        {
            ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
            string message = string.Empty;
            string[] urls = { url.Replace("https://", "http://"), url.Replace("http://", "https://") };
            foreach (string item in urls)
            {
                try
                {
                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(item);
                    req.Accept = "text/html, application/xhtml+xml, */*";
                    req.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)";
                    req.KeepAlive = true;
                    req.ProtocolVersion = HttpVersion.Version11;
                    req.Timeout = timeout;
                    ServicePointManager.Expect100Continue = false;
                    HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
                    Stream strm = resp.GetResponseStream();
                    StreamReader sr = new StreamReader(strm, Encoding.UTF8);
                    message = sr.ReadToEnd();
                    sr.Close();
                    strm.Close();
                    Song.BrushCount++;
                    break;
                }
                catch (Exception ex)
                {
                    message = ex.Message;
                }
            }
            return message;
        }
        #endregion
    }

    /// <summary>
    /// 歌手类
    /// </summary>
    [Serializable]
    public class Singer
    {
        #region 静态字段
        public static int count = 1;
        public static string[] CGI = { "https://cgi.kg.qq.com/fcgi-bin/kg_ugc_get_homepage?jsonpCallback=callback_0&inCharset=GB2312&outCharset=utf-8&g_tk=5381&g_tk_openkey=5381&type=get_uinfo&start=", "&num=8&touin=&share_uid=" };
        public const string cgi = "https://cgi.kg.qq.com/fcgi-bin/kg_ugc_get_homepage?jsonpCallback=callback_0&inCharset=GB2312&outCharset=utf-8&g_tk=5381&g_tk_openkey=5381&type=get_uinfo&start=1&num=8&touin=&share_uid=";
        public const string URL = "https://node.kg.qq.com/personal?uid=";
        #endregion
        #region 属性集
        /// <summary>
        /// 歌手Uid
        /// </summary>
        public string Uid { get; set; }
        /// <summary>
        /// 歌手名
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 最新作品发布时间戳
        /// </summary>
        public int MaxTimeStamp { get; set; }
        /// <summary>
        /// 最新作品发布时间戳
        /// </summary>
        public DateTime MaxTime
        { get { return Program.StampToTime(MaxTimeStamp); } }
        /// <summary>
        /// 歌曲列表
        /// </summary>
        public List<Song> SongList
        { get; set; }
        /// <summary>
        /// 网页源码
        /// </summary>
        public string Src { get; set; }
        /// <summary>
        /// 网页源码累加
        /// </summary>
        public string SrcPP { get; set; }
        /// <summary>
        /// 导出格式
        /// </summary>
        public string Export
        { get { return string.Format("{0}:{1}", Uid, Name); } }

        /// <summary>
        /// 歌曲数量
        /// </summary>
        public int Count { get { return SongList.Count; } }
        #endregion
        #region 构造函数
        /// <summary>
        /// Singer有参构造
        /// </summary>
        /// <param name="uid">uid</param>
        public Singer(string uid)
        {
            Console.Write("正在获取歌手信息:");
            Uid = uid;
            Src = Program.Get(cgi + Uid);
            if (Src.Contains("参数错误") || !Src.Contains("ok"))
            {
                throw new Exception(string.Format("{0}\nuid:{1}\n请检查网络连接以及输入的uid是否正确", Src, Uid));
            }
            Name = Program.GetMiddleText(this.Src, "\"nickname\": \"", "\", \"sCityId");
            Name = Program.RemoveInvalidFile(Name);
            MaxTimeStamp = GetTime(0);
            Console.WriteLine("{0}\t最新作品发布时间:{1}", Name, Program.StampToTime(MaxTimeStamp));
            SongList = new List<Song>();
        }

        /// <summary>
        /// SInger有参构造
        /// </summary>
        /// <param name="uid">uid</param>
        /// <param name="n">昵称</param>
        public Singer(string uid, string n)
        {
            Console.Write("正在获取歌手信息:");
            Uid = uid;
            Name = Program.RemoveInvalidFile(n);
            Src = Program.Get(cgi + Uid);
            if (Src.Contains("参数错误") || !Src.Contains("ok"))
            {
                throw new Exception(string.Format("{0}\nuid:{1}\n请检查网络连接以及输入的uid是否正确", Src, Uid));
            }
            MaxTimeStamp = GetTime(0);
            Console.WriteLine("{0}\t最新作品发布时间:{1}", Name, Program.StampToTime(MaxTimeStamp));
            SongList = new List<Song>();
        }
        /// <summary>
        /// Singer默认构造(无需联网)
        /// </summary>
        public Singer()
        { }
        #endregion
        #region 功能函数
        /// <summary>
        /// 将字符串转换为Singer对象
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static Singer Parse(string s)
        {
            string str = s.Contains("personal?uid=") ? Program.GetRightText(s, "personal?uid=") : s;
            if (str.Contains(":"))
            {
                string[] temp = str.Split(':');
                return (new Singer(temp[0], temp[1]));
            }
            return new Singer(str);
        }

        /// <summary>
        /// 从文件中读取歌手
        /// </summary>
        /// <param name="path">路径</param>
        /// <returns></returns>
        public static List<Singer> FromFile(string path)
        {
            List<Singer> list = new List<Singer>();
            string[] str = File.ReadAllLines(path, Encoding.Default);
            foreach (string i in str)
            {
                if (i.Trim() != "" && !i.Contains("/"))
                    list.Add(Singer.Parse(i));
            }
            return list;
        }

        /// <summary>
        /// 重写方法ToString()
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            //return string.Format("{0}\t{1}", Uid, name);
            return string.Format("{0}\t{1}\t最新作品发布时间:{2}", Uid, Name, this.MaxTime);
        }

        /// <summary>
        /// 获取所有作品
        /// </summary>
        public void GetAll()
        {
            Src = Program.Get(cgi + Uid);
            if (!Src.Contains("shareid"))
            { return; }
            Console.Write("获取{0}的所有作品信息:", Name);
            string temp = null;
            for (int i = 1; true; i++)
            {
                string str = Program.Get(string.Format("{0}{1}{2}{3}", CGI[0], i, CGI[1], Uid));
                if (!str.Contains("shareid"))
                { break; }
                temp += str.Substring(str.IndexOf("ugclist"));
            }
            SrcPP = temp;
            string[] song = Program.GetMiddleTextMultiple(SrcPP, "shareid\": \"", "\", \"stralbumid");
            string[] names = Program.GetMiddleTextMultiple(SrcPP, "title\": \"", "\", \"ugcmask");
            for (int i = 0; i < song.Length; i++)
            {
                Song s = new Song(song[i], Name, names[i]);
                SongList.Add(s);
            }
            Console.WriteLine("共有{0}首作品", SongList.Count);
        }
        /// <summary>
        /// 下载所有作品
        /// </summary>
        public void DownloadAll()
        {
            if (SongList.Count == 0)
            { Console.WriteLine("{0}没有任何作品", Name); }
            Console.WriteLine("开始下载{0}的所有作品", Name);
            foreach (Song i in SongList)
            { i.Download(); }
            Console.WriteLine("下载完成");
        }
        /// <summary>
        /// 监控
        /// </summary>
        /// <returns>返回新作品</returns>
        public Song Monit()
        {
            try
            {
                Src = Program.Get(cgi + Uid);
                int t = GetTime(MaxTimeStamp);
                if (t > MaxTimeStamp)
                {//发现新作品,准备下载
                    Console.WriteLine(DateTime.Now + "发现新作品,准备下载");
                    Program.Beep();
                    Song s = DownloadNew(MaxTimeStamp);
                    Console.WriteLine("下载完成:{0},目前最新作品发布时间为:{1}", s.Title, this.MaxTime);
                    s.BrushAsync(1000);
                    Song.Retry();
                    return s;
                }
                return null;
            }
            catch (Exception ex)
            {
                Program.WriteTxt(ex.Message, true);
                Song.Retry();
                return null;
            }
            finally
            {
                Program.Collect();
                count++;
            }
        }
        /// <summary>
        /// 获取最大发布时间
        /// </summary>
        /// <param name="protectedTime"></param>
        /// <returns></returns>
        public int GetTime(int protectedTime)
        {
            try
            {
                string[] time = Program.GetMiddleTextMultiple(Src, "time\": ", ", \"title");
                if (time.Length == 0)
                { return 0; }
                protectedTime = Program.ArrayMax(Program.StringArrayToInt(time));
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Program.WriteTxt(ex.Message, true);
            }
            return protectedTime;
        }
        /// <summary>
        /// 下载最新作品
        /// </summary>
        /// <param name="t0">已知最大时间</param>
        public Song DownloadNew(int t0)
        {
            try
            {
                string[] song = Program.GetMiddleTextMultiple(Src, "shareid\": \"", "\", \"stralbumid");
                int[] t = Program.StringArrayToInt(Program.GetMiddleTextMultiple(Src, "time\": ", ", \"title"));
                MaxTimeStamp = Program.ArrayMax(t);
                if (t[0] > t0)
                {
                    Song s = new Song(song[0], Name);
                    s.Download();
                    return s;
                }
                if (t[1] > t0)
                {
                    Song s = new Song(song[1], Name);
                    s.Download();
                    return s;
                }
                if (t[2] > t0)
                {
                    Song s = new Song(song[2], Name);
                    s.Download();
                    return s;
                }
                if (t[3] > t0)
                {
                    Song s = new Song(song[3], Name);
                    s.Download();
                    return s;
                }
                return new Song(song[0], Name);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Program.WriteTxt(ex.Message, true);
                throw;
            }
        }
        #endregion
    }

    /// <summary>
    /// 歌曲类
    /// </summary>
    [Serializable]
    public class Song
    {
        #region 静态字段
        public const string url = "https://node.kg.qq.com/play?s=";
        public const string cgi = "https://cgi.kg.qq.com/fcgi-bin/kg_ugc_getdetail?inCharset=GB2312&outCharset=utf-8&v=4&shareid=";
        public static string[] CGI = { "https://node.kg.qq.com/cgi/fcgi-bin/fcg_play_common_count?shareid=", "&ptime=20&outCharset=utf-8&from=1&nocache=", "&format=json&g_tk=5281&g_tk_openkey=5281" };
        public static List<Song> exList = new List<Song>();
        #endregion
        #region 属性集
        public string SQL { get { return string.Format("insert into Wesing values('{0}','{1}','{2}')",Shareid,Title,SingerName); } }
        /// <summary>
        /// 歌曲id
        /// </summary>
        public string Shareid { get; set; }
        /// <summary>
        /// 歌名
        /// </summary>
        public string Title { get; set; }
        /// <summary>
        /// 歌手名
        /// </summary>
        public string SingerName { get; set; }
        /// <summary>
        /// 网页源码
        /// </summary>
        public string Src { get; set; }
        /// <summary>
        /// 播放量
        /// </summary>
        public int Count { get; set; }
        /// <summary>
        /// 是否为MV
        /// </summary>
        public bool IsMV
        { get { return Src.Contains(".mp4"); } }
        /// <summary>
        /// 导出格式
        /// </summary>
        public string Export
        { get { return string.Format("{0}:{1}:{2}", Shareid, SingerName, Title); } }
        /// <summary>
        /// 下载地址
        /// </summary>
        public string DownUrl
        { get; set; }
        /// <summary>
        /// 发布时间
        /// </summary>
        public int Time
        { get; set; }
        public int BrushedCount { get; set; }
        /// <summary>
        /// 显示格式
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        { return string.Format("{0} - {1}", SingerName, Title); }
        #endregion
        #region 构造函数
        /// <summary>
        /// Song有参构造
        /// </summary>
        /// <param name="shareid">作品id</param>
        public Song(string shareid, string singerName)
        {
            Shareid = shareid;
            SingerName = singerName;
        }
        /// <summary>
        /// Song有参构造(手动传参)
        /// </summary>
        /// <param name="shareid"></param>
        /// <param name="singerName"></param>
        /// <param name="title"></param>
        public Song(string shareid, string singerName, string title)
        {
            Shareid = shareid;
            SingerName = singerName;
            Title = title;
        }
        /// <summary>
        /// Song有参构造(自动获取歌手名)
        /// </summary>
        /// <param name="shareid"></param>
        public Song(string shareid)
        { Shareid = shareid; }

        /// <summary>
        /// Song默认构造
        /// </summary>
        public Song()
        { }
        #endregion
        #region
        /// <summary>
        /// 将字符串转换为Song对象
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static Song Parse(string s)
        {
            string str = s;
            if (s.Contains("play?s="))
            {
                return new Song(str.Substring(s.IndexOf("play?s=") + 7, 16));
            }
            if (str.Contains(":"))
            {
                string[] temp = str.Split(':');
                if (temp.Length == 2)
                    return new Song(temp[0].Trim(), temp[1].Trim());
                else return new Song(temp[0].Trim(), temp[1].Trim(), temp[2].Trim());
            }
            return new Song(str);
        }

        /// <summary>
        /// 从文件中读取歌曲
        /// </summary>
        /// <param name="path">路径</param>
        /// <returns></returns>
        public static List<Song> FromFile(string path)
        {
            List<Song> list = new List<Song>();
            string[] str = File.ReadAllLines(path, Encoding.Default);
            foreach (string i in str)
            {
                if (i.Trim() != "" && !i.Contains("/"))
                    list.Add(Song.Parse(i));
            }
            return list;
        }
        /// <summary>
        /// 获取或更新作品信息
        /// </summary>
        public virtual void Getdata(bool throwex = false)
        {
            try
            {
                Src = Program.Get(url + Shareid);
                if (!Src.Contains("playurl"))
                {
                    DownUrl = "https://node.kg.qq.com/cgi/fcgi-bin/fcg_get_play_url?shareid=" + Shareid;
                    Console.WriteLine("获取歌曲信息失败,将直接解析下载地址");
                    return;
                }
                if (IsMV)
                { DownUrl = Program.GetMiddleText(Src, "playurl_video\":\"", "&fromtag").Replace("\\", ""); }
                else { DownUrl = Program.GetMiddleText(Src, "playurl\":\"", "&fromtag").Replace("\\", ""); }
                Count = Program.TriParse(Program.GetMiddleText(Src, "play_num\":", ",\"playurl"), 0);
                if (SingerName == null)
                { SingerName = Program.GetMiddleText(Src, "shareNick\":\"", "\"};"); }
                if (Title == null)
                { Title = Program.GetMiddleText(Src, "<meta itemprop=\"name\" content=\"", "\" />"); }
                int[] times = Program.StringArrayToInt(Program.GetMiddleTextMultiple(Src, "ctime\":", ",\"f_lat"));
                for (int i = 0; i < times.Length; i++)
                {
                    if (times[i] == 0) { times[i] = Program.ArrayMax(times); }
                }
                Time = Program.ArrayMin(times);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Program.WriteTxt(ex.Message, true);
                if (throwex)
                    throw;
            }
        }

        /// <summary>
        /// 下载
        /// </summary>
        public void Download()
        {
            try
            {
                Getdata();
                Console.Title = string.Format("{0} 正在下载中:{1}-{2}", DateTime.Now, SingerName, Title);
                Console.WriteLine("{0} 正在下载中:{1}-{2}\t{3}", DateTime.Now, SingerName, Title, url + Shareid);
                Program.WriteTxt(string.Format("{0} 正在下载中:{1}-{2}\t{3}\n{4}\n", DateTime.Now, SingerName, Title, url + Shareid, DownUrl), false);
                string fileName = IsMV ? string.Format("{0} - {1}.mp4", SingerName, Title) : string.Format("{0} - {1}.m4a", SingerName, Title);
                fileName = Program.RemoveInvalidFile(fileName);//删除非法字符
                string dir = Environment.CurrentDirectory;
                if (!Directory.Exists(string.Format("{0}\\{1}", dir, SingerName)))
                { Directory.CreateDirectory(string.Format("{0}\\{1}", dir, SingerName)); }//创建文件夹
                dir = string.Format("{0}\\{1}", Environment.CurrentDirectory, SingerName);
                string path = string.Format("{0}\\{1}", dir, fileName);
                if (File.Exists(path))
                {//重名文件自动重命名
                    fileName = Program.StampToTime(Time).ToString("yyyy-MM-dd HHmmss") + fileName;
                    path = string.Format("{0}\\{1}", dir, fileName);
                }
                Program.Down(path, DownUrl);
                exList.Remove(this);//下载成功,从错误列表中移除
            }
            catch (Exception ex)
            {//下载失败,添加至错误列表
                Console.WriteLine(ex.Message);
                Program.WriteTxt(ex.Message, true);
                exList.Add(this);
            }
        }

        /// <summary>
        /// 单独线程下载
        /// </summary>
        public void DownloadAsync()
        {
            Thread t = new Thread(Download);
            t.Start();
        }

        /// <summary>
        /// 重新下载所有出错的歌曲
        /// </summary>
        /// <returns></returns>
        public static int Retry()
        {
            if (exList.Count == 0)
            { return 0; }
            Console.WriteLine("正在重新下载出错的歌曲,共{0}首", exList.Count);
            Program.WriteTxt(string.Format("正在重新下载出错的歌曲,共{0}首", exList.Count), true);
            Song[] arr = new Song[exList.Count];
            for (int i = 0; i < exList.Count; i++)
            { arr[i] = (Song)exList[i]; }
            foreach (Song i in arr)
            { i.Download(); }
            return exList.Count;
        }

        /// <summary>
        /// 刷播放量
        /// </summary>
        /// <returns>判断是否能刷</returns>
        public bool Brush()
        {
            int c1 = 0, c2 = 0;
            try
            {
                Getdata();
                c1 = Count;
                Program.Get(CGI[0] + Shareid + CGI[1] + Program.TimeToStamp(DateTime.Now) + CGI[2]);
                Getdata();
                c2 = Count;
            }
            catch (Exception)
            { return false; }
            return c2 > c1;
        }

        /// <summary>
        /// 一键刷量
        /// </summary>
        public void BrushOneKey(object delay)
        {
            while (true)
            {
                if (!Brush())
                {
                    Program.Beep();
                    return;
                }
                Thread.Sleep((int)delay);
            }
        }

        /// <summary>
        /// 单独线程一键刷量
        /// </summary>
        public void BrushAsync(int count)
        {
            for (int i = 0; i < count; i++)
            {
                CrazyBrush();
            }
        }

        public void CrazyBrush()
        {
            Program.Get(CGI[0] + Shareid + CGI[1] + Program.TimeToStamp(DateTime.Now) + CGI[2], 5000);
            this.BrushedCount++;
        }
        public static int BrushCount = 1;
        #endregion
    }
}
