You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

parse.go 3.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. package classic
  2. import (
  3. "github.com/PuerkitoBio/goquery"
  4. "errors"
  5. "strconv"
  6. "time"
  7. "github.com/terorie/youtube-mango/data"
  8. )
  9. const likeBtnSelector = ".like-button-renderer-like-button-unclicked"
  10. const dislikeBtnSelector = ".like-button-renderer-dislike-button-unclicked"
  11. const viewCountSelector = "div .watch-view-count"
  12. const userInfoSelector = "div .yt-user-info"
  13. const channelNameSelector = ".yt-uix-sessionlink"
  14. type parseInfo struct {
  15. v *data.Video
  16. doc *goquery.Document
  17. }
  18. func (p *parseInfo) parse() error {
  19. if err := p.parseLikeDislike();
  20. err != nil { return err }
  21. if err := p.parseViewCount();
  22. err != nil { return err }
  23. if err := p.parseUploader();
  24. err != nil { return err }
  25. if err := p.parseDescription();
  26. err != nil { return err }
  27. p.parseMetas()
  28. return nil
  29. }
  30. func (p *parseInfo) parseLikeDislike() error {
  31. likeText := p.doc.Find(likeBtnSelector).First().Text()
  32. dislikeText := p.doc.Find(dislikeBtnSelector).First().Text()
  33. if len(likeText) == 0 || len(dislikeText) == 0 {
  34. return errors.New("failed to parse like buttons")
  35. }
  36. var err error
  37. p.v.Likes, err = extractNumber(likeText)
  38. if err != nil { return err }
  39. p.v.Dislikes, err = extractNumber(dislikeText)
  40. if err != nil { return err }
  41. return nil
  42. }
  43. func (p *parseInfo) parseViewCount() error {
  44. viewCountText := p.doc.Find(viewCountSelector).First().Text()
  45. viewCount, err := extractNumber(viewCountText)
  46. if err != nil { return err }
  47. p.v.Views = viewCount
  48. return nil
  49. }
  50. func (p *parseInfo) parseUploader() error {
  51. userInfo := p.doc.Find(userInfoSelector)
  52. userLinkNode := userInfo.Find(".yt-uix-sessionlink")
  53. // get link
  54. userLink, _ := userLinkNode.Attr("href")
  55. if userLink == "" { return errors.New("couldn't find channel link") }
  56. p.v.UploaderURL = "https://www.youtube.com" + userLink
  57. // get name
  58. channelName := userInfo.Find(channelNameSelector).Text()
  59. if channelName == "" { return errors.New("could not find channel name") }
  60. p.v.Uploader = channelName
  61. return nil
  62. }
  63. func (p *parseInfo) parseMetas() {
  64. metas := p.doc.Find("meta")
  65. // For each <meta>
  66. for _, node := range metas.Nodes {
  67. // Attributes
  68. var content string
  69. var itemprop string
  70. var prop string
  71. // Parse attributes
  72. for _, attr := range node.Attr {
  73. switch attr.Key {
  74. case "property": prop = attr.Val
  75. case "itemprop": itemprop = attr.Val
  76. case "content": content = attr.Val
  77. }
  78. }
  79. // Content not set
  80. if len(content) == 0 {
  81. continue
  82. }
  83. // <meta property …
  84. if len(prop) != 0 {
  85. switch prop {
  86. case "og:title":
  87. p.v.Title = content
  88. case "og:video:tag":
  89. p.v.Tags = append(p.v.Tags, content)
  90. case "og:url":
  91. p.v.URL = content
  92. case "og:image":
  93. p.v.Thumbnail = content
  94. }
  95. continue
  96. }
  97. // <meta itemprop …
  98. if len(itemprop) != 0 {
  99. switch itemprop {
  100. case "datePublished":
  101. if val, err := time.Parse("2006-01-02", content);
  102. err == nil { p.v.UploadDate = val }
  103. case "genre":
  104. p.v.Genre = content
  105. case "channelId":
  106. p.v.UploaderID = content
  107. case "duration":
  108. if val, err := parseDuration(content);
  109. err == nil { p.v.Duration = val }
  110. case "isFamilyFriendly":
  111. if val, err := strconv.ParseBool(content);
  112. err == nil { p.v.FamilyFriendly = val }
  113. }
  114. continue
  115. }
  116. }
  117. }