20
vendor/github.com/keighl/metabolize/.editorconfig
generated
vendored
Normal file
20
vendor/github.com/keighl/metabolize/.editorconfig
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
root = true
|
||||
|
||||
; Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
|
||||
; Golang
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
; YAML
|
||||
[*.{yaml,yml}]
|
||||
indent_size = 2
|
||||
2
vendor/github.com/keighl/metabolize/.gitignore
generated
vendored
Normal file
2
vendor/github.com/keighl/metabolize/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.DS_Store
|
||||
*.out
|
||||
12
vendor/github.com/keighl/metabolize/.travis.yml
generated
vendored
Normal file
12
vendor/github.com/keighl/metabolize/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.4
|
||||
- tip
|
||||
|
||||
before_install:
|
||||
- go get github.com/axw/gocov/gocov
|
||||
- go get github.com/mattn/goveralls
|
||||
- if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
|
||||
script:
|
||||
- $HOME/gopath/bin/goveralls -service=travis-ci
|
||||
8
vendor/github.com/keighl/metabolize/CHANGELOG.md
generated
vendored
Normal file
8
vendor/github.com/keighl/metabolize/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## 0.1 - 2015-09-08
|
||||
|
||||
* Initial release
|
||||
|
||||
14
vendor/github.com/keighl/metabolize/LICENSE
generated
vendored
Normal file
14
vendor/github.com/keighl/metabolize/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
Copyright (c) 2015 keighl.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms are permitted
|
||||
provided that the above copyright notice and this paragraph are
|
||||
duplicated in all such forms and that any documentation,
|
||||
advertising materials, and other materials related to such
|
||||
distribution and use acknowledge that the software was developed
|
||||
by keighl. The name of the
|
||||
keighl may not be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
82
vendor/github.com/keighl/metabolize/README.md
generated
vendored
Normal file
82
vendor/github.com/keighl/metabolize/README.md
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
# Metabolize
|
||||
|
||||
[](https://travis-ci.org/keighl/metabolize) [](https://coveralls.io/r/keighl/metabolize)
|
||||
|
||||
Decodes HTML <meta> values into a Golang struct. Great for quickly grabbing [open graph](http://ogp.me/) data.
|
||||
|
||||
### Installation
|
||||
|
||||
go get -u github.com/keighl/metabolize
|
||||
|
||||
### Usage
|
||||
|
||||
Use `meta:"xxx"` tags on your struct to tell metabolize how to decode metadata from an HTML document.
|
||||
|
||||
```go
|
||||
type MetaData struct {
|
||||
Title string `meta:"og:title"`
|
||||
// If no `og:description`, will fall back to `description`
|
||||
Description string `meta:"og:description,description"`
|
||||
}
|
||||
```
|
||||
|
||||
Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
m "github.com/keighl/metabolize"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type MetaData struct {
|
||||
Title string `meta:"og:title"`
|
||||
Description string `meta:"og:description,description"`
|
||||
Type string `meta:"og:type"`
|
||||
URL url.URL `meta:"og:url"`
|
||||
VideoWidth int64 `meta:"og:video:width"`
|
||||
VideoHeight int64 `meta:"og:video:height"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
res, _ := http.Get("https://www.youtube.com/watch?v=FzRH3iTQPrk")
|
||||
|
||||
data := new(MetaData)
|
||||
|
||||
err := m.Metabolize(res.Body, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Title: %s\n", data.Title)
|
||||
fmt.Printf("Description: %s\n", data.Description)
|
||||
fmt.Printf("Type: %s\n", data.Type)
|
||||
fmt.Printf("URL: %s\n", data.URL.String())
|
||||
fmt.Printf("VideoWidth: %d\n", data.VideoWidth)
|
||||
fmt.Printf("VideoHeight: %d\n", data.VideoHeight)
|
||||
}
|
||||
```
|
||||
|
||||
Outputs:
|
||||
|
||||
```
|
||||
Title: The Sneezing Baby Panda
|
||||
Description: A Baby Panda Sneezing Original footage taken and being used with kind permission of LJM Productions Pty. Ltd.,/Wild Candy Pty. Ltd. Authentic t-shirts http:/...
|
||||
Type: video
|
||||
URL: http://www.youtube.com/watch?v=FzRH3iTQPrk
|
||||
VideoWidth: 480
|
||||
VideoHeight: 360
|
||||
```
|
||||
|
||||
### Supported types
|
||||
|
||||
* `string`
|
||||
* `bool`
|
||||
* `float64`
|
||||
* `int64`
|
||||
* `time.Time`
|
||||
* `url.URL`
|
||||
|
||||
153
vendor/github.com/keighl/metabolize/metabolize.go
generated
vendored
Normal file
153
vendor/github.com/keighl/metabolize/metabolize.go
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
package metabolize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"golang.org/x/net/html"
|
||||
"io"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
TagName = `meta`
|
||||
htmlRegion = `head`
|
||||
htmlTag = `meta`
|
||||
)
|
||||
|
||||
var (
|
||||
NotStructError = fmt.Errorf(`Destination is not a struct`)
|
||||
)
|
||||
|
||||
type MetaData map[string]string
|
||||
|
||||
type Meta struct {
|
||||
Title string `meta:og:title`
|
||||
Desc string `meta:og:image`
|
||||
}
|
||||
|
||||
func Metabolize(doc io.Reader, obj interface{}) error {
|
||||
data, err := ParseDocument(doc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Decode(data, obj)
|
||||
}
|
||||
|
||||
func Decode(data MetaData, obj interface{}) error {
|
||||
elem := reflect.ValueOf(obj).Elem()
|
||||
if elem.Kind() != reflect.Struct {
|
||||
return NotStructError
|
||||
}
|
||||
|
||||
for i := 0; i < elem.NumField(); i++ {
|
||||
field := elem.Type().Field(i)
|
||||
|
||||
fieldValue := elem.FieldByName(field.Name)
|
||||
if !fieldValue.IsValid() {
|
||||
continue
|
||||
}
|
||||
if !fieldValue.CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
tag := field.Tag.Get(TagName)
|
||||
if tag == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
tags := strings.Split(tag, ",")
|
||||
for _, tagItem := range tags {
|
||||
if data[tagItem] == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if fieldValue.Kind() == reflect.String {
|
||||
val := string(data[tagItem])
|
||||
fieldValue.SetString(val)
|
||||
}
|
||||
|
||||
if fieldValue.Kind() == reflect.Bool {
|
||||
val, err := strconv.ParseBool(data[tagItem])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
fieldValue.SetBool(val)
|
||||
}
|
||||
|
||||
if fieldValue.Kind() == reflect.Float64 {
|
||||
val, err := strconv.ParseFloat(data[tagItem], 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
fieldValue.SetFloat(val)
|
||||
}
|
||||
|
||||
if fieldValue.Kind() == reflect.Int64 {
|
||||
val, err := strconv.ParseInt(data[tagItem], 0, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
fieldValue.SetInt(val)
|
||||
}
|
||||
|
||||
if field.Type.Name() == "URL" {
|
||||
val, err := url.Parse(data[tagItem])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(*val))
|
||||
}
|
||||
|
||||
if field.Type.Name() == "Time" {
|
||||
val, err := time.Parse(time.RFC3339, data[tagItem])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
fieldValue.Set(reflect.ValueOf(val))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseDocument(doc io.Reader) (MetaData, error) {
|
||||
data := MetaData{}
|
||||
tokenizer := html.NewTokenizer(doc)
|
||||
for {
|
||||
tt := tokenizer.Next()
|
||||
if tt == html.ErrorToken {
|
||||
if tokenizer.Err() == io.EOF {
|
||||
return data, nil
|
||||
}
|
||||
return nil, tokenizer.Err()
|
||||
}
|
||||
|
||||
token := tokenizer.Token()
|
||||
|
||||
if token.Type == html.EndTagToken && token.Data == htmlRegion {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
if token.Data == htmlTag {
|
||||
var property, content string
|
||||
for _, attr := range token.Attr {
|
||||
switch attr.Key {
|
||||
case "property", "name":
|
||||
property = strings.ToLower(attr.Val)
|
||||
case "content":
|
||||
content = attr.Val
|
||||
}
|
||||
}
|
||||
|
||||
if property != "" {
|
||||
data[strings.TrimSpace(property)] = content
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
Reference in New Issue
Block a user