6
6
7
7
package api
8
8
9
- import (
9
+ import (
10
+ "bytes"
11
+ "encoding/json"
12
+ "errors"
10
13
"fmt"
11
14
"io/ioutil"
12
15
"net/http"
@@ -18,7 +21,23 @@ import (
18
21
"google.golang.org/api/youtube/v3"
19
22
)
20
23
24
+
25
+ //const variables
26
+ const (
27
+
28
+ //Video extractor
29
+ videoExtractor = "http://youtube.com/get_video_info?video_id="
30
+ )
31
+
21
32
//Youtube Downloader Data file.
33
+ type RawVideoData struct {
34
+ Title string `json:"title"`
35
+ Author string `json:"author`
36
+ Status string `json:"status"`
37
+ URLEncodedFmtStreamMap map [string ][]string `json:"url_encoded_fmt_stream_map"`
38
+ }
39
+
40
+ }
22
41
type ApiData struct {
23
42
FileName string
24
43
Title string
@@ -33,16 +52,17 @@ type ApiData struct {
33
52
//gets the Video ID from youtube url
34
53
func getVideoId (url string ) ( string , error ) {
35
54
36
- s := strings .Split (url , "?v=" ))
55
+ s := strings .Split (url , "?v=" )
37
56
s = strings .Split (s [1 ], "&" )
38
57
if len (s [0 ]) == 0 {
39
- s [0 ], error .New ("Empty string)
58
+ return s [0 ], errors .New ("Empty string" )
40
59
}
41
60
42
61
return s [0 ], nil
43
62
}
44
63
45
64
65
+
46
66
func printVideosListResults (response * youtube.VideoListResponse ) {
47
67
for _ , item := range response .Items {
48
68
fmt .Println (item .Id , ": " , item .Snippet .Title )
@@ -60,141 +80,148 @@ func videosListById(service *youtube.Service, part string, id string) {
60
80
printVideosListResults (response )
61
81
}
62
82
63
- videosListById (service , "snippet,contentDetails,statistics" , "Ks-_Mh1QhMc" )
64
-
65
-
66
83
67
84
85
+ //Gets Video Data from Youtube URL
68
86
func APIGetVideoStream (service youtube.Service , url string )(videoData []byte , err error ) {
87
+
88
+ videoStream := new (RawVideoData )
69
89
70
90
//Gets video Id
71
91
id , err := getVideoId (url )
72
92
auth .HandleError (err , "Invalid youtube URL." )
73
93
74
- //Get Video response stream
75
- videosListById (service , "snippet,contentDetails,liveStreamingDetails, fileDetails" , id )//fileDetails part not permitted.
94
+ //Get Video Data stream
95
+ videoUrl := videoExtractor + id
96
+ resp , er := http .Get (videoUrl )
97
+ auth .HandleError (er , "Error in GET request)
98
+ defer resp .Body .Close ()
99
+ out , e := ioutil .ReadAll (resp .Body )
100
+ auth .HandleError (e , "Error reading video data" )
101
+ if err = json .Unmarshal (out , & a .output ); err != nil {
102
+ logrus .Errorf ("Error JSON Unmarshall: %v" , err )
103
+ }
104
+ //Extract Video information.
105
+ videoInfo := videosListById (service , "snippet,contentDetails" , id )//fileDetails part not permitted.
76
106
77
107
//Get Data stream from video response
78
-
108
+ if err = json .Unmarshal (out , & videoStream ); err != nil {
109
+ logrus .Errorf ("Error JSON Unmarshall: %v" , err )
110
+ }
79
111
80
112
//Download data stream to memory.
81
113
82
114
//convert video file to flv or mp3
83
115
84
116
117
+ }
85
118
86
119
87
- //retrieve uploads
88
- func main () {
89
- flag .Parse ()
90
120
91
- client , err := buildOAuthHTTPClient (youtube .YoutubeReadonlyScope )
92
- if err != nil {
93
- log .Fatalf ("Error building OAuth client: %v" , err )
94
- }
121
+ func ApiDownloadVideo () {
122
+
95
123
96
- service , err := youtube .New (client )
124
+ }
125
+
126
+
127
+
128
+ func decodeVideoInfo (response string ) (streams streamList , err error ) {
129
+ // decode
130
+
131
+ answer , err := url .ParseQuery (response )
97
132
if err != nil {
98
- log .Fatalf ("Error creating YouTube client: %v" , err )
133
+ err = fmt .Errorf ("parsing the server's answer: '%s'" , err )
134
+ return
99
135
}
100
136
101
- // Start making YouTube API calls.
102
- // Call the channels.list method. Set the mine parameter to true to
103
- // retrieve the playlist ID for uploads to the authenticated user's
104
- // channel.
105
- call := service .Channels .List ("contentDetails" ).Mine (true )
137
+ // check the status
106
138
107
- response , err := call . Do ( )
139
+ err = ensureFields ( answer , [] string { "status" , "url_encoded_fmt_stream_map" , "title" , "author" } )
108
140
if err != nil {
109
- // The channels.list method call returned an error.
110
- log . Fatalf ( "Error making API call to list channels: %v" , err . Error ())
141
+ err = fmt . Errorf ( "Missing fields in the server's answer: '%s'" , err )
142
+ return
111
143
}
112
144
113
- for _ , channel := range response .Items {
114
- playlistId := channel .ContentDetails .RelatedPlaylists .Uploads
115
- // Print the playlist ID for the list of uploaded videos.
116
- fmt .Printf ("Videos in list %s\r \n " , playlistId )
117
-
118
- nextPageToken := ""
119
- for {
120
- // Call the playlistItems.list method to retrieve the
121
- // list of uploaded videos. Each request retrieves 50
122
- // videos until all videos have been retrieved.
123
- playlistCall := service .PlaylistItems .List ("snippet" ).
124
- PlaylistId (playlistId ).
125
- MaxResults (50 ).
126
- PageToken (nextPageToken )
127
-
128
- playlistResponse , err := playlistCall .Do ()
129
-
130
- if err != nil {
131
- // The playlistItems.list method call returned an error.
132
- log .Fatalf ("Error fetching playlist items: %v" , err .Error ())
133
- }
134
-
135
- for _ , playlistItem := range playlistResponse .Items {
136
- title := playlistItem .Snippet .Title
137
- videoId := playlistItem .Snippet .ResourceId .VideoId
138
- fmt .Printf ("%v, (%v)\r \n " , title , videoId )
139
- }
140
-
141
- // Set the token to retrieve the next page of results
142
- // or exit the loop if all results have been retrieved.
143
- nextPageToken = playlistResponse .NextPageToken
144
- if nextPageToken == "" {
145
- break
146
- }
147
- fmt .Println ()
145
+ status := answer ["status" ]
146
+ if status [0 ] == "fail" {
147
+ reason , ok := answer ["reason" ]
148
+ if ok {
149
+ err = fmt .Errorf ("'fail' response status found in the server's answer, reason: '%s'" , reason [0 ])
150
+ } else {
151
+ err = errors .New (fmt .Sprint ("'fail' response status found in the server's answer, no reason given" ))
148
152
}
153
+ return
154
+ }
155
+ if status [0 ] != "ok" {
156
+ err = fmt .Errorf ("non-success response status found in the server's answer (status: '%s')" , status )
157
+ return
149
158
}
150
- }
151
159
152
- func ApiDownloadVideo () {
160
+ log ( "Server answered with a success code" )
153
161
162
+ /*
163
+ for k, v := range answer {
164
+ log("%s: %#v", k, v)
165
+ }
166
+ */
154
167
155
- }main () {
156
- flag .Parse ()
168
+ // read the streams map
157
169
158
- if * filename == "" {
159
- log .Fatalf ("You must provide a filename of a video file to upload" )
160
- }
170
+ stream_map := answer ["url_encoded_fmt_stream_map" ]
161
171
162
- client , err := buildOAuthHTTPClient (youtube .YoutubeUploadScope )
163
- if err != nil {
164
- log .Fatalf ("Error building OAuth client: %v" , err )
165
- }
172
+ // read each stream
166
173
167
- service , err := youtube .New (client )
168
- if err != nil {
169
- log .Fatalf ("Error creating YouTube client: %v" , err )
170
- }
174
+ streams_list := strings .Split (stream_map [0 ], "," )
171
175
172
- upload := & youtube.Video {
173
- Snippet : & youtube.VideoSnippet {
174
- Title : * title ,
175
- Description : * description ,
176
- CategoryId : * category ,
177
- },
178
- Status : & youtube.VideoStatus {PrivacyStatus : * privacy },
179
- }
176
+ log ("Found %d streams in answer" , len (streams_list ))
180
177
181
- // The API returns a 400 Bad Request response if tags is an empty string.
182
- if strings .Trim (* keywords , "" ) != "" {
183
- upload .Snippet .Tags = strings .Split (* keywords , "," )
184
- }
178
+ for stream_pos , stream_raw := range streams_list {
179
+ stream_qry , err := url .ParseQuery (stream_raw )
180
+ if err != nil {
181
+ log (fmt .Sprintf ("An error occured while decoding one of the video's stream's information: stream %d: %s\n " , stream_pos , err ))
182
+ continue
183
+ }
184
+ err = ensureFields (stream_qry , []string {"quality" , "type" , "url" })
185
+ if err != nil {
186
+ log (fmt .Sprintf ("Missing fields in one of the video's stream's information: stream %d: %s\n " , stream_pos , err ))
187
+ continue
188
+ }
189
+ /* dumps the raw streams
190
+ log(fmt.Sprintf("%v\n", stream_qry))
191
+ */
192
+ stream := stream {
193
+ "quality" : stream_qry ["quality" ][0 ],
194
+ "type" : stream_qry ["type" ][0 ],
195
+ "url" : stream_qry ["url" ][0 ],
196
+ "sig" : "" ,
197
+ "title" : answer ["title" ][0 ],
198
+ "author" : answer ["author" ][0 ],
199
+ }
200
+
201
+ if sig , exists := stream_qry ["sig" ]; exists { // old one
202
+ stream ["sig" ] = sig [0 ]
203
+ }
204
+
205
+ if sig , exists := stream_qry ["s" ]; exists { // now they use this
206
+ stream ["sig" ] = sig [0 ]
207
+ }
208
+
209
+ streams = append (streams , stream )
185
210
186
- call := service .Videos .Insert ("snippet,status" , upload )
211
+ quality := stream .Quality ()
212
+ if quality == QUALITY_UNKNOWN {
213
+ log ("Found unknown quality '%s'" , stream ["quality" ])
214
+ }
187
215
188
- file , err := os .Open (* filename )
189
- defer file .Close ()
190
- if err != nil {
191
- log .Fatalf ("Error opening %v: %v" , * filename , err )
192
- }
216
+ format := stream .Format ()
217
+ if format == FORMAT_UNKNOWN {
218
+ log ("Found unknown format '%s'" , stream ["type" ])
219
+ }
193
220
194
- response , err := call .Media (file ).Do ()
195
- if err != nil {
196
- log .Fatalf ("Error making YouTube API call: %v" , err )
221
+ log ("Stream found: quality '%s', format '%s'" , quality , format )
197
222
}
198
- fmt .Printf ("Upload successful! Video ID: %v\n " , response .Id )
199
- }
200
223
224
+ log ("Successfully decoded %d streams" , len (streams ))
225
+
226
+ return
227
+ }
0 commit comments