UtubeDownloader

    • C#
    • .NET (FX) 4.5–4.8

    Es gibt 32 Antworten in diesem Thema. Der letzte Beitrag () ist von C-Sharp.

      UtubeDownloader

      anbei eine Überarbeitung eines sehr listigen Werkes von @AliveDevil. :thumbsup: :thumbsup:

      C#-Quellcode

      1. /*die utube-api returnt "String-Tables" - damit meine ich das format: "name0=value0&name1=value1&name2=value2" usw. Und das verschachtelt sie sogar - die eingeschachtelten Angaben sind aber url-encodet, müssen erstmal decodet werden.
      2. Also der string-table-eintrag für 'url_encoded_fmt_stream_map' ist eine komma-getrennte Liste weiterer stringTables, jede mit Angaben über einen downloadbaren streams. Aus diesen werden letztlich die StreamInfos generiert.
      3. Die StreamInfo-Klasse ist bischen ein Hack - ihre Member heissen nämlich wie die Bezeichner der stringTable-Einträge. Auf diese Weise kann man per Reflection mit nur einem Befehl verschiedene Einträge direkt verschiedenen Properties zuweisen, ohne ein Mapping implementieren zu müssen. Die Idee dazu entstammt auch AliveDevils Vorlage.
      4. */
      5. namespace UtubeDownloader {
      6. class Program {
      7. static void Main(string[] args) {
      8. Console.WriteLine("Video ID");
      9. var id = Console.ReadLine();
      10. id = "eMVNjMF1Suo";
      11. var client = new WebClient();
      12. var raw_video_info = client.DownloadString(new Uri("http://youtube.com/get_video_info?&video_id=" + id));
      13. var rawAllData = ToStringTable(raw_video_info).ToDictionary(entry => entry[0], entry => entry[1]);
      14. var rawStream_map = HttpUtility.UrlDecode(rawAllData["url_encoded_fmt_stream_map"]);
      15. var streamInfos = rawStream_map.Split(',').Select(data => StreamInfo.FromStringTable(ToStringTable(data))).ToArray();
      16. var index = 0;
      17. Console.WriteLine(string.Join("\n", streamInfos.Select(item => (index++).ToString() + ") " + item.Type + " " + item.Quality)));
      18. Console.WriteLine("Choose an Index");
      19. if (int.TryParse(Console.ReadLine(), out index)) {
      20. var info = streamInfos[index];
      21. var fileName = ValidFileName(HttpUtility.UrlDecode(rawAllData["title"]));
      22. var fileType = info.FileType;
      23. client.DownloadFile(info.DownloadUrl, fileName + "." + fileType);
      24. }
      25. }
      26. private static IEnumerable<string[]> ToStringTable(string s) { return s.Split('&').Select(entry => entry.Split('=')); }
      27. private static string ValidFileName(string s) { return string.Join("-", s.Split(Path.GetInvalidFileNameChars(), StringSplitOptions.None)); }
      28. }
      29. public class StreamInfo {
      30. private string url;
      31. private string type;
      32. public static StreamInfo FromStringTable(IEnumerable<string[]> table) {
      33. StreamInfo info = new StreamInfo();
      34. Type infoType = typeof(StreamInfo);
      35. var properties = infoType.GetProperties().ToDictionary(item => item.Name.ToLower());
      36. foreach (var entry in table) {
      37. properties[entry[0].ToLower()].SetValue(info, Convert.ChangeType(entry[1], properties[entry[0].ToLower()].PropertyType));
      38. }
      39. return info;
      40. }
      41. public string Url {
      42. get { return url; }
      43. set { url = HttpUtility.UrlDecode(value); }
      44. }
      45. public string Fallback_Host { get; set; }
      46. public int ITag { get; set; }
      47. public string Quality { get; set; }
      48. public string Sig { get; set; }
      49. public string Type { get { return type; } set { type = HttpUtility.UrlDecode(value); } }
      50. public string FileType { get { return Type.Split(';')[0].Split('/')[1].Replace("x-", ""); } }
      51. public string DownloadUrl { get { return string.Format("{0}&fallback_host={1}&signature={2}", Url, Fallback_Host, Sig); } }
      52. }
      53. }
      Verwendet wird ein utube-api get_video_info - die eine Video-Id verlangt - (zeile #15)
      zeile#13 ist übrigens eine hardcodet Id, weil ich zu faul bin, bei jedem Testlauf eine gültige Id einzugeben - also die Zeile ist zu löschen, wenn man wirklich eine Id eingeben möchte.

      Jo, das Parsen des Video-Infos ist ziemlich krank und krass, das kann ich nicht mehr erklären als im Datei-Kommentar ja bereits steht - sorry :( .
      Ums zu blicken muss man wohl debuggen, und sich raw_video_info, rawAllData, rawStream_map, streamInfos im LokalFenster angugge, und auch mal Einträge in einen Editor umkopieren, weil manche der Strings sind lang und inhaltsreich.


      Edit 9-2015: vb-Übersetzung zugefügt
      Dateien

      Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „ErfinderDesRades“ ()

      Macht auf jeden Fall was es soll. Allerdings sind die Qualitäten ein bisschen Kurios. Bei meinem Testvideo sind 144p, 240p, 360p, 480p, 720p, 1080p vorhanden. Dennoch bekomme ich nur diese Auswahl (nur item.Quality):



      Wie verteilen sich die anderen Qualitäten?

      5 -> 144p, 4 -> 240p, 3 -> 360p, 2 -> 480p, 1 -> ?720p?, 0 -> ?1080p?

      Irgendwie verwundert mich das sehr.. Hab natürlich meine ID angepasst.

      nafets schrieb:

      Was soll das hier werden?

      C#-Quellcode

      1. var id = Console.ReadLine();
      2. id = "eMVNjMF1Suo";


      zeile#13 ist übrigens eine hardcodet Id, weil ich zu faul bin, bei jedem Testlauf eine gültige Id einzugeben - also die Zeile ist zu löschen, wenn man wirklich eine Id eingeben möchte.



      Autsch, in die ich lese nicht Falle geraten :D (Natürlich nur Spaß)

      ThuCommix schrieb:

      Wie verteilen sich die anderen Qualitäten?

      5 -> 144p, 4 -> 240p, 3 -> 360p, 2 -> 480p, 1 -> ?720p?, 0 -> ?1080p?

      Irgendwie verwundert mich das sehr.. Hab natürlich meine ID angepasst.


      kann man das nicht ergooglen? Ich mein ich kenn mich nicht aus mit so utube-sachen, aber vielleicht findet man ja was.
      er (@'ErfinderDesRades') schreibt ja was von ner API.

      EDIT:
      die üblichen 2 Minuten Google :B (bzw. das ist für die JS-API, wird aber für die hier benutzte wahrscheinlich ähnlich sein(wie gesagt kenn mich da nicht aus ;) ))
      Ich möchte an dieser Stelle noch auf ein interessantes Projekt hinweisen - den YoutubeExtractor.
      Man kann sich das Projekt auf GitHub herunterladen oder noch besser direkt im VisualStudio als NuGet-Paket automatisch installieren lassen (NuGet Konsole: Install-Package YoutubeExtractor).
      Damit ist auch Full-HD kein Problem.

      Hab mal schnell zum Test etwas zusammengefummelt:
      Warum funktioniert das nur bei deiner Beispiel-ID und manchen anderen YT-Videos, aber längst nicht bei allen?
      Bei sehr vielen Videos erhalte ich den Fehler:
      An unhandled exception of type 'System.Collections.Generic.KeyNotFoundException' occurred in mscorlib.dll

      Additional information: Der angegebene Schlüssel war nicht im Wörterbuch angegeben

      Liegt es an mir?

      LG

      simpelSoft schrieb:

      Ich möchte an dieser Stelle noch auf ein interessantes Projekt hinweisen - den YoutubeExtractor.
      Man kann sich das Projekt auf GitHub herunterladen oder noch besser direkt im VisualStudio als NuGet-Paket automatisch installieren lassen (NuGet Konsole: Install-Package YoutubeExtractor).
      Damit ist auch Full-HD kein Problem.

      Hab mal schnell zum Test etwas zusammengefummelt:
      vb-paradise.de/index.php/Attac…ac001453c60d1badab00b59e9


      Ich bin sicher nur zu blöd dazu, aber ich verstehe nicht, wie man die videoInfos behält oder zurückerhält, wenn man in der Liste navigiert. Also wie weißt Du, welches File Du downloaden willst?

      Dksksm schrieb:

      aber ich verstehe nicht, wie man die videoInfos behält oder zurückerhält, wenn man in der Liste navigiert. Also wie weißt Du, welches File Du downloaden willst?

      Z.B. über den Index Deiner gewählten Zeile - in diesem Beispiel ist es ein ListView: VideoInfo video = videoInfos.ElementAt(listViewVideoInfo.SelectedItems[0].Index);

      Anbei das gesamte Projekt zum Spielen.
      Bedenke - alles nur schnell zusammen-gehauen, aber funktioniert - bislang keine Fehlerbehandlung - kannst es ja komplettieren :) .
      Spoiler anzeigen

      C#-Quellcode

      1. using System;
      2. using System.Collections.Generic;
      3. using System.IO;
      4. using System.Linq;
      5. using System.Windows.Forms;
      6. using YoutubeExtractor;
      7. namespace YouTube
      8. {
      9. public partial class frmMain : Form
      10. {
      11. IEnumerable<VideoInfo> videoInfos;
      12. public frmMain()
      13. {
      14. InitializeComponent();
      15. }
      16. private void buttonGetInfos_Click(object sender, EventArgs e)
      17. {
      18. // Youtube link
      19. string link = textBoxUrl.Text;
      20. // Get the available video formats
      21. videoInfos = DownloadUrlResolver.GetDownloadUrls(link);
      22. // Fill the ListView
      23. foreach (var item in videoInfos)
      24. {
      25. ListViewItem i = new ListViewItem();
      26. i.Tag = item.Resolution;
      27. i.Text = item.Resolution.ToString();
      28. i.SubItems.Add(item.VideoType.ToString());
      29. i.SubItems.Add(item.VideoExtension);
      30. i.SubItems.Add(item.Title);
      31. listViewVideoInfo.Items.Add(i);
      32. }
      33. // Expand last column
      34. listViewVideoInfo.Columns[listViewVideoInfo.Columns.Count - 1].Width = -2;
      35. }
      36. private void listViewVideoInfo_SelectedIndexChanged(object sender, EventArgs e)
      37. {
      38. if (listViewVideoInfo.SelectedItems.Count == 0)
      39. buttonGetVideo.Enabled = false;
      40. else
      41. buttonGetVideo.Enabled = true;
      42. }
      43. private void buttonGetVideo_Click(object sender, EventArgs e)
      44. {
      45. // Get VideoInfos from slected row
      46. VideoInfo video = videoInfos.ElementAt(listViewVideoInfo.SelectedItems[0].Index);
      47. // If the video has a decrypted signature, decipher it
      48. if (video.RequiresDecryption)
      49. DownloadUrlResolver.DecryptDownloadUrl(video);
      50. // New VideoDownloader & Downloadprogress
      51. var videoDownloader = new VideoDownloader(video, Path.Combine("E:/", video.Title + video.VideoExtension));
      52. videoDownloader.DownloadProgressChanged += downloader_DownloadProgressChanged;
      53. videoDownloader.Execute();
      54. }
      55. private void downloader_DownloadProgressChanged(object sender, ProgressEventArgs e)
      56. {
      57. progressBarDownload.Value = (int)(e.ProgressPercentage);
      58. }
      59. }
      60. }

      Dateien
      • YouTube.zip

        (3,43 MB, 407 mal heruntergeladen, zuletzt: )

      simpelSoft schrieb:

      Dksksm schrieb:

      aber ich verstehe nicht, wie man die videoInfos behält oder zurückerhält, wenn man in der Liste navigiert. Also wie weißt Du, welches File Du downloaden willst?

      Z.B. über den Index Deiner gewählten Zeile - in diesem Beispiel ist es ein ListView: VideoInfo video = videoInfos.ElementAt(listViewVideoInfo.SelectedItems[0].Index);


      Ich hatte es mir wesentlich schwerer gemacht und ein Dataset mit Bindigssource und DGV gebaut. Die Bindigssource wird dann noch gefiltert und sortiert, so dass ich vermutlich nicht einfach über den Index gehen kann, es sei denn ich bau mir den (als ID) auch manuell auf. Ich werde damit noch etwas probieren. Mein Ansatz funktioniert zwar, aber mir gefiele es besser, ich könnte das Objekt "VideoInfo" selbst speichern.
      Dazu bin ich allerdings nicht in der Lage :(

      Danke für Deine Unterstützung :)
      Mann muss auswerten wo die Soruce des Videos liegt und dann dort den Download Inizialisieren!