Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Supported download GIF #52

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ Usage:
-t, --tweet TWEET_ID Single tweet id to download
-n, --nbr NBR Number of tweets to download
-i, --img Download images only
-g, --gif Download GIFs only (need ffmpeg)
-v, --video Download videos only
-a, --all Download images and videos
-a, --all Download images, GIFs(need ffmpeg) and videos
-r, --retweet Download retweet too
-z, --url Print media url without download it
-R, --retweet-only Donwload only retweet
Expand All @@ -38,7 +39,7 @@ Usage:

#### Download 300 tweets from @Spraytrains.

If the tweet doesn't contain a photo or video nothing will be downloaded but it will count towards the 300.
If the tweet doesn't contain a photo, gif or video nothing will be downloaded but it will count towards the 300.

```sh
twmd -u Spraytrains -o ~/Downloads -a -n 300
Expand All @@ -57,6 +58,11 @@ You can use `-r|--retweet` to download retweets as well, or `-R|--retweet-only`
twmd -t 156170319961391104
```

#### Download animated-GIFs:

Animated-GIFs are downloaded as mp4 files.
If you want to get them as gif files, you need to install ffmpeg.

#### NSFW tweets

You'll need to login `-L|--login` for downloading nsfw tweets.
Expand Down Expand Up @@ -134,6 +140,3 @@ fi


Check [here](https://gist.github.com/mmpx12/f0741d40909ed3f182fd6f9b33b580d7) for a full termux-url-opener example.


#### Gifs are not supported at the moment.
102 changes: 94 additions & 8 deletions twmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"net/http"
URL "net/url"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
Expand All @@ -32,6 +33,7 @@ var (
onlyrtw bool
vidz bool
imgs bool
gifs bool
urlOnly bool
version = "1.13.3"
scraper *twitterscraper.Scraper
Expand Down Expand Up @@ -75,6 +77,7 @@ func download(wg *sync.WaitGroup, tweet interface{}, url string, filetype string
}

var f *os.File
var outputDir string
defer f.Close()
if dwn_type == "user" {
if update {
Expand All @@ -84,11 +87,16 @@ func download(wg *sync.WaitGroup, tweet interface{}, url string, filetype string
}
}
if filetype == "rtimg" {
f, _ = os.Create(output + "/img/RE-" + name)
outputDir = output + "/img/"
name = "RE-" + name
} else if filetype == "rtgif" {
outputDir = output + "/gif/"
name = "RE-" + name
} else if filetype == "rtvideo" {
f, _ = os.Create(output + "/video/RE-" + name)
outputDir = output + "/video/"
name = "RE-" + name
} else {
f, _ = os.Create(output + "/" + filetype + "/" + name)
outputDir = output + "/" + filetype + "/"
}
} else {
if update {
Expand All @@ -97,10 +105,39 @@ func download(wg *sync.WaitGroup, tweet interface{}, url string, filetype string
return
}
}
f, _ = os.Create(output + "/" + name)
outputDir = output + "/"
}

f, _ = os.Create(outputDir + name)
io.Copy(f, resp.Body)
fmt.Println("Downloaded " + name)

isGif := strings.Contains(url, "tweet_video")
if isGif {
_, err := exec.LookPath("ffmpeg")
if err != nil {
fmt.Println("Convert error: ", err)
return
}
gifName := strings.TrimSuffix(name, ".mp4") + ".gif"

args := []string{
"-y",
"-i", outputDir + name,
"-c:v", "gif",
outputDir + gifName,
}

cmd := exec.Command("ffmpeg", args...)
err = cmd.Run()
if err != nil {
fmt.Println("Convert error: ", err)
return
}

os.Remove(outputDir + name)
fmt.Println("Converted " + gifName)
}
}

func videoUser(wait *sync.WaitGroup, tweet *twitterscraper.TweetResult, output string, rt bool) {
Expand Down Expand Up @@ -153,6 +190,24 @@ func photoUser(wait *sync.WaitGroup, tweet *twitterscraper.TweetResult, output s
}
}

func gifUser(wait *sync.WaitGroup, tweet *twitterscraper.TweetResult, output string, rt bool) {
defer wait.Done()
wg := sync.WaitGroup{}
if len(tweet.GIFs) > 0 || tweet.IsRetweet {
if tweet.IsRetweet && (rt || onlyrtw) {
singleTweet(output, tweet.ID)
}
for _, i := range tweet.GIFs {
if onlyrtw || tweet.IsRetweet {
continue
}
wg.Add(1)
go download(&wg, tweet, i.URL, "gif", output, "user")
}
wg.Wait()
}
}

func videoSingle(tweet *twitterscraper.Tweet, output string) {
if tweet == nil {
return
Expand Down Expand Up @@ -180,7 +235,6 @@ func photoSingle(tweet *twitterscraper.Tweet, output string) {
if len(tweet.Photos) > 0 {
wg := sync.WaitGroup{}
for _, i := range tweet.Photos {
fmt.Println(i.URL)
var url string
if !strings.Contains(i.URL, "video_thumb/") {
if size == "orig" || size == "small" {
Expand All @@ -201,6 +255,25 @@ func photoSingle(tweet *twitterscraper.Tweet, output string) {
}
}

func gifSingle(tweet *twitterscraper.Tweet, output string) {
if tweet == nil {
return
}
if len(tweet.GIFs) > 0 {
wg := sync.WaitGroup{}
for _, i := range tweet.GIFs {
if usr != "" {
wg.Add(1)
go download(&wg, tweet, i.URL, "rtgif", output, "user")
} else {
wg.Add(1)
go download(&wg, tweet, i.URL, "tweet", output, "tweet")
}
}
wg.Wait()
}
}

func askPass(loginp, twofa bool) {
for {
var username string
Expand Down Expand Up @@ -270,9 +343,13 @@ func singleTweet(output string, id string) {
if imgs {
photoSingle(tweet, output)
}
if gifs {
gifSingle(tweet, output)
}
} else {
videoSingle(tweet, output)
photoSingle(tweet, output)
gifSingle(tweet, output)
}
}

Expand Down Expand Up @@ -408,8 +485,9 @@ func main() {
op.On("-t", "--tweet TWEET_ID", "Single tweet to download", &single)
op.On("-n", "--nbr NBR", "Number of tweets to download", &nbr)
op.On("-i", "--img", "Download images only", &imgs)
op.On("-g", "--gif", "Download GIFs only (need ffmpeg)", &gifs)
op.On("-v", "--video", "Download videos only", &vidz)
op.On("-a", "--all", "Download images and videos", &all)
op.On("-a", "--all", "Download images, GIFs (need ffmpeg) and videos", &all)
op.On("-r", "--retweet", "Download retweet too", &retweet)
op.On("-z", "--url", "Print media url without download it", &urlOnly)
op.On("-R", "--retweet-only", "Download only retweet", &onlyrtw)
Expand Down Expand Up @@ -446,9 +524,10 @@ func main() {
if all {
vidz = true
imgs = true
gifs = true
}
if !vidz && !imgs && single == "" {
fmt.Println("You must specify what to download. (-i --img) for images, (-v --video) for videos or (-a --all) for both")
if !vidz && !imgs && !gifs && single == "" {
fmt.Println("You must specify what to download. (-i --img) for images, (-g --gif) for GIFs, (-v --video) for videos or (-a --all) for both")
op.Help()
os.Exit(1)
}
Expand Down Expand Up @@ -517,6 +596,9 @@ func main() {
if imgs {
os.MkdirAll(output+"/img", os.ModePerm)
}
if gifs {
os.MkdirAll(output+"/gif", os.ModePerm)
}
nbrs, _ := strconv.Atoi(nbr)
wg := sync.WaitGroup{}
for tweet := range scraper.GetTweets(context.Background(), usr, nbrs) {
Expand All @@ -532,6 +614,10 @@ func main() {
wg.Add(1)
go photoUser(&wg, tweet, output, retweet)
}
if gifs {
wg.Add(1)
go gifUser(&wg, tweet, output, retweet)
}
}
wg.Wait()
}