vimego

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 20, 2023 License: MIT Imports: 14 Imported by: 0

README

vimego

Search, download Vimeo videos and retrieve metadata.
Largely based on yashrathi's vimeo_downloader.

Installing

go get github.com/raitonoberu/vimego

Usage

Get a direct URL for the best available .mp4 stream (video+audio)
package main

import (
	"fmt"
	"github.com/raitonoberu/vimego"
)

func main() {
	video, _ := vimego.NewVideo("https://vimeo.com/206152466")
	formats, err := video.Formats()
	if err != nil {
		panic(err)
	}

	fmt.Println(formats.Progressive.Best().URL)
	// https://vod-progressive.akamaized.net/...
}
Get metadata
package main

import (
	"encoding/json"
	"fmt"
	"github.com/raitonoberu/vimego"
)

func main() {
	video, _ := vimego.NewVideo("https://vimeo.com/206152466")
	metadata, err := video.Metadata()
	if err != nil {
		panic(err)
	}

	jsonstr, _ := json.Marshal(metadata)
	fmt.Println(string(jsonstr))
}
Example Result
{
  "id": 206152466,
  "title": "Crystal Castles - Kept",
  "description": "",
  "url": "https://vimeo.com/206152466",
  "upload_date": "2017-02-28 18:07:25",
  "thumbnail_small": "http://i.vimeocdn.com/video/621091880_100x75",
  "thumbnail_medium": "http://i.vimeocdn.com/video/621091880_200x150",
  "thumbnail_large": "http://i.vimeocdn.com/video/621091880_640",
  "user_id": 19229427,
  "user_name": "Vladislav Donets",
  "user_url": "https://vimeo.com/donec",
  "user_portrait_small": "http://i.vimeocdn.com/portrait/8438592_30x30",
  "user_portrait_medium": "http://i.vimeocdn.com/portrait/8438592_75x75",
  "user_portrait_large": "http://i.vimeocdn.com/portrait/8438592_100x100",
  "user_portrait_huge": "http://i.vimeocdn.com/portrait/8438592_300x300",
  "stats_number_of_likes": 211,
  "stats_number_of_plays": 65095,
  "stats_number_of_comments": 17,
  "duration": 243,
  "width": 1280,
  "height": 720,
  "tags": "Crystal Castles",
  "embed_privacy": "anywhere"
}
Search for videos
package main

import (
	"encoding/json"
	"fmt"
	"github.com/raitonoberu/vimego"
)

func main() {
	client := vimego.NewSearchClient()
	result, err := client.Search("Rick Astley", 1)
	if err != nil {
		panic(err)
	}
	video := result.Data.Videos()[0]

	jsonstr, _ := json.Marshal(video)
	fmt.Println(string(jsonstr))
}
Example Result
{
   "name":"The Rick Astley Remixer",
   "link":"https://vimeo.com/dinahmoe/the-rick-astley-project",
   "duration":182,
   "created_time":"2011-06-21T22:30:02Z",
   "privacy":{
      "view":"anybody"
   },
   "pictures":{
      "sizes":[
         {
            "width":100,
            "height":75,
            "link":"https://i.vimeocdn.com/video/167407170-345a400d1c7c4919f9bf098da33dba5673eb0cba165da8559516acd3e64d7f07-d_100x75?r=pad"
         },
         {
            "width":200,
            "height":150,
            "link":"https://i.vimeocdn.com/video/167407170-345a400d1c7c4919f9bf098da33dba5673eb0cba165da8559516acd3e64d7f07-d_200x150?r=pad"
         },
         {
            "width":295,
            "height":166,
            "link":"https://i.vimeocdn.com/video/167407170-345a400d1c7c4919f9bf098da33dba5673eb0cba165da8559516acd3e64d7f07-d_295x166?r=pad"
         },
         {
            "width":640,
            "height":360,
            "link":"https://i.vimeocdn.com/video/167407170-345a400d1c7c4919f9bf098da33dba5673eb0cba165da8559516acd3e64d7f07-d_640x360?r=pad"
         },
         {
            "width":960,
            "height":540,
            "link":"https://i.vimeocdn.com/video/167407170-345a400d1c7c4919f9bf098da33dba5673eb0cba165da8559516acd3e64d7f07-d_960x540?r=pad"
         },
         {
            "width":1280,
            "height":720,
            "link":"https://i.vimeocdn.com/video/167407170-345a400d1c7c4919f9bf098da33dba5673eb0cba165da8559516acd3e64d7f07-d_1280x720?r=pad"
         },
         {
            "width":1920,
            "height":1080,
            "link":"https://i.vimeocdn.com/video/167407170-345a400d1c7c4919f9bf098da33dba5673eb0cba165da8559516acd3e64d7f07-d_1920x1080?r=pad"
         }
      ]
   },
   "metadata":{
      "connections":{
         "comments":{
            "total":3
         },
         "likes":{
            "total":32
         }
      }
   },
   "user":{
      "name":"DinahmoeSTHLM",
      "link":"https://vimeo.com/dinahmoe",
      "location":"Stockholm, Sweden",
      "pictures":{
         "sizes":[
            {
               "width":30,
               "height":30,
               "link":"https://i.vimeocdn.com/portrait/17506926_30x30"
            },
            {
               "width":72,
               "height":72,
               "link":"https://i.vimeocdn.com/portrait/17506926_72x72"
            },
            {
               "width":75,
               "height":75,
               "link":"https://i.vimeocdn.com/portrait/17506926_75x75"
            },
            {
               "width":100,
               "height":100,
               "link":"https://i.vimeocdn.com/portrait/17506926_100x100"
            },
            {
               "width":144,
               "height":144,
               "link":"https://i.vimeocdn.com/portrait/17506926_144x144"
            },
            {
               "width":216,
               "height":216,
               "link":"https://i.vimeocdn.com/portrait/17506926_216x216"
            },
            {
               "width":288,
               "height":288,
               "link":"https://i.vimeocdn.com/portrait/17506926_288x288"
            },
            {
               "width":300,
               "height":300,
               "link":"https://i.vimeocdn.com/portrait/17506926_300x300"
            },
            {
               "width":360,
               "height":360,
               "link":"https://i.vimeocdn.com/portrait/17506926_360x360"
            }
         ]
      }
   }
}

Advanced usage

About formats

Vimeo stores its streams in 3 different formats:

  • Progressive
    • A direct URL to the .mp4 stream (video+audio).
    • Max quality - 1080p.
    • Most probably, this is what you're looking for.
  • Hls
    • An URL to the master.m3u8 playlist.
    • Max quality - 2160p.
    • Best for passing it to video players (VLC, mpv, etc.).
  • Dash
    • An URL to the JSON containing data about segments.
    • Max quality - 2160p.
    • Suitable if you need a video-only or audio-only stream.
Get video-only or audio-only stream

There is a Video.GetDashStreams method that parses the DASH format and provides information about the available streams.

package main

import (
	"io"
	"os"
	"github.com/raitonoberu/vimego"
)

func main() {
	video, _ := vimego.NewVideo("https://vimeo.com/206152466")
	formats, _ := video.Formats()
	streams, _ := video.GetDashStreams(formats.Dash.Url())

	stream, _, _ := streams.Audio.Best().Reader(nil) // io.ReadCloser
	file, _ := os.Create("output.m4a")
	defer file.Close()
	io.Copy(file, stream)
}
Get embed-only videos

If the video you want to download can only be played on a specific site, there is a way to get its streams. You need to set the value Referer in the headers. Note that Video.Metadata() does not work with such videos.

package main

import (
	"fmt"
	"github.com/raitonoberu/vimego"
)

func main() {
	video, _ := vimego.NewVideo("https://player.vimeo.com/video/498617513")
	video.Header["Referer"] = []string{"https://atpstar.com/plans-162.html"}

	formats, _ := video.Formats()
	fmt.Println(formats.Progressive.Best().URL)
}

Information

The code seems to be ready, but I have some thoughts on improving it.

TODO:
  • Handle video IDs other than int
  • Captcha processing
  • Make a CLI tool

License

MIT License, see LICENSE file for additional information.

Documentation

Overview

Package vimego: Search, download Vimeo videos and retrieve metadata.

Index

Constants

View Source
const UserAgent = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0"

Variables

View Source
var (
	ErrInvalidUrl    = errors.New("the URL is invalid")
	ErrParsingFailed = errors.New("couldn't get config")
)

Functions

This section is empty.

Types

type ChannelItem

type ChannelItem struct {
	Name     string `json:"name"`
	Link     string `json:"link"`
	Pictures struct {
		Sizes []PictureSize `json:"sizes"`
	} `json:"pictures"`
	Metadata struct {
		Connections struct {
			Users struct {
				Total int `json:"total"`
			} `json:"users"`
			Videos struct {
				Total int `json:"total"`
			} `json:"videos"`
		} `json:"connections"`
	} `json:"metadata"`
}

type DashAudioStream

type DashAudioStream struct {
	Channels   int `json:"channels"`
	SampleRate int `json:"sample_rate"`
	DashStream
}

type DashAudioStreams

type DashAudioStreams []*DashAudioStream

func (DashAudioStreams) Best

Best returns the DashAudioStream with the highest bitrate.

func (DashAudioStreams) Len

func (d DashAudioStreams) Len() int

func (DashAudioStreams) Less

func (d DashAudioStreams) Less(a, b int) bool

func (DashAudioStreams) Swap

func (d DashAudioStreams) Swap(a, b int)

func (DashAudioStreams) Worst

func (d DashAudioStreams) Worst() *DashAudioStream

Worst returns the DashAudioStream with the lowest bitrate.

type DashFormat

type DashFormat struct {
	SeparateAv bool   `json:"separate_av"`
	DefaultCdn string `json:"default_cdn"`
	Cdns       struct {
		AkfireInterconnectQuic struct {
			URL    string `json:"url"`
			Origin string `json:"origin"`
			AvcURL string `json:"avc_url"`
		} `json:"akfire_interconnect_quic"`
		FastlySkyfire struct {
			URL    string `json:"url"`
			Origin string `json:"origin"`
			AvcURL string `json:"avc_url"`
		} `json:"fastly_skyfire"`
	} `json:"cdns"`
}

func (*DashFormat) Url

func (s *DashFormat) Url() string

Url returns the URL for the video stream.

type DashSegment

type DashSegment struct {
	Start float64 `json:"start"`
	End   float64 `json:"end"`
	URL   string  `json:"url"`
	Size  int     `json:"size"`
}

type DashStream

type DashStream struct {
	ID                 string         `json:"id"`
	URL                string         `json:"url"`
	BaseURL            string         `json:"base_url"`
	Format             string         `json:"format"`
	MimeType           string         `json:"mime_type"`
	Codecs             string         `json:"codecs"`
	Bitrate            int            `json:"bitrate"`
	AvgBitrate         int            `json:"avg_bitrate"`
	Duration           float64        `json:"duration"`
	MaxSegmentDuration int            `json:"max_segment_duration"`
	InitSegment        string         `json:"init_segment"`
	Segments           []*DashSegment `json:"segments"`
}

func (*DashStream) Reader

func (s *DashStream) Reader(httpClient *http.Client) (io.ReadCloser, int64, error)

Readers returns an io.ReadCloser for reading streaming data.

type DashStreams

type DashStreams struct {
	ClipID  string           `json:"clip_id"`
	BaseURL string           `json:"base_url"`
	Video   DashVideoStreams `json:"video"`
	Audio   DashAudioStreams `json:"audio"`
}

type DashVideoStream

type DashVideoStream struct {
	Framerate float64 `json:"framerate"`
	Width     int     `json:"width"`
	Height    int     `json:"height"`
	DashStream
}

type DashVideoStreams

type DashVideoStreams []*DashVideoStream

func (DashVideoStreams) Best

Best returns the DashVideoStream with the highest bitrate.

func (DashVideoStreams) Len

func (d DashVideoStreams) Len() int

func (DashVideoStreams) Less

func (d DashVideoStreams) Less(a, b int) bool

func (DashVideoStreams) Swap

func (d DashVideoStreams) Swap(a, b int)

func (DashVideoStreams) Worst

func (d DashVideoStreams) Worst() *DashVideoStream

Worst returns the DashVideoStream with the lowest bitrate.

type ErrUnexpectedStatusCode

type ErrUnexpectedStatusCode int

func (ErrUnexpectedStatusCode) Error

func (err ErrUnexpectedStatusCode) Error() string

type GroupItem

type GroupItem struct {
	Name     string `json:"name"`
	Link     string `json:"link"`
	Pictures struct {
		Sizes []PictureSize `json:"sizes"`
	} `json:"pictures"`
	Metadata struct {
		Connections struct {
			Users struct {
				Total int `json:"total"`
			} `json:"users"`
			Videos struct {
				Total int `json:"total"`
			} `json:"videos"`
		} `json:"connections"`
	} `json:"metadata"`
}

type HlsFormat

type HlsFormat struct {
	SeparateAv bool   `json:"separate_av"`
	DefaultCdn string `json:"default_cdn"`
	Cdns       struct {
		AkfireInterconnectQuic struct {
			URL    string `json:"url"`
			Origin string `json:"origin"`
			AvcURL string `json:"avc_url"`
		} `json:"akfire_interconnect_quic"`
		FastlySkyfire struct {
			URL    string `json:"url"`
			Origin string `json:"origin"`
			AvcURL string `json:"avc_url"`
		} `json:"fastly_skyfire"`
	} `json:"cdns"`
}

func (*HlsFormat) Url

func (s *HlsFormat) Url() string

Url returns the URL for the .m3u8 playlist.

type Metadata

type Metadata struct {
	ID                 int    `json:"id"`
	Title              string `json:"title"`
	Description        string `json:"description"`
	URL                string `json:"url"`
	UploadDate         string `json:"upload_date"`
	ThumbnailSmall     string `json:"thumbnail_small"`
	ThumbnailMedium    string `json:"thumbnail_medium"`
	ThumbnailLarge     string `json:"thumbnail_large"`
	UserID             int    `json:"user_id"`
	UserName           string `json:"user_name"`
	UserURL            string `json:"user_url"`
	UserPortraitSmall  string `json:"user_portrait_small"`
	UserPortraitMedium string `json:"user_portrait_medium"`
	UserPortraitLarge  string `json:"user_portrait_large"`
	UserPortraitHuge   string `json:"user_portrait_huge"`
	Likes              int    `json:"stats_number_of_likes"`
	Plays              int    `json:"stats_number_of_plays"`
	Comments           int    `json:"stats_number_of_comments"`
	Duration           int    `json:"duration"`
	Width              int    `json:"width"`
	Height             int    `json:"height"`
	Tags               string `json:"tags"`
	EmbedPrivacy       string `json:"embed_privacy"`
}

func (*Metadata) GetUploadDate

func (m *Metadata) GetUploadDate() (time.Time, error)

GetUploadDate returns the video upload date as time.Time object.

type PeopleItem

type PeopleItem struct {
	Name     string `json:"name"`
	Link     string `json:"link"`
	Location string `json:"location"`
	Pictures struct {
		Sizes []PictureSize `json:"sizes"`
	} `json:"pictures"`
	Metadata struct {
		Connections struct {
			Followers struct {
				Total int `json:"total"`
			} `json:"followers"`
			Videos struct {
				Total int `json:"total"`
			} `json:"videos"`
		} `json:"connections"`
	} `json:"badge"`
}

type PictureSize

type PictureSize struct {
	Width  int    `json:"width"`
	Height int    `json:"height"`
	Link   string `json:"link"`
}

type ProgressiveFormat

type ProgressiveFormat struct {
	Profile int    `json:"profile,string"`
	Width   int    `json:"width"`
	Mime    string `json:"mime"`
	URL     string `json:"url"`
	Cdn     string `json:"cdn"`
	Quality string `json:"quality"`
	Origin  string `json:"origin"`
	Height  int    `json:"height"`
}

type ProgressiveFormats

type ProgressiveFormats []*ProgressiveFormat

func (ProgressiveFormats) Best

Best returns the ProgressiveFormat with the hightest resolution.

func (ProgressiveFormats) Len

func (p ProgressiveFormats) Len() int

func (ProgressiveFormats) Less

func (p ProgressiveFormats) Less(a, b int) bool

func (ProgressiveFormats) Swap

func (p ProgressiveFormats) Swap(a, b int)

func (ProgressiveFormats) Worst

Worst returns the ProgressiveFormat with the lowest resolution.

type SearchCategory

type SearchCategory string
const (
	AnyCategory                    SearchCategory = ""
	TrailersCategory               SearchCategory = "trailers"
	NarrativeCategory              SearchCategory = "narrative"
	DocumentaryCategory            SearchCategory = "documentary"
	ExperimentalCategory           SearchCategory = "experimental"
	AnimationCategory              SearchCategory = "animation"
	EducationalCategory            SearchCategory = "educational"
	AdsAndCommercialsCategory      SearchCategory = "adsandcommercials"
	MusicCategory                  SearchCategory = "music"
	BrandedContentCategory         SearchCategory = "brandedcontent"
	SportsCategory                 SearchCategory = "sports"
	TravelCategory                 SearchCategory = "travel"
	CameraTechniquesCategory       SearchCategory = "cameratechniques"
	ComedyCategory                 SearchCategory = "comedy"
	EventsCategory                 SearchCategory = "events"
	FashionCategory                SearchCategory = "fashion"
	FoodCategory                   SearchCategory = "food"
	IdentsAndAnimatedLogosCategory SearchCategory = "identsandanimatedlogos"
	IndustryCategory               SearchCategory = "industry"
	IndustrionalsCategory          SearchCategory = "instructionals"
	JournalismCategory             SearchCategory = "journalism"
	PersonalCategory               SearchCategory = "personal"
	ProductCategory                SearchCategory = "product"
	TalksCategory                  SearchCategory = "talks"
	TitleAndCreditsCategory        SearchCategory = "titlesandcredits"
	VideoSchoolCategory            SearchCategory = "videoschool"
	WeedingCategory                SearchCategory = "wedding"
)

type SearchClient

type SearchClient struct {
	PerPage   int
	Filter    SearchFilter
	Order     SortOrder
	Direction SortDirection
	Category  SearchCategory

	Header     map[string][]string
	HTTPClient *http.Client
	// contains filtered or unexported fields
}

func NewSearchClient

func NewSearchClient() *SearchClient

NewSearchClient creates a new SearchClient with default parameters.

func (*SearchClient) Search

func (c *SearchClient) Search(query string, page int) (*SearchResult, error)

Search returns the result from the requested page.

type SearchData

type SearchData []struct {
	Type    string       `json:"type"`
	Video   *VideoItem   `json:"clip,omitempty"`
	People  *PeopleItem  `json:"people,omitempty"`
	Channel *ChannelItem `json:"channel,omitempty"`
	Group   *GroupItem   `json:"group,omitempty"`
}

func (SearchData) Channels

func (d SearchData) Channels() []*ChannelItem

func (SearchData) Groups

func (d SearchData) Groups() []*GroupItem

func (SearchData) People

func (d SearchData) People() []*PeopleItem

func (SearchData) Videos

func (d SearchData) Videos() []*VideoItem

type SearchFilter

type SearchFilter string
const (
	VideoFilter   SearchFilter = "clip"
	PeopleFilter  SearchFilter = "people"
	ChannelFilter SearchFilter = "channel"
	GroupFilter   SearchFilter = "group"
)

type SearchResult

type SearchResult struct {
	Total   int        `json:"total"`
	Page    int        `json:"page"`
	PerPage int        `json:"per_page"`
	Data    SearchData `json:"data"`
}

type SortDirection

type SortDirection string
const (
	AscDirection  SortDirection = "asc"
	DescDirection SortDirection = "desc"
)

type SortOrder

type SortOrder string
const (
	RelevanceOrder    SortOrder = "relevance"
	LatestOrder       SortOrder = "latest"
	PopularityOrder   SortOrder = "popularity"
	AlphabeticalOrder SortOrder = "alphabetical"
	DurationOrder     SortOrder = "duration"
)

type Video

type Video struct {
	Url     string
	VideoId int

	Header     map[string][]string
	HTTPClient *http.Client
}

func NewVideo

func NewVideo(url string) (*Video, error)

NewVideo creates a new Video from URL.

func NewVideoFromId

func NewVideoFromId(videoId int) *Video

NewVideo creates a new Video from video ID.

func (*Video) Formats

func (v *Video) Formats() (*VideoFormats, error)

Formats returns the video formats. Progressive formats contain direct video+audio streams up to 1080p. Hls format contains an URL to .m3u8 playlist with all possible streams. Dash format contains a JSON URL that can be parsed using GetDashStreams.

func (*Video) GetDashStreams

func (v *Video) GetDashStreams(dashUrl string) (*DashStreams, error)

GetDashStreams returns DASH streams of the video.

func (*Video) Metadata

func (v *Video) Metadata() (*Metadata, error)

Metadata returns the video metadata.

type VideoFormats

type VideoFormats struct {
	Progressive ProgressiveFormats `json:"progressive"`
	Dash        *DashFormat        `json:"dash"`
	Hls         *HlsFormat         `json:"hls"`
}

type VideoItem

type VideoItem struct {
	Name        string    `json:"name"`
	Link        string    `json:"link"`
	Duration    int       `json:"duration"`
	CreatedTime time.Time `json:"created_time"`
	Privacy     struct {
		View string `json:"view"`
	} `json:"privacy"`
	Pictures struct {
		Sizes []PictureSize `json:"sizes"`
	} `json:"pictures"`
	Metadata struct {
		Connections struct {
			Comments struct {
				Total int `json:"total"`
			} `json:"comments"`
			Likes struct {
				Total int `json:"total"`
			} `json:"likes"`
		} `json:"connections"`
	} `json:"metadata"`
	User struct {
		Name     string `json:"name"`
		Link     string `json:"link"`
		Location string `json:"location"`
		Pictures struct {
			Sizes []PictureSize `json:"sizes"`
		} `json:"pictures"`
	} `json:"user"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL