コード例 #1
0
ファイル: Program.cs プロジェクト: 204504bySE/twigaten
        public async Task <(long MinDownloadedAt, string[] MediaPath)> GetMediaPath(long downloaded_at)
        {
            using (var cmd = new MySqlCommand(@"SELECT
m.media_id, mt.media_url
FROM media m
JOIN media_downloaded_at md ON m.media_id = md.media_id
JOIN media_text mt ON m.media_id = mt.media_id
WHERE md.downloaded_at <= @downloaded_at
ORDER BY md.downloaded_at DESC
LIMIT @limit"))
            {
                cmd.Parameters.AddWithValue("@downloaded_at", downloaded_at);
                cmd.Parameters.AddWithValue("@limit", 1000);

                var  ret             = new List <string>();
                long minDownloadedAt = long.MaxValue;
                await ExecuteReader(cmd, (r) =>
                {
                    long down = r.GetInt64(0);
                    if (down < minDownloadedAt)
                    {
                        minDownloadedAt = down;
                    }
                    ret.Add(MediaFolderPath.ThumbPath(down, r.GetString(1)));
                }).ConfigureAwait(false);

                return(minDownloadedAt, ret.ToArray());
            }
        }
コード例 #2
0
ファイル: Program.cs プロジェクト: 204504bySE/twigaten
        static async Task Main(string[] args)
        {
            //多重起動防止
            //CheckOldProcess.CheckandExit();

            var config = Config.Instance;
            var db     = new DBHandler();

            await db.NullifyPidAll().ConfigureAwait(false);

            var child = new ChildProcessHandler();

            //子プロセスが複数あったらいるかもしれないけど今は無用(´・ω・`)
            //LockerHandler.CheckAndStart();
            //画像保存用フォルダはここで作る
            MediaFolderPath.MkdirAll();

            bool      GetMyTweet = false; //後から追加されたアカウントはstreamer側で自分のツイートを取得させる
            Stopwatch LoopWatch  = new Stopwatch();

            while (true)
            {
                LoopWatch.Restart();

                long[] users = await db.SelectNewToken().ConfigureAwait(false);

                int NeedProcessCount = (int)(await db.CountToken().ConfigureAwait(false) / config.crawlparent.AccountLimit + 1);
                if (users.Length > 0)
                {
                    Console.WriteLine("Assigning {0} tokens", users.Length);
                    //アカウント数からして必要な個数のtwidownを起動する
                    while (NeedProcessCount > child.Count)
                    {
                        int newpid = child.Start();
                        if (newpid < 0)
                        {
                            await Task.Delay(60000).ConfigureAwait(false); continue;
                        }                                                                               //雑すぎるエラー処理
                        Console.WriteLine("New PID: {0}", newpid);
                    }
                    //あとはボコボコ突っ込む
                    await child.AssignToken(users, GetMyTweet).ConfigureAwait(false);
                }
                GetMyTweet = true;


                //ここでプロセス間通信を監視して返事がなかったら再起動する
                do
                {
                    await child.DeleteDead().ConfigureAwait(false);

                    //LockerHandler.CheckAndStart();
                    await Task.Delay(10000).ConfigureAwait(false);
                } while (LoopWatch.ElapsedMilliseconds < 60000);
                LoopWatch.Stop();
            }
        }
コード例 #3
0
ファイル: Program.cs プロジェクト: 204504bySE/twigaten
        //しばらくツイートがないアカウントのprofile_imageを消す
        public async Task RemoveOldProfileImage()
        {
            DriveInfo    drive         = new DriveInfo(config.crawl.PictPathProfileImage);
            int          RemovedCount  = 0;
            const int    BulkUnit      = 1000;
            const string head          = @"DELETE FROM user_updated_at WHERE user_id IN";
            string       BulkUpdateCmd = BulkCmdStrIn(BulkUnit, head);

            //Console.WriteLine("{0} / {1} MB Free.", drive.AvailableFreeSpace >> 20, drive.TotalSize >> 20);
            try
            {
                var Table = new List <(long user_id, string profile_image_url, bool is_default_profile_image)>(BulkUnit);
                while (drive.TotalFreeSpace < drive.TotalSize / 16)
                {
                    using (var cmd = new MySqlCommand(@"SELECT
user_id, profile_image_url, is_default_profile_image
FROM user
JOIN user_updated_at USING (user_id)
WHERE profile_image_url IS NOT NULL
ORDER BY updated_at LIMIT @limit;"))
                    {
                        cmd.Parameters.AddWithValue("@limit", BulkUnit);
                        if (!await ExecuteReader(cmd, (r) => Table.Add((r.GetInt64(0), r.GetString(1), r.GetBoolean(2)))))
                        {
                            return;
                        }
                    }
                    if (Table.Count < BulkUnit)
                    {
                        break;
                    }

                    foreach (var row in Table)
                    {
                        if (!row.is_default_profile_image)
                        {
                            File.Delete(MediaFolderPath.ProfileImagePath(row.user_id, row.is_default_profile_image, row.profile_image_url));
                        }
                    }
                    using (var upcmd = new MySqlCommand(BulkUpdateCmd))
                    {
                        for (int n = 0; n < Table.Count; n++)
                        {
                            upcmd.Parameters.Add("@" + n.ToString(), DbType.Int64).Value = Table[n].user_id;
                        }
                        RemovedCount += await ExecuteNonQuery(upcmd);
                    }
                    //Console.WriteLine("{0} Icons removed", RemovedCount);
                    //Console.WriteLine("{0} / {1} MB Free.", drive.AvailableFreeSpace >> 20, drive.TotalSize >> 20);
                    Table.Clear();
                }
            }
            catch (Exception e) { Console.WriteLine(e); return; }
            Console.WriteLine("{0} Icons removed.", RemovedCount);
        }
コード例 #4
0
ファイル: Program.cs プロジェクト: 204504bySE/twigaten
        public async Task RemoveOldMedia()
        {
            DriveInfo drive            = new DriveInfo(config.crawl.PictPaththumb);
            int       RemovedCountFile = 0;
            const int BulkUnit         = 1000;

            //Console.WriteLine("{0} / {0} MB Free.", drive.AvailableFreeSpace >> 20, drive.TotalSize >> 20);
            try
            {
                var Table = new List <(long media_id, string media_url)>(BulkUnit);
                while (drive.TotalFreeSpace < drive.TotalSize / 16)
                {
                    using (MySqlCommand cmd = new MySqlCommand(@"(SELECT
m.media_id, mt.media_url
FROM media_downloaded_at md
JOIN media m on md.media_id = m.media_id
JOIN media_text mt ON m.media_id = mt.media_id
ORDER BY downloaded_at
LIMIT @limit)
ORDER BY media_id;"))
                    {
                        cmd.Parameters.AddWithValue("@limit", BulkUnit);
                        if (!await ExecuteReader(cmd, (r) => Table.Add((r.GetInt64(0), r.GetString(1)))))
                        {
                            return;
                        }
                    }
                    if (Table.Count < BulkUnit)
                    {
                        break;
                    }

                    foreach (var row in Table)
                    {
                        File.Delete(MediaFolderPath.ThumbPath(row.media_id, row.media_url));
                    }

                    using (var Cmd = new MySqlCommand(BulkCmdStrIn(Table.Count, @"DELETE FROM media_downloaded_at WHERE media_id IN")))
                    {
                        for (int i = 0; i < Table.Count; i++)
                        {
                            Cmd.Parameters.Add("@" + i.ToString(), DbType.Int64).Value = Table[i].media_id;
                        }
                        await ExecuteNonQuery(Cmd).ConfigureAwait(false);
                    }
                    RemovedCountFile += Table.Count;
                    //Console.WriteLine("{0} Media removed", RemovedCountFile);
                    //Console.WriteLine("{0} / {1} MB Free.", drive.AvailableFreeSpace >> 20, drive.TotalSize >> 20);
                    Table.Clear();
                }
            }
            catch (Exception e) { Console.WriteLine(e); return; }
            Console.WriteLine("{0} Old Media removed.", RemovedCountFile);
        }
コード例 #5
0
        public async Task <IActionResult> thumb(string FileName)
        {
            if (!long.TryParse(Path.GetFileNameWithoutExtension(FileName), out long media_id))
            {
                return(StatusCode(400));
            }

            //まずは鯖内のファイルを探す 拡張子はリクエストURLを信頼して手を抜く
            string localmedia = MediaFolderPath.ThumbPath(media_id, FileName);

            if (System.IO.File.Exists(localmedia))
            {
                return(PhysicalFile(localmedia, GetMime(FileName), true));
            }

            //鯖内にファイルがなかったのでtwitterから横流しする
            var MediaInfo = await DB.SelectThumbUrl(media_id).ConfigureAwait(false);

            if (MediaInfo == null)
            {
                return(StatusCode(404));
            }

            var ret = await Download(MediaInfo.Value.media_url + (MediaInfo.Value.media_url.IndexOf("twimg.com") >= 0 ? ":thumb" : ""),
                                     MediaInfo.Value.tweet_url).ConfigureAwait(false);

            if (RemovedStatusCode(ret.StatusCode))
            {
                Removed.Enqueue(MediaInfo.Value.source_tweet_id);
            }
            if (ret.FileBytes != null)
            {
                //画像の取得に成功したわけだし保存しておきたい
                StoreMediaBlock.Post((MediaInfo.Value, ret.FileBytes));

                return(File(ret.FileBytes, GetMime(FileName)));
            }
            else
            {
                return(StatusCode((int)ret.StatusCode));
            }
        }
コード例 #6
0
ファイル: Program.cs プロジェクト: 204504bySE/twigaten
        //ツイートが削除されて参照されなくなった画像を消す
        public async Task RemoveOrphanMedia()
        {
            int       RemovedCount = 0;
            const int BulkUnit     = 1000;

            try
            {
                var Table = new List <(long media_id, string media_url)>(BulkUnit);
                do
                {
                    Table.Clear();  //ループ判定が後ろにあるのでここでやるしかない
                    using (MySqlCommand cmd = new MySqlCommand(@"SELECT m.media_id, mt.media_url
FROM media m
LEFT JOIN media_downloaded_at md ON m.media_id = md.media_id
JOIN media_text mt ON m.media_id = mt.media_id
WHERE m.source_tweet_id IS NULL
AND (md.downloaded_at IS NULL OR md.downloaded_at < @downloaded_at)
ORDER BY m.media_id
LIMIT @limit;"))
                    {
                        //ダウンロードしたての画像は除く
                        cmd.Parameters.Add("@downloaded_at", MySqlDbType.Int64).Value = DateTimeOffset.UtcNow.ToUnixTimeSeconds() - 600;
                        cmd.Parameters.AddWithValue("@limit", BulkUnit);
                        if (!await ExecuteReader(cmd, (r) => Table.Add((r.GetInt64(0), r.GetString(1)))))
                        {
                            return;
                        }
                    }
                    if (Table.Count < 1)
                    {
                        break;
                    }

                    var DeleteMediaBlock = new ActionBlock <(long media_id, string media_url)>(async(row) =>
                    {
                        File.Delete(MediaFolderPath.ThumbPath(row.media_id, row.media_url));

                        using var DeleteCmd  = new MySqlCommand(@"DELETE FROM media WHERE media_id = @media_id");
                        using var DeleteCmd2 = new MySqlCommand(@"DELETE FROM media_text WHERE media_id = @media_id");
                        DeleteCmd.Parameters.Add("@media_id", MySqlDbType.Int64).Value  = row.media_id;
                        DeleteCmd2.Parameters.Add("@media_id", MySqlDbType.Int64).Value = row.media_id;

                        int deleted = await ExecuteNonQuery(new[] { DeleteCmd, DeleteCmd2 }).ConfigureAwait(false);
                        if (deleted > 0)
                        {
                            Interlocked.Add(ref RemovedCount, deleted >> 1);
                        }
                    }, new ExecutionDataflowBlockOptions()
                    {
                        MaxDegreeOfParallelism = Environment.ProcessorCount
                    });

                    foreach (var row in Table)
                    {
                        DeleteMediaBlock.Post(row);
                    }
                    DeleteMediaBlock.Complete();
                    await DeleteMediaBlock.Completion.ConfigureAwait(false);

                    //Console.WriteLine("{0} Orphan Media removed.", RemovedCount);
                } while (Table.Count >= BulkUnit);
            }
            catch (Exception e) { Console.WriteLine(e); return; }
            Console.WriteLine("{0} Orphan Media removed.", RemovedCount);
        }
コード例 #7
0
        /// <summary>
        /// ユーザーのアイコンを実際に探す
        /// 鯖内 > 横流し の優先度
        /// </summary>
        /// <param name="FileName">アカウントID.拡張子</param>
        /// <returns></returns>
        async Task <(int StatusCode, Stream Data)> FindProfileImage(string FileName)
        {
            //ファイル名の先頭が"_"だったら初期アイコンのURLとみなす
            if (FileName.StartsWith('_'))
            {
                string trimmedname = MediaFolderPath.DefaultProfileImagePath(FileName.Substring(1));
                if (System.IO.File.Exists(MediaFolderPath.DefaultProfileImagePath(trimmedname)))
                {
                    return(StatusCodes.Status200OK, System.IO.File.OpenRead(trimmedname));
                }
                else
                {
                    return(StatusCodes.Status404NotFound, null);
                }
            }

            //↑以外はuser_idとみなす
            if (!long.TryParse(Path.GetFileNameWithoutExtension(FileName), out long user_id))
            {
                return(StatusCodes.Status400BadRequest, null);
            }

            //まずは鯖内のファイルを探す 拡張子はリクエストURLを信頼して手を抜く
            //初期アイコンではないものとして探す
            string localmedia = MediaFolderPath.ProfileImagePath(user_id, false, FileName);

            if (System.IO.File.Exists(localmedia))
            {
                return(StatusCodes.Status200OK, System.IO.File.OpenRead(localmedia));
            }

            var ProfileImageInfo = await DB.SelectProfileImageUrl(user_id).ConfigureAwait(false);

            if (ProfileImageInfo == null)
            {
                return(StatusCodes.Status404NotFound, null);
            }
            //初期アイコンなら改めて鯖内を探す(通常はここには来ない)
            if (ProfileImageInfo.Value.is_default_profile_image)
            {
                string defaulticon = MediaFolderPath.ProfileImagePath(user_id, true, ProfileImageInfo.Value.profile_image_url);
                if (System.IO.File.Exists(defaulticon))
                {
                    return(StatusCodes.Status200OK, System.IO.File.OpenRead(defaulticon));
                }
            }
            //鯖内にファイルがなかったのでtwitterから横流しする
            var downloaded = await Download(ProfileImageInfo.Value.profile_image_url, ProfileImageInfo.Value.tweet_url).ConfigureAwait(false);

            if (downloaded.FileBytes != null)
            {
                //画像の取得に成功したわけだし保存しておきたい
                //初期アイコンはいろいろ面倒なのでここではやらない
                if (!ProfileImageInfo.Value.is_default_profile_image)
                {
                    StoreProfileImageBlock.Post((ProfileImageInfo.Value, downloaded.FileBytes));
                }

                return(StatusCodes.Status200OK, new MemoryStream(downloaded.FileBytes, false));
            }
            else
            {
                return((int)downloaded.StatusCode, null);
            }
        }
コード例 #8
0
        /// <summary>
        /// こいつのツイートを全消しする
        /// </summary>
        /// <param name="user_id"></param>
        /// <returns></returns>
        public async Task DeleteUser(long user_id)
        {
            const int DeleteUnit = 1000;

            //ツイートを次々消すやつ
            var RemoveTweetBlock = new ActionBlock <long>(async(tweet_id) =>
            {
                Counter.TweetToDelete.Increment();
                using (var cmd = new MySqlCommand(@"DELETE FROM tweet WHERE tweet_id = @tweet_id;"))
                    using (var cmd2 = new MySqlCommand(@"DELETE FROM tweet_text WHERE tweet_id = @tweet_id;"))
                    {
                        cmd.Parameters.Add("@tweet_id", MySqlDbType.Int64).Value  = tweet_id;
                        cmd2.Parameters.Add("@tweet_id", MySqlDbType.Int64).Value = tweet_id;
                        if (await ExecuteNonQuery(new[] { cmd, cmd2 }).ConfigureAwait(false) > 0)
                        {
                            Counter.TweetDeleted.Increment();
                        }
                    }
            }, new ExecutionDataflowBlockOptions()
            {
                MaxDegreeOfParallelism = Environment.ProcessorCount, BoundedCapacity = DeleteUnit
            });


            long LastTweetId = 0;
            var  IdList      = new Queue <long>();

            //まずはツイートを消す
            while (true)
            {
                IdList.Clear();
                using (var cmd = new MySqlCommand(@"SELECT
tweet_id
FROM tweet
WHERE user_id = @user_id AND tweet_id > @tweet_id 
ORDER BY tweet_id LIMIT @limit;"))
                {
                    cmd.Parameters.Add("user_id", MySqlDbType.Int64).Value  = user_id;
                    cmd.Parameters.Add("tweet_id", MySqlDbType.Int64).Value = LastTweetId;
                    cmd.Parameters.Add("limit", MySqlDbType.Int64).Value    = (long)DeleteUnit;
                    await ExecuteReader(cmd, (r) =>
                    {
                        long id = r.GetInt64(0);
                        IdList.Enqueue(id);
                        LastTweetId         = id;
                        Counter.LastTweetID = id;
                    }).ConfigureAwait(false);

                    if (IdList.Count == 0)
                    {
                        break;
                    }
                    while (IdList.TryDequeue(out long id))
                    {
                        await RemoveTweetBlock.SendAsync(id);

                        LastTweetId = id;
                    }
                }
            }
            RemoveTweetBlock.Complete();
            await RemoveTweetBlock.Completion.ConfigureAwait(false);


            //アイコンも忘れずに消す
            using (var cmd = new MySqlCommand(@"SELECT
profile_image_url, is_default_profile_image
FROM user
JOIN user_updated_at USING (user_id)
WHERE user_id = @user_id;"))
            {
                string profile_image_url        = null;
                bool   is_default_profile_image = true;

                cmd.Parameters.Add("user_id", MySqlDbType.Int64).Value = user_id;
                await ExecuteReader(cmd, (r) => { profile_image_url = r.GetString(0); is_default_profile_image = r.GetBoolean(1); }).ConfigureAwait(false);

                if (profile_image_url != null && !is_default_profile_image)
                {
                    File.Delete(MediaFolderPath.ProfileImagePath(user_id, is_default_profile_image, profile_image_url));
                }
            }

            using (var cmd = new MySqlCommand(@"DELETE FROM user WHERE user_id = @user_id;"))
            {
                cmd.Parameters.Add("user_id", MySqlDbType.Int64).Value = user_id;
                await ExecuteNonQuery(cmd).ConfigureAwait(false);
            }
        }