Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Sunday, April 3, 2016

MULTI THREADING ( Only What I know and worked out )

 


 

this the only one that I know and it worked out for me...

string[] ric = new string[] 
            {
                "https://www.youtube.com/watch?v=vzuQeFOiyYE",
"https://www.youtube.com/watch?v=xXdHgCao0fU",
"https://www.youtube.com/watch?v=3Fi-KxsTF9E"};


                Parallel.ForEach(
                    ric, tuple =>
                    {
                        Console.WriteLine(tuple); Thread.Sleep(1000);
                        Console.WriteLine("{0}: Extracted {1} urls", DateTime.Now, count);
                        Interlocked.Increment(ref count);
                    });
            

 

Monday, March 28, 2016

Custom Thumbnails youtube v3 ( 2 method )

 


 

I am facing a huge problem using the youtube-api. I try to set a custom thumbnail for one of my videos. Of course I have the permission to set custom thumbnails for my videos and an authenticated youtube service. I tried to do it in two ways (C#):

Method 1 using the .Net API:

    [...]
    Debug.WriteLine("Try to set thumbnail...");
    thumbnailStream = File.OpenRead(@"D:\myThumbnail.jpg");
    YouTubeService service = GetYouTubeService(); // Gets the authenticated youtube service
    ThumbnailsResource tr = new ThumbnailsResource(service);
    ThumbnailsResource.SetMediaUpload mediaUploadThumbnail = tr.Set(MY_VIDEO_ID, thumbnailStream, "image/jpeg");
    mediaUploadThumbnail.ChunkSize = 256 * 1024;
    mediaUploadThumbnail.ProgressChanged += mediaUploadThumbnail_ProgressChanged;
    mediaUploadThumbnail.ResponseReceived += mediaUploadThumbnail_ResponseReceived;
    Debug.WriteLine("... Starting async upload");
    mediaUploadThumbnail.UploadAsync();
    [...]

    void mediaUploadThumbnail_ProgressChanged(IUploadProgress obj)
    {
        Debug.WriteLine("... Progress changed");
        Debug.Write(" ... " + obj.Status.ToString() + ": " + obj.BytesSent + " bytes sent");
        if (obj.Exception != null)
        {
            Debug.Write("... Exception:" + obj.Exception.ToString());
            thumbnailStream.Close();
            uploadList[aktuelleUploadPosition].Status = UploadVideoStatus.ThumbnailFehler;
            objectListViewUploads.RefreshObjects(uploadList);
            Starte_Uploads();
        }
    }

    void mediaUploadThumbnail_ResponseReceived(ThumbnailSetResponse obj)
    {
        Debug.WriteLine("... Done: " + obj.Items[0].High.Url);
        thumbnailStream.Close();
        uploadList[aktuelleUploadPosition].Status = UploadVideoStatus.Fertig;
        objectListViewUploads.RefreshObjects(uploadList);
        Starte_Uploads();
    }

The Output of Method 1 is:

Try to set thumbnail...
... Starting async upload
... Progress changed ... Starting: 0 bytes sent
... Progress changed ... Uploading: 262144 bytes sent
... Progress changed ... Uploading: 524288 bytes sent
... Done: https://i.ytimg.com/s_vi/CjiPNurZs2M/hqdefault.jpg?sqp=CJCH958F&rs=AOn4CLA5OGm9ZfythL5cri4cjV3CyP4Pmg
... Progress changed ... Completed: 750441 bytes sent

Method 2 using direct Webrequest:

    Debug.WriteLine("Try to set thumbnail...");
    RefreshToken(); // sets a new access token or refreshes the existing on
    thumbnailStream = File.OpenRead(@"D:\myThumbnail.jpg");
    HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create("https://www.googleapis.com/upload/youtube/v3/thumbnails/set?videoId=" + MY_VIDEO_ID);
    webReq.Method = "POST";
    webReq.ContentLength = thumbnailStream.Length;
    webReq.ContentType = "image/jpeg";
    webReq.Headers.Set(HttpRequestHeader.Authorization, "Bearer " + access_token);
    byte[] thumbBuffer = ReadToEnd(thumbnailStream);
    var reqStream = webReq.GetRequestStream();
    reqStream.Write(thumbBuffer, 0, thumbBuffer.Length);
    reqStream.Close();
    try
    {
        HttpWebResponse resp = (HttpWebResponse)webReq.GetResponse();
        byte[] bytes = new byte[(int)resp.ContentLength];
        resp.GetResponseStream().Read(bytes, 0, (int)resp.ContentLength);
        Debug.WriteLine("Content: " + System.Text.Encoding.UTF8.GetString(bytes));
    }
    catch (WebException ex)
    {
        byte[] bytes = new byte[(int)ex.Response.ContentLength];
        ex.Response.GetResponseStream().Read(bytes, 0, (int)ex.Response.ContentLength);
        Debug.WriteLine("Content: " + System.Text.Encoding.UTF8.GetString(bytes));
    }
    thumbnailStream.Close();

The Output of Method 2 is:

Try to set thumbnail...
Content: {
 "kind": "youtube#thumbnailSetResponse",
 "etag": "\"gMjDJfS6nsym0T-NKCXALC_u_rM/FktWbvFp7RSXY9xeTkZbzfsVtH0\"",
 "items": [
  {
   "default": {
    "url": "https://i.ytimg.com/s_vi/CjiPNurZs2M/default.jpg?sqp=CMCQ958F&rs=AOn4CLAr7zN45H1HExk4K_-y8ujYXWMBFw",
    "width": 120,
    "height": 90
   },
   "medium": {
    "url": "https://i.ytimg.com/s_vi/CjiPNurZs2M/mqdefault.jpg?sqp=CMCQ958F&rs=AOn4CLAJcATINZ2yuyNE1Ij6engYjZA5qw",
    "width": 320,
    "height": 180
   },
   "high": {
    "url": "https://i.ytimg.com/s_vi/CjiPNurZs2M/hqdefault.jpg?sqp=CMCQ958F&rs=AOn4CLBOJfds9D6hyIFDyeYzr9oug52-Lg",
    "width": 480,
    "height": 360
   }
  }
 ]
}

Result:

Both methods seem to be doing, what they are supposed to do. But if I check the images represented by the urls in the outputs, I see that they are the default “in progress image” (the one with the three dots) if no thumbnail for this video had been set before or the old thumbnail if it had been set before. The thumbnail shown for the video in my video manager is always the default “in progress image”. So no image is being set. I know that the image (D:\myThumbnail.jpg) is ok and can be set, because I can set it as a thumbnail in my video manager.

Multi threading with Windows Forms

The article taken from  http://www.codeproject.com/Articles/631514/Multi-threading-with-Windows-Forms as my note, all credits goes to author and codeproject

another example is http://stoyanov.in/2010/12/29/multithreading-with-windows-forms-in-c/


Introduction

I've come across a requirement on a number of occasions to produce a Windows application that interacts with a remote web server. The Windows application may be dealing with a web-service, or simply automating form input or screen scraping - what is common is that there is one side of the equation that is web based and therefore can potentially handle multiple requests, thus allowing us to complete the process faster. This article is a project that consists of two parts: a threaded Windows client, and a simple MVC application that it interacts with. As not all remote services allow multiple connections, the project gives the option for running the process in sequential or parallel (threaded) mode. The source code of both projects is attached.

Background

The main concept being introduced in this article is Windows multi-threading. The approach I have taken is one of many open to developers.
The technologies being demonstrated here are:
  • using an HTTPClient in async mode in a thread
  • XML messages
  • interacting with a Windows application main operating thread to update objects on the user interface in a thread safe manner
The threading concept is as follows:
  • create a thread and set its various attributes
  • when the thread completes its work, have it call back into a work-completed method in the main form thread and update the user as required.

Setting things up

The simple server - An MVC app

In order to test our work, and not trigger a denial of service warning (large numbers of multiple threads can do that!), we will create a test harness. In this case, a simple MVC application will suffice. We will create a controller method GetXML that takes in an ID sent by the Windows application, and returns an XML response.
The GetXML method is called like this: http://localhost:4174/home/GetXML?ItemID=23.
And returns output XML like this:
<response type="response-out" timestamp="20130804132059"> 
    <itemid>23</itemid>
    <result>0</result>
</response>
NB: for the purposes of this test, a "result" of 0 = failure, 1 = success.
(1) Create a new MVC app, and add a new controller GetXML. We are also going to put in a small "sleep" command to slow things down a bit and emulate delay over the very busy Interwebs.
public ContentResult GetXML()
{
    // assign post parameters to variables
    string ReceivedID = Request.Params["ItemID"];
    // generate a random sleep time in milli-seconds
    Random rnd = new Random();
    // multiplier ensures we have good breaks between sleeps
    // note that with Random, the upper bound is exclusive so this really means 1..5
    int SleepTime = rnd.Next(1, 2) * 1000;
    // generate XML string to send back
    System.Threading.Thread.Sleep(SleepTime);
    return Content(TestModel.GetXMLResponse(ReceivedID), "text/xml");
} 

(2) Create a model method that takes care of the logic of constructing the XML response to send back. This will take as parameter an ID (int) that represents the identifier of a list of objects/queries that the user is working with. These could be credit cards, websites to scrape, account numbers, etc. In reality you can send in any data you need to work with, and return then to the main calling application.
public static string GetXMLResponse(string ItemID)
{
    // generate a random result code, 0= fail, 1 = success
    // note that with Random, the upper bound is exclusive so this really means 1..2
    Random rnd = new Random();
    string ResultCode = rnd.Next(0, 2).ToString();
    string TimeStamp = GetTimeStamp();

    // create an XML document to send as response
    XmlDocument doc = new XmlDocument();

    // add root node and some attributes
    XmlNode rootNode = doc.CreateElement("response");
    XmlAttribute attr = doc.CreateAttribute("type");
    attr.Value = "response-out";
    rootNode.Attributes.Append(attr);
    attr = doc.CreateAttribute("timestamp");
    attr.Value = TimeStamp;
    rootNode.Attributes.Append(attr);
    doc.AppendChild(rootNode);

    // add child to root node sending back the item ID
    XmlNode dataNode = doc.CreateElement("itemid");
    dataNode.InnerText = ItemID;
    rootNode.AppendChild(dataNode);

    // add our random result
    dataNode = doc.CreateElement("result");
    dataNode.InnerText = ResultCode;
    rootNode.AppendChild(dataNode);
 
    // send back xml
    return doc.OuterXml;
}

The threaded client - A Windows form app

The client is visually quite simple. It contains two edit boxes for input variables, a listview to show the user what is happening, and a checkbox to tell the program if it should run in sequential or threaded mode.

We will go through the overall logic first by examining the sequential process, and then look at the threading part.
At the top of the form class we keep track of some variables:
private int RunningThreadCount;
private int RunTimes;
private int TimeStart;
Everything is kicked off by the RunProcess button click event:
TimeStart = System.Environment.TickCount;
InitProcess();
if (chkRunThreaded.Checked)
    RunProcessThreaded(); 
else RunProcess();
 
We keep track of the start time, and update this when all processes are complete to test how long the process took. We also at this stage call an Init method that sets things up for us, assigning some variables and filling the ListView with values.
// set up some default values
public void InitProcess()
{
    btnExit.Enabled = false;
    btnRunProcess.Enabled = false;
    chkRunThreaded.Enabled = false;
    RunTimes = int.Parse(edtTimesToRun.Text);
    FillListView();

    RunningThreadCount = 0;
}

// fill the ListView with the count items
public void FillListView()
{
    lvMain.Items.Clear();
    for (int i = 0; i < RunTimes; i++)
    {
        ListViewItem itm = new ListViewItem();
        itm.Text = (i+1).ToString();
        itm.SubItems.Add("Pending");
        itm.SubItems.Add("-");
        itm.SubItems.Add("-");
        lvMain.Items.Add(itm);
    } 
}
Let's now look at the sequential RunProcess method. This controls the main body of work for each web request. The number of times to run the process is set by the value of edtTimesToRun.Text which is assigned to the variable RunTimes.
The RunProcess method has a keyword of async - this is important as we are using the await keyword within the RunProcess method. The important part of this code is SendWebRequest - this takes the input, queries the web server, and returns a value that we use to update the UI for the user.
public async void RunProcess()
{
    for (int i = 0; i < RunTimes; i++)  {
        updateStatusLabel("Processing: " + (i + 1).ToString() + 
                 "/" + RunTimes.ToString());
        lvMain.Items[i].Selected = true;
        lvMain.Items[i].EnsureVisible();
        lvMain.Items[i].SubItems[1].Text = "Processing...";
        SimpleObj result = await Shared.SendWebRequest(
                                 new SimpleObj() 
                                     { ItemID = i.ToString(), 
                                       WebURL = edtTestServer.Text}
                                      );
        lvMain.Items[i].SubItems[1].Text = result.ResultCode;
        if (result.ResultCode == "ERR")
            lvMain.Items[i].SubItems[2].Text = result.Message;
    }
    CleanUp();
}
SendWebRequest is located in a separate shared.cs file as it is used from two different places. The shared.cs file also contains a simple object called SimpleObj. This is used to carry data between methods.
public class SimpleObj
{
    public string WebURL; // web address to send post to
    public string ResultCode; // 0 = failure, 1 = success
    // Used to store the html received back from our HTTPClient request
    public string XMLData;
    public string Message; // What we will show back to the user as response
    public string ItemID;
}
// Used to store the ListView item ID/index so
// we can update it when the thread completes    
The SendWebRequest method is again, flagged as async. This will be explained later.
In the SendWebRequest method, we set up an HTTPClient, calling its PostAsync method. Here we are telling the HTTPClient to perform a "POST" action against the server. If you have done web programming before, you will recall setting up a form:
<form action="somedomain.com/someaction?somevalue=134" method="post"> 
<input type="text" id="ItemID">
<input type="submit" value="send">
</form>
 
That is in effect we are doing here. We create the client object, send in as parameters the URL of the website we want to send the data to, together with the "content", which is the data packet to send. In this case the content simply consists of the parameter ItemID and its value.
HttpResponseMessage response = await httpClient.PostAsync(rec.WebURL, content);
The await keyword tells the code to sit there until with the HTTPClient comes back with a response, or an exception is raised. We examine the response to ensure it is valid (response.IsSuccessStatusCode), and assuming it is, we proceed to take the response content stream and process its XML result. Note the outer Try/Except wrapper - this will catch any HTTP connection errors and report these separately and allow the application to continue working smoothly.
public static async Task<SimpleObj> SendWebRequest(SimpleObj rec)
{
    SimpleObj rslt = new SimpleObj();
    rslt = rec;
    var httpClient = new HttpClient();

    // we send the server the ItemID
    StringContent content = new StringContent(rec.ItemID); 
    try
    {
        HttpResponseMessage response = 
            await httpClient.PostAsync(rec.WebURL, content);
        if (response.IsSuccessStatusCode)
        {
            HttpContent stream = response.Content;
            Task<string> data = stream.ReadAsStringAsync();
            rslt.XMLData = data.Result.ToString();
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(rslt.XMLData);
            XmlNode resultNode = doc.SelectSingleNode("response");
            string resultStatus = resultNode.InnerText;
            if (resultStatus == "1")
                rslt.ResultCode = "OK";
            else if (resultStatus == "0")
                rslt.ResultCode = "ERR";
            rslt.Message = doc.InnerXml;
        }
    }
    catch (Exception ex)
    { 
        rslt.ResultCode = "ERR";
        rslt.Message = "Connection error: " + ex.Message;
    } 
    return rslt;
}
So, that is the basic sequential work-flow. Take a list of work items, iterate through them in sequence, call the web server, and parse back the xml response.
As the processes are run sequentially, the overall time taken to complete can be high.








Now let's run through the RunProcessThread code and see the difference.
Here is our outer wrapper method in the main form:
public void RunProcessThreaded()
{
    updateStatusLabel("Status: threaded mode - watch thread count and list status");
    lblThreadCount.Visible = true;

    for (int i = 0; i < RunTimes; i++)
    {
        updateStatusLabel("Processing: " + (i + 1).ToString() + "/" + RunTimes.ToString());
        lvMain.Items[i].Selected = true;
        lvMain.Items[i].SubItems[1].Text = "Processing...";
        SimpleObj rec = new SimpleObj() { ItemID = i.ToString(), WebURL = edtTestServer.Text };
        CreateWorkThread(rec);
        RunningThreadCount++;
        UpdateThreadCount();
    } 
} 
The critical change is that instead of carrying out the WebRequest task on each loop sequentially, we are passing that task off to the method CreateWorkThread, passing in the required parameters.
public void CreateWorkThread(SimpleObj rec){
    ThreadWorker item = new ThreadWorker(rec);
    //subscribe to be notified when result is ready
    item.Completed += WorkThread_Completed;
    item.DoWork();
} 
This small method creates a new object ThreadWorker, and tells it to call WorkThread_Completed when it is finished. The WorkThread_Completed method in the form simply updates the form UI *in the context of the form thread* and performs some cleanup.
//handler method to run when work has completed
private void WorkThread_Completed(object sender, WorkItemCompletedEventArgs e)
{
    lvMain.Items[int.Parse(e.Result.ItemID)].SubItems[1].Text = e.Result.ResultCode;
    if (e.Result.ResultCode == "ERR")
        lvMain.Items[int.Parse(e.Result.ItemID)].SubItems[2].Text = e.Result.Message;

    RunningThreadCount--;
    UpdateThreadCount();

    if (RunningThreadCount == 0)
    {
        CleanUp();
    }
}
I have created a separate class/file ThreadWorker to manage the thread work - this keeps things separate and clean.
The class has some private members and a public event:
class ThreadWorker
{
    private AsyncOperation op; // async operation representing the work item
    private SimpleObj ARec; // variable to store the request and response details
    public event EventHandler<WorkItemCompletedEventArgs> Completed;
    //event handler to be run when work has completed with a result


    // constructor for the thread. Takes param ID index of the Listview to keep track of.
    public ThreadWorker(SimpleObj Rec)
    {
        ARec = Rec;                 
    } 
}
You will recall that in our main form, when we are creating each thread worker, we set up the thread object, then tell it to DoWork so this is the main kick-off method for the thread object.
public void DoWork()
{
    //get new async op object calling forms sync context
    this.op = AsyncOperationManager.CreateOperation(null);
    //queue work so a thread from the thread pool can pick it up and execute it
    ThreadPool.QueueUserWorkItem((o) => this.PerformWork(ARec)); 
} 
 
 
The reason I am using a ThreadPool object is that creating threads is a very expensive operation therefore using the pool means that on completion, threads can be put into a pool to be reused. After being added to the pool, we tell the thread to kick off the method PreformWork. This method calls our shared method SendWebRequest, and when that is finished, calls PostCompleted which gets picked up by the WorkThread_Completed method in the main form class.
private void PostCompleted() //SimpleObj result
{
    // call OnCompleted, passing in the SimpleObj result to it,
    // the lambda passed into this method is invoked in the context of the form UI
    op.PostOperationCompleted((o) => 
      this.OnCompleted(new WorkItemCompletedEventArgs(ARec)), ARec);
}

protected virtual void OnCompleted(WorkItemCompletedEventArgs Args)
{
    //raise the Completed event in the context of the form 
    EventHandler<WorkItemCompletedEventArgs> temp = Completed;
    if (temp != null)
    {
        temp.Invoke(this, Args);
    }
}
Our main form "WorkThread_Completed" method watches each incoming terminated thread, and when they have all completed, runs some cleanup code.
//handler method to run when work has completed
private void WorkThread_Completed(object sender, WorkItemCompletedEventArgs e)
{
    lvMain.Items[int.Parse(e.Result.ItemID)].SubItems[1].Text = e.Result.ResultCode;
    if (e.Result.ResultCode == "ERR")
        lvMain.Items[int.Parse(e.Result.ItemID)].SubItems[2].Text = e.Result.Message;
    RunningThreadCount--;
    UpdateThreadCount();
    if (RunningThreadCount == 0)
    {
        CleanUp();
    }
} 
And that is it, as you can see running threaded adds a bit more code, but dramatically improves performance.






As I stated at the start of this article, this is but one way of handling a threaded application. Thread pools have their advantages and disadvantages, you need to weigh up your goals and granular needs against the ease of use. If you are interested in this area you should also look at Background worker and if you want to harness the power that is in multi-core CPUs while threading the Task Parallel Library is a great way to go.
(PS: If you found this article useful or downloaded the code please let me know by giving a rating below!)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License

Friday, September 25, 2015

Using the ProcessRunner class

 


 

Getting from here as notepad http://csharptest.net/319/using-the-processrunner-class/

Let’s face it, using the System.Diagnostics.Process to run a process and capture the output is not as easy as it should be. People often get it wrong without realizing it. Adding to the complexities can be reading both the standard output as well as the standard error stream. To put this to bed once and for all here is the ‘right’ way all wrapped up in a helper class ProcessRunner.
Usage Example:
using CSharpTest.Net.Processes;
partial class Program
{
static int Main(string[] args)
{
 
string path = "C:\\MyProject";
ProcessRunner run = new ProcessRunner("svn.exe", "update", "{0}");
run.OutputReceived += new ProcessOutputEventHandler(run_OutputReceived);
return run.RunFormatArgs(path);
}
 
static void run_OutputReceived(object sender, ProcessOutputEventArgs args)
{
Console.WriteLine("{0}: {1}", args.Error ? "Error" : "Output", args.Data);
}
}
 

Using Process.Start to capture console output


 
Copy paste from as note, allcredit belong there http://csharptest.net/532/using-processstart-to-capture-console-output/



Background: I get a lot of traffic looking for details on running a process and collecting the process output. If you haven’t already done so, you should read “How to use System.Diagnostics.Process correctly“. It outlines the major pitfalls of using this class. Another post on “Using the ProcessRunner class” will demonstrate using the helper class I wrote: CSharpTest.Net.Processes.ProcessRunner.
So again I find myself writing about using System.Diagnostics.Process and the Process.Start method. This time it’s not to provide any more information, but rather a simplified example. This example will build upon the previous two posts “How to search the environment’s path for an exe or dll“, and “How to correctly escape command line arguments” to use those methods for solving those needs. Otherwise this example is a relatively stand-alone method for running a process and capturing it’s output.
/// 
/// Runs the specified executable with the provided arguments and returns the process' exit code.
/// 
/// Recieves the output of either std/err or std/out
/// Provides the line-by-line input that will be written to std/in, null for empty
/// The executable to run, may be unqualified or contain environment variables
/// The list of unescaped arguments to provide to the executable
/// Returns process' exit code after the program exits
/// Raised when the exe was not found
/// Raised when one of the arguments is null
/// Raised if an argument contains '\0', '\r', or '\n'
public static int Run(Action output, TextReader input, string exe, params string[] args)
{
if (String.IsNullOrEmpty(exe))
throw new FileNotFoundException();
if (output == null)
throw new ArgumentNullException("output");
 
ProcessStartInfo psi = new ProcessStartInfo();
psi.UseShellExecute = false;
psi.RedirectStandardError = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.CreateNoWindow = true;
psi.ErrorDialog = false;
psi.WorkingDirectory = Environment.CurrentDirectory;
psi.FileName = FindExePath(exe); //see http://csharptest.net/?p=526
psi.Arguments = EscapeArguments(args); // see http://csharptest.net/?p=529
 
using (Process process = Process.Start(psi))
using (ManualResetEvent mreOut = new ManualResetEvent(false),
mreErr = new ManualResetEvent(false))
{
process.OutputDataReceived += (o, e) => { if (e.Data == null) mreOut.Set(); else output(e.Data); };
process.BeginOutputReadLine();
process.ErrorDataReceived += (o, e) => { if (e.Data == null) mreErr.Set(); else output(e.Data); };
process.BeginErrorReadLine();
 
string line;
while (input != null && null != (line = input.ReadLine()))
process.StandardInput.WriteLine(line);
 
process.StandardInput.Close();
process.WaitForExit();
 
mreOut.WaitOne();
mreErr.WaitOne();
return process.ExitCode;
}
}
Though most people won’t require writing to std::in, it is demonstrated here. If you do decide to remove this be careful that you still set psi.RedirectStandardInput to true, and call process.StandardInput.Close() before you call process.WaitForExit().
If you want to read a single character at a time (so as not to wait for a new line) that can also be done. Unfortunately it does require more work. The following sample class reads one character at a time and writes it to the console:
//In the above example you would remove the event subscription and the call to
//process.BeginOutputReadLine() and replace it with the following:
new ReadOutput(process.StandardInput, mreOut);
 
private class ReadOutput
{
private StreamReader _reader;
private ManualResetEvent _complete;
 
public ReadOutput(StreamReader reader, ManualResetEvent complete)
{
_reader = reader;
_complete = complete;
Thread t = new Thread(ReadAll);
t.Start();
}
 
void ReadAll()
{
int ch;
while(-1 != (ch = _reader.Read()))
{
Console.Write((char) ch);
}
_complete.Set();
}
}
Always remember you need two threads reading both std::out and std::err or you run into the possibility of hanging the process.

Tuesday, August 25, 2015

Resizing Image With quality or proportional

 

Resizing image with quality. 

Taken from http://stackoverflow.com/a/24199315/1247243 all credit belong to Mark


/// <summary>
/// Resize the image to the specified width and height.
/// </summary>
/// <param name="image">The image to resize.</param>
/// <param name="width">The width to resize to.</param>
/// <param name="height">The height to resize to.</param>
/// <returns>The resized image.</returns>
public static Bitmap ResizeImage(Image image, int width, int height)
{
    var destRect = new Rectangle(0, 0, width, height);
    var destImage = new Bitmap(width, height);

    destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

    using (var graphics = Graphics.FromImage(destImage))
    {
        graphics.CompositingMode = CompositingMode.SourceCopy;
        graphics.CompositingQuality = CompositingQuality.HighQuality;
        graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
        graphics.SmoothingMode = SmoothingMode.HighQuality;
        graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

        using (var wrapMode = new ImageAttributes())
        {
            wrapMode.SetWrapMode(WrapMode.TileFlipXY);
            graphics.DrawImage(image, destRect, 0, 0, image.Width,image.Height, GraphicsUnit.Pixel, wrapMode);
        }
    }

    return destImage;
}
  • wrapMode.SetWrapMode(WrapMode.TileFlipXY) prevents ghosting around the image borders -- naive resizing will sample transparent pixels beyond the image boundaries, but by mirroring the image we can get a better sample (this setting is very noticeable)
  • destImage.SetResolution maintains DPI regardless of physically size -- may increase quality when reducing image dimensions or when printing
  • Compositing controls how pixels are blended with the background -- might not be needed since we're only drawing one thing
  • InterpolationMode determines how intermediate values between two endpoints are calculated
  • SmoothingMode specifies whether lines, curves, and the edges of filled areas use smoothing (also called antialiasing) -- probably only works on vectors
  • PixelOffsetMode affects rendering quality when drawing the new image
Maintaining aspect ratio is left as an exercise for the reader (actually, I just don't think it's this function's job to do that for you).
Also, this is a good article describing some of the pitfalls with image resizing. The above function will cover most of them, but you still have to worry about saving.

Or using proportion resizing 


taken from http://stackoverflow.com/a/6501997/1247243 credit belong to Alex

Like this?
public static void Test()
{
    using (var image = Image.FromFile(@"c:\logo.png"))
    using (var newImage = ScaleImage(image, 300, 400))
    {
        newImage.Save(@"c:\test.png", ImageFormat.Png);
    }
}

public static Image ScaleImage(Image image, int maxWidth, int maxHeight)
{
    var ratioX = (double)maxWidth / image.Width;
    var ratioY = (double)maxHeight / image.Height;
    var ratio = Math.Min(ratioX, ratioY);

    var newWidth = (int)(image.Width * ratio);
    var newHeight = (int)(image.Height * ratio);

    var newImage = new Bitmap(newWidth, newHeight);

    using (var graphics = Graphics.FromImage(newImage))
        graphics.DrawImage(image, 0, 0, newWidth, newHeight);

    return newImage;
}

Saturday, August 8, 2015

OpenFileDialog and SaveFileDialog

 


 

Open file:

            OpenFileDialog opendialog = new OpenFileDialog();
            opendialog.Filter = "Text files|*.txt";
            opendialog.Title = "Import Settings";
            DialogResult result = opendialog.ShowDialog(); // Show the dialog.
            if (result == DialogResult.OK) // Test result.
            {
                //opendialog.FileName;
            }



Save File Dialog
            SaveFileDialog savedialog = new SaveFileDialog();
            savedialog.Filter = "Text files|*.txt";
            savedialog.Title = "Export Settings";
            DialogResult result = savedialog.ShowDialog(); // Show the dialog.
            if (result == DialogResult.OK) // Test result.
            {
               //savedialog.FileName;
            }

Friday, August 7, 2015

Snippet Import and Export setting in C#


Dont forget changing your name space according your own namespace.

Credit http://snipplr.com/view/24482/persisting-data-using-xml-config-files-in-winforms-saving-and-restoring-user-and-application-data/  taken as my own snippet




using System;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;


namespace TestingGround
{
    public static class SettingsIO
    {
        internal static void Import(string settingsFilePath)
        {
            if (!File.Exists(settingsFilePath))
            {
                throw new FileNotFoundException();
            }

            var appSettings = Properties.Settings.Default;
            try
            {
                var config =
    ConfigurationManager.OpenExeConfiguration(
    ConfigurationUserLevel.PerUserRoamingAndLocal);

                string appSettingsXmlName =
   Properties.Settings.Default.Context["GroupName"].ToString();
                // returns "MyApplication.Properties.Settings";

                // Open settings file as XML
                var import = XDocument.Load(settingsFilePath);
                // Get the whole XML inside the settings node
                var settings = import.XPathSelectElements("//" + appSettingsXmlName);

                config.GetSectionGroup("userSettings")
                    .Sections[appSettingsXmlName]
                    .SectionInformation
                    .SetRawXml(settings.Single().ToString());
                config.Save(ConfigurationSaveMode.Modified);
                ConfigurationManager.RefreshSection("userSettings");

                appSettings.Reload();
            }
            catch (Exception) // Should make this more specific
            {
                // Could not import settings.
                appSettings.Reload(); // from last set saved, not defaults
            }
        }

        internal static void Export(string settingsFilePath)
        {
            Properties.Settings.Default.Save();
            var config =
    ConfigurationManager.OpenExeConfiguration(
    ConfigurationUserLevel.PerUserRoamingAndLocal);
            config.SaveAs(settingsFilePath);
        }
    }
}

 OOT

Or you can using this one. https://github.com/crdx/PortableSettingsProvider , this will save your setting in YOURAPPNAME.settings but little bit work. :) . This more portable like inifile in delphi or pascal.

Good luck

Tuesday, August 4, 2015

MessageBox in top other application

MessageBox in top other application for warning....


MessageBox.Show(new Form() { TopMost = true }, "You have not inputted a username or password. Would you like to configure your settings now?",
                 "Settings Needed",
                 MessageBoxButtons.YesNo,
                 MessageBoxIcon.Question);
or

MessageBox.Show(this,
                "You have not inputted a username or password. Would you like to configure your settings now?",
                "Settings Needed",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question
                MessageBoxDefaultButton.Button1,  // specify "Yes" as the default
                (MessageBoxOptions)0x40000);      // specify MB_TOPMOST
another way using class...



static public class TopMostMessageBox
{
    static public DialogResult Show(string message)
    {
        return Show(message, string.Empty, MessageBoxButtons.OK);
    }

    static public DialogResult Show(string message, string title)
    {
        return Show(message, title, MessageBoxButtons.OK);
    }

    static public DialogResult Show(string message, string title, 
        MessageBoxButtons buttons)
    {
        // Create a host form that is a TopMost window which will be the 
        // parent of the MessageBox.
        Form topmostForm = new Form();
        // We do not want anyone to see this window so position it off the 
        // visible screen and make it as small as possible
        topmostForm.Size = new System.Drawing.Size(1, 1);
        topmostForm.StartPosition = FormStartPosition.Manual;
        System.Drawing.Rectangle rect = SystemInformation.VirtualScreen;
        topmostForm.Location = new System.Drawing.Point(rect.Bottom + 10, 
            rect.Right + 10);
        topmostForm.Show();
        // Make this form the active form and make it TopMost
        topmostForm.Focus();
        topmostForm.BringToFront();
        topmostForm.TopMost = true;
        // Finally show the MessageBox with the form just created as its owner
        DialogResult result = MessageBox.Show(topmostForm, message, title, 
            buttons);
        topmostForm.Dispose(); // clean it up all the way

        return result;
    }
} 
Called like this


TopMostMessageBox.Show(
    "This will appear in a message box that is a topmost window",
    "Title", MessageBoxButtons.AbortRetryIgnore);

Friday, July 31, 2015

Blogspot ( Blogger URL parser ) Tools



Example : http://test.blogspot.com/atom.xml?redirect=false&start-index=1&max-results=3

just insert url and parsing url....
 After scraping url you can parsing the data using HTML SCRAPER

 Download below:
 http://www.mediafire.com/download/pz9i1ae9980u5b9/Blogger_urlParser.zip


NET framework 4

Right Click menu in richtextbox in C#



Add this code to your event....
        private void richTextBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                ContextMenu contextMenu = new System.Windows.Forms.ContextMenu();
                MenuItem menuItem = new MenuItem("Cut");
                menuItem.Click += new EventHandler(CutAction);
                contextMenu.MenuItems.Add(menuItem);
                menuItem = new MenuItem("Copy");
                menuItem.Click += new EventHandler(CopyAction);
                contextMenu.MenuItems.Add(menuItem);
                menuItem = new MenuItem("Paste");
                menuItem.Click += new EventHandler(PasteAction);
                contextMenu.MenuItems.Add(menuItem);
                menuItem = new MenuItem("Select All");
                menuItem.Click += new EventHandler(SelectAll);
                contextMenu.MenuItems.Add(menuItem);
                richTextBox1.ContextMenu = contextMenu;
            }
        }

        void CutAction(object sender, EventArgs e)
        {
            richTextBox1.Cut();
        }

        void CopyAction(object sender, EventArgs e)
        {
            Clipboard.SetText(richTextBox1.SelectedText);
        }

        void PasteAction(object sender, EventArgs e)
        {
            if (Clipboard.ContainsText())
            {
                richTextBox1.Text += Clipboard.GetText(TextDataFormat.Text).ToString();
            }
        }

        void SelectAll(object sender, EventArgs e)
        {
           richTextBox1.SelectAll();
           richTextBox1.Focus();
        }

Thursday, June 11, 2015

MessageBox using Winform and richtextbox

private void ShowRichMessageBox(string title, string message)
        {
            RichTextBox rtbMessage = new RichTextBox();
            rtbMessage.Text = message;
            rtbMessage.Dock = DockStyle.Fill;
            rtbMessage.ReadOnly = true;
            rtbMessage.BorderStyle = BorderStyle.FixedSingle;

            Form RichMessageBox = new Form();
            RichMessageBox.Text = title;
            RichMessageBox.StartPosition = FormStartPosition.CenterScreen;
            RichMessageBox.MaximizeBox = false;
            RichMessageBox.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
            RichMessageBox.Size = new Size(300, 250);

            RichMessageBox.Controls.Add(rtbMessage);
            RichMessageBox.ShowDialog();
        }

Getting WebException with full response body

Di load langsung...
   try
        {
            WebClient client = new WebClient();
            client.Encoding = Encoding.UTF8;
            string content = client.DownloadString("https://sandiegodata.atlassian.net/wiki/pages/doaddcomment.action?pageId=524365");
            Console.WriteLine(content);
            Console.ReadKey();
        }
        catch (WebException erro)
        {
            string webpageContent  = ""; 
            if( erro.Response == null )
                  {
                   webpageContent = "Error:"+erro.Status;
                  }
                  else if( erro.Response != null)
                  {
                    var resp = new StreamReader(erro.Response.GetResponseStream()).ReadToEnd();
                   
                }
                
        }
with json.net
  catch (WebException ex)
        {
if( a.Response == null )
                  {
                   webpageContent = "Error:"+a.Status;
                  }
                  else if( a.Response != null)
                  {
                var resp = new StreamReader(a.Response.GetResponseStream()).ReadToEnd();

                dynamic obj = JsonConvert.DeserializeObject(resp);
                var messageFromServer = obj.error.message;
                var codemessage = obj.error.code;
                string mesageex = a.Message;
                webpageContent = "Error:"+codemessage.Value + ":" + messageFromServer.Value;
                  }
}

Saturday, June 6, 2015

Killing child process when parent closing, OTHERWAY

taken from http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net

http://stackoverflow.com/questions/3342941/kill-child-process-when-parent-process-is-killed/4657392#4657392
http://stackoverflow.com/questions/14955045/assignprocesstojobobject-dont-assign-properly
http://www.xtremevbtalk.com/showthread.php?p=1335552#post1335552

Working example:
https://www.add-in-express.com/creating-addins-blog/2013/11/05/release-excel-com-objects/

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace JobManagement
{
    public class Job : IDisposable
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
        static extern IntPtr CreateJobObject(IntPtr a, string lpName);

        [DllImport("kernel32.dll")]
        static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool CloseHandle(IntPtr hObject);

        private IntPtr handle;
        private bool disposed;

        public Job()
        {
            handle = CreateJobObject(IntPtr.Zero, null);

            var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
            {
                LimitFlags = 0x2000
            };

            var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
            {
                BasicLimitInformation = info
            };

            int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
            IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length);
            Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);

            if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length))
                throw new Exception(string.Format("Unable to set information.  Error: {0}", Marshal.GetLastWin32Error()));
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (disposed)
                return;

            if (disposing) { }

            Close();
            disposed = true;
        }

        public void Close()
        {
            CloseHandle(handle);
            handle = IntPtr.Zero;
        }

        public bool AddProcess(IntPtr processHandle)
        {
            return AssignProcessToJobObject(handle, processHandle);
        }

        public bool AddProcess(int processId)
        {
            return AddProcess(Process.GetProcessById(processId).Handle);
        }

    }

    #region Helper classes

    [StructLayout(LayoutKind.Sequential)]
    struct IO_COUNTERS
    {
        public UInt64 ReadOperationCount;
        public UInt64 WriteOperationCount;
        public UInt64 OtherOperationCount;
        public UInt64 ReadTransferCount;
        public UInt64 WriteTransferCount;
        public UInt64 OtherTransferCount;
    }


    [StructLayout(LayoutKind.Sequential)]
    struct JOBOBJECT_BASIC_LIMIT_INFORMATION
    {
        public Int64 PerProcessUserTimeLimit;
        public Int64 PerJobUserTimeLimit;
        public UInt32 LimitFlags;
        public UIntPtr MinimumWorkingSetSize;
        public UIntPtr MaximumWorkingSetSize;
        public UInt32 ActiveProcessLimit;
        public UIntPtr Affinity;
        public UInt32 PriorityClass;
        public UInt32 SchedulingClass;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SECURITY_ATTRIBUTES
    {
        public UInt32 nLength;
        public IntPtr lpSecurityDescriptor;
        public Int32 bInheritHandle;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
    {
        public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
        public IO_COUNTERS IoInfo;
        public UIntPtr ProcessMemoryLimit;
        public UIntPtr JobMemoryLimit;
        public UIntPtr PeakProcessMemoryUsed;
        public UIntPtr PeakJobMemoryUsed;
    }

    public enum JobObjectInfoType
    {
        AssociateCompletionPortInformation = 7,
        BasicLimitInformation = 2,
        BasicUIRestrictions = 4,
        EndOfJobTimeInformation = 6,
        ExtendedLimitInformation = 9,
        SecurityLimitInformation = 5,
        GroupInformation = 11
    }

    #endregion

}

Killing child process when parent software killed.

You need add using System.Management;

 
 /// 
           /// Kill a process, and all of its children, grandchildren, etc.
           /// 
           /// Process ID.
           private static void KillProcessAndChildren(int pid)
           {
               ManagementObjectSearcher searcher = new ManagementObjectSearcher
                 ("Select * From Win32_Process Where ParentProcessID=" + pid);
               ManagementObjectCollection moc = searcher.Get();
               foreach (ManagementObject mo in moc)
               {
                   KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
               }
               try
               {
                   Process proc = Process.GetProcessById(pid);
                   proc.Kill();
               }
               catch (ArgumentException)
               {
                   // Process already exited.
               }
           } 

Sample using it declare first, so we can access it everywhere

 private int idkita;
Button CLICK
private void button1_Click(object sender, EventArgs e)
           {

               ProcessStartInfo startInfo = new ProcessStartInfo();
               //startInfo.WindowStyle = ProcessWindowStyle.Hidden;
               Process ieProcess = Process.Start(@"notepad.exe", "");
               idkita = ieProcess.Id;
}
Add to form closing FORM CLOSING
  private void Form1_FormClosing(object sender, FormClosingEventArgs e)
           {
               KillProcessAndChildren(idkita);
           }

Thursday, May 7, 2015

Tackling timeout issues when uploading large files with HttpWebRequest

Taking from http://www.thomaslevesque.com/2014/01/14/tackling-timeout-issues-when-uploading-large-files-with-httpwebrequest/  as my personal NOTE . All credit belong to

 
If you ever had to upload large volumes of data over HTTP, you probably ran into timeout issues. The default Timeout value for HttpWebRequest is 100 seconds, which means that if it takes more than that from the time you send the request headers to the time you receive the response headers, your request will fail. Obviously, if you’re uploading a large file, you need to increase that timeout… but to which value?
If you know the available bandwidth, you could calculate a rough estimate of how long it should take to upload the file, but it’s not very reliable, because if there is some network congestion, it will take longer, and your request will fail even though it could have succeeded given enough time. So, should you set the timeout to a very large value, like several hours, or even Timeout.Infinite? Probably not. The most compelling reason is that even though the transfer itself could take hours, some phases of the exchange shouldn’t take that long. Let’s decompose the phases of an HTTP upload:




Obtaining the request stream or getting the response (orange parts) isn’t supposed to take very long, so obviously we need a rather short timeout there (the default value of 100 seconds seems reasonable). But sending the request body (blue part) could take much longer, and there is no reliable way  to decide how long that should be; as long as we keep sending data and the server is receiving it, there is no reason not to continue, even if it’s taking hours. So we actually don’t want a timeout at all there! Unfortunately, the behavior of the Timeout property is to consider everything from the call to GetRequestStream to the return of GetResponse
In my opinion, it’s a design flaw of the HttpWebRequest class, and one that has bothered me for a very long time. So I eventually came up with a solution. It relies on the fact that the asynchronous versions of GetRequestStream and GetResponse don’t have a timeout mechanism. Here’s what the documentation says:
The Timeout property has no effect on asynchronous requests made with the BeginGetResponse or BeginGetRequestStream method.
In the case of asynchronous requests, the client application implements its own time-out mechanism. Refer to the example in the BeginGetResponse method.
So, a solution could be to to use these methods directly (or the new Task-based versions: GetRequestStreamAsync and GetResponseAsync); but more often than not, you already have an existing code base that uses the synchronous methods, and changing the code to make it fully asynchronous is usually not trivial. So, the easy approach is to create synchronous wrappers around BeginGetRequestStream and BeginGetResponse, with a way to specify a timeout for these operations:
    public static class WebRequestExtensions
    {
        public static Stream GetRequestStreamWithTimeout(
            this WebRequest request,
            int? millisecondsTimeout = null)
        {
            return AsyncToSyncWithTimeout(
                request.BeginGetRequestStream,
                request.EndGetRequestStream,
                millisecondsTimeout ?? request.Timeout);
        }

        public static WebResponse GetResponseWithTimeout(
            this HttpWebRequest request,
            int? millisecondsTimeout = null)
        {
            return AsyncToSyncWithTimeout(
                request.BeginGetResponse,
                request.EndGetResponse,
                millisecondsTimeout ?? request.Timeout);
        }

        private static T AsyncToSyncWithTimeout<T>(
            Func<AsyncCallback, object, IAsyncResult> begin,
            Func<IAsyncResult, T> end,
            int millisecondsTimeout)
        {
            var iar = begin(null, null);
            if (!iar.AsyncWaitHandle.WaitOne(millisecondsTimeout))
            {
                var ex = new TimeoutException();
                throw new WebException(ex.Message, ex, WebExceptionStatus.Timeout, null);
            }
            return end(iar);
        }
    }
(note that I used the Begin/End methods rather than the Async methods, in order to keep compatibility with older versions of .NET)
These extension methods can be used instead of GetRequestStream and GetResponse; each of them will timeout if they take too long, but once you have the request stream, you can take as long as you want to upload the data. Note that the stream itself has its own read and write timeout (5 minutes by default), so if 5 minutes go by without any data being uploaded, the Write method will cause an exception. Here is the new upload scenario using these methods:



As you can see, the only difference is that the timeout doesn’t apply anymore to the transfer of the request body, but only to obtaining the request stream and getting the response. Here’s a full example that corresponds to the scenario above:
long UploadFile(string path, string url, string contentType)
{
    // Build request
    var request = (HttpWebRequest)WebRequest.Create(url);
    request.Method = WebRequestMethods.Http.Post;
    request.AllowWriteStreamBuffering = false;
    request.ContentType = contentType;
    string fileName = Path.GetFileName(path);
    request.Headers["Content-Disposition"] = string.Format("attachment; filename=\"{0}\"", fileName);
    
    try
    {
        // Open source file
        using (var fileStream = File.OpenRead(path))
        {
            // Set content length based on source file length
            request.ContentLength = fileStream.Length;
            
            // Get the request stream with the default timeout
            using (var requestStream = request.GetRequestStreamWithTimeout())
            {
                // Upload the file with no timeout
                fileStream.CopyTo(requestStream);
            }
        }
        
        // Get response with the default timeout, and parse the response body
        using (var response = request.GetResponseWithTimeout())
        using (var responseStream = response.GetResponseStream())
        using (var reader = new StreamReader(responseStream))
        {
            string json = reader.ReadToEnd();
            var j = JObject.Parse(json);
            return j.Value<long>("Id");
        }
    }
    catch (WebException ex)
    {
        if (ex.Status == WebExceptionStatus.Timeout)
        {
            LogError(ex, "Timeout while uploading '{0}'", fileName);
        }
        else
        {
            LogError(ex, "Error while uploading '{0}'", fileName);
        }
        throw;
    }
}
I hope you will find this helpful!

Thursday, April 16, 2015

TvMuse Jadwal Scraper


Project iseng, gara gara tiap hari stress di gangguin "seseorang". -_- . Demi ketenangan dalam dunia persilatan maka terpaksa di buatkan tvmuse jadwal scraper.

Main software

Settingnya
Csv Filenya



perlu NET40 ( di buat cepet dan nggak ribet )


http://www.mediafire.com/download/v8a05aj1r86lkdf/TvMuse_v1.0.zip
update, plus scraping dan download imagenya langsung. Ben nggak ribet

http://www.mediafire.com/download/1gqgl9lc3rwwfz2/TvMuse_v1.1.zip


- solved error jika nggak ada idnya di tmbd
- auto download image dan kalau nggak ada di copyiin file no image
http://www.mediafire.com/download/m6xzwkdmr5m7o01/TvMuse_v1.2.zip


- update scraping with range  :D
- Bentuk csvnya ada image pathnya 


http://www.mediafire.com/download/dd8wo0qaafwpap2/TvMuse_v1.3.rar