Last active 1450529936

blha303 revised this gist 1378150813. Go to revision

1 file changed, 25 insertions, 11 deletions

jtvrtmp.py

@@ -25,7 +25,8 @@
25 25
26 26 from urllib2 import urlopen
27 27 import json
28 - from sys import argv, exit
28 + from sys import argv, exit, stderr
29 + import traceback
29 30
30 31 def getswfaddr(channelname):
31 32 """ Follows the redirect to get the channel player url, strips out the useragent """
@@ -47,16 +48,25 @@ def buildcmdline(channelname, data=None, quality="live"):
47 48 data = getstreaminfo(channelname)
48 49 if quality == "live":
49 50 for i in data:
50 - if "+" in i:
51 + if "Source" in i:
51 52 quality = i
52 53 try:
53 54 data = data[quality]
54 55 except KeyError:
55 - return '; echo "-------------"; echo "Couldn\'t find stream info for %s, maybe the stream is offline?"; echo "-------------" #' % quality
56 - if not "live-cdn" in data["connect"] and not "justintvlivefs" in data["connect"]:
57 - justinlegacyparams = '-j "%s" ' % data["token"].replace('"', r'\"')
58 - else:
59 - justinlegacyparams = ""
56 + return '-q; echo "-------------"; echo "Couldn\'t find stream info for %s, maybe the stream is offline?"; echo "-------------" #' % quality
57 + try:
58 + if not "live-cdn" in data["connect"] and not "justintvlivefs" in data["connect"]:
59 + justinlegacyparams = '-j "%s" ' % data["token"].replace('"', r'\"')
60 + else:
61 + justinlegacyparams = ""
62 + except KeyError:
63 + qualities = []
64 + for i in data.keys():
65 + stderr.write(str(i))
66 + stderr.write(str(data))
67 + if "connect" in data[i]:
68 + qualities.append(i)
69 + return '-q; echo "-------------"; echo "Couldn\'t find stream info for %s, maybe you need to be subscribed to watch that resolution? Try one of these: ' + ", ".join(qualities) + '"; echo "-------------" #' % quality
60 70 out = '-r "%s/%s" %s--swfVfy "%s" -v -o -' % (data["connect"], data["play"], justinlegacyparams, getswfaddr(channelname))
61 71 return out
62 72
@@ -71,7 +81,11 @@ def main(args):
71 81 return buildcmdline(args[1])
72 82
73 83 if __name__ == "__main__":
74 - out = main(argv)
75 - print out
76 - if "Usage:" in out:
77 - exit(2)
84 + try:
85 + out = main(argv)
86 + print out
87 + if "Usage:" in out:
88 + exit(2)
89 + except:
90 + print '-q; echo "-------------"; echo "Error:"#'
91 + traceback.print_exc()

blha303 revised this gist 1374442556. Go to revision

1 file changed, 1 insertion, 1 deletion

streaminfooutput.txt

@@ -1,4 +1,4 @@
1 - # Output for 'python jtvrtmp.py Ludendi' 2013-07-22
1 + # Output for python -c "import jtvrtmp; print jtvrtmp.getstreaminfo('Ludendi')" 2013-07-22
2 2 {u'360p':
3 3 {u'node': u'video8-2.lax01',
4 4 u'needed_info': u'',

blha303 revised this gist 1374442350. Go to revision

2 files changed, 77 insertions, 14 deletions

jtvrtmp.py

@@ -1,14 +1,15 @@
1 1 #!/usr/bin/env python2
2 2
3 - # rtmpdump parameter generator for justin.tv/twitch.tv streams v5
3 + # rtmpdump parameter generator for justin.tv/twitch.tv streams v6
4 4 # * Usage: jtvrtmp.py channelname [quality]
5 5 # where channelname is the channel name ([twitch/justin].tv/channelname)
6 6 # and quality is an optional parameter with a valid quality setting
7 7 # (360p, 480p, 720p, etc) If quality isn't present, it selects 'live'.
8 - # * if quality is 'live', it picks the quality setting with a +, which
8 + # * if quality is 'live', it picks the quality setting with a +, which
9 9 # is a restream of the video being sent to jtv, no transcoding involved.
10 10 #
11 11 # Changelog:
12 + # v6: Added documentation!
12 13 # v5: Changed to default to 'live' quality instead of 360p, as some streams
13 14 # don't have 360p transcoding; made the code in main() slightly cleaner,
14 15 # possibly shorter.
@@ -27,16 +28,23 @@ import json
27 28 from sys import argv, exit
28 29
29 30 def getswfaddr(channelname):
31 + """ Follows the redirect to get the channel player url, strips out the useragent """
30 32 return urlopen("http://www.justin.tv/widgets/live_embed_player.swf?channel=" + channelname).geturl().split("&userAgent")[0]
31 33
32 34 def getstreaminfo(channelname):
35 + """ returns a dict of {quality: data}. Example output in streaminfooutput.txt """
33 36 data = json.loads(urlopen("http://usher.justin.tv/find/%s.json?type=any" % channelname.lower()).read())
34 37 newd = {}
35 38 for i in data:
36 39 newd[i['display']] = i
37 40 return newd
38 41
39 - def buildcmdline(channelname, data, quality="live"):
42 + def buildcmdline(channelname, data=None, quality="live"):
43 + """ Builds and returns rtmpdump parameter string for loading a twitch/justin.tv channel.
44 + Usage: buildcmdline(channelname as string, data dict from usher.justin.tv, formatted as {quality: data}
45 + Check getstreaminfo() for how to create this dict, or let buildcmdline call it by not setting data. """
46 + if not data:
47 + data = getstreaminfo(channelname)
40 48 if quality == "live":
41 49 for i in data:
42 50 if "+" in i:
@@ -49,19 +57,21 @@ def buildcmdline(channelname, data, quality="live"):
49 57 justinlegacyparams = '-j "%s" ' % data["token"].replace('"', r'\"')
50 58 else:
51 59 justinlegacyparams = ""
52 - out = '-r "%s/%s" %s--swfVfy "%s" -o -' % (data["connect"], data["play"], justinlegacyparams, getswfaddr(channelname))
60 + out = '-r "%s/%s" %s--swfVfy "%s" -v -o -' % (data["connect"], data["play"], justinlegacyparams, getswfaddr(channelname))
53 61 return out
54 62
55 - def main():
56 - if len(argv) < 2:
57 - print "Usage: %s channelname [quality]" % argv[0]
58 - exit(2)
59 - channelname = argv[1]
60 - data = getstreaminfo(channelname)
61 - if len(argv) > 2:
62 - print buildcmdline(channelname, data, quality=argv[2])
63 + def main(args):
64 + """ Returns rtmpdump parameter string for channelname in specified list;
65 + argv is a list, either [channelname] or [channelname, quality] """
66 + if len(args) < 2:
67 + return "Usage: %s channelname [quality]" % args[0]
68 + elif len(args) > 2:
69 + return buildcmdline(args[1], quality=args[2])
63 70 else:
64 - print buildcmdline(channelname, data)
71 + return buildcmdline(args[1])
65 72
66 73 if __name__ == "__main__":
67 - main()
74 + out = main(argv)
75 + print out
76 + if "Usage:" in out:
77 + exit(2)

streaminfooutput.txt(file created)

@@ -0,0 +1,53 @@
1 + # Output for 'python jtvrtmp.py Ludendi' 2013-07-22
2 + {u'360p':
3 + {u'node': u'video8-2.lax01',
4 + u'needed_info': u'',
5 + u'play': u'jtv_SAz4Mud24V7aKSw0',
6 + u'meta_game': u'The Legend of Zelda: Ocarina of Time',
7 + u'cluster': u'lax01',
8 + u'type': u'360p',
9 + u'broadcast_part': 1,
10 + u'persistent': u'true',
11 + u'video_height': 360,
12 + u'token': u'26b8cee84453fd5e370d0ba13108a1ef59c9e58b:{"swfDomains": ["justin.tv", "jtvx.com", "xarth.com","twitchtv.com", "twitch.tv","newjtv.com", "jtvnw.net", "wdtinc.com", "imapweather.com", "facebook.com", "starcrafting.com"], "streamName": "jtv_SAz4Mud24V7aKSw0", "expiration": 1374431364, "geo_ip": "124.169.218.124", "server": "video8-2.lax01"}',
13 + u'connect': u'rtmp://199.9.254.164/app',
14 + u'broadcast_id': 6230376768L,
15 + u'bitrate': 512,
16 + u'display': u'360p',
17 + u'find_type': u'dist'
18 + },
19 + u'480p':
20 + {u'node': u'video2-2.lax01',
21 + u'needed_info': u'',
22 + u'play': u'jtv_yv5WRBFg0ftTWUEO',
23 + u'meta_game': u'The Legend of Zelda: Ocarina of Time',
24 + u'cluster': u'lax01',
25 + u'type': u'480p',
26 + u'broadcast_part': 1,
27 + u'persistent': u'true',
28 + u'video_height': 480,
29 + u'token': u'5e7a6465c72d71b455d0d78249ecf50e8cb64462:{"swfDomains": ["justin.tv", "jtvx.com", "xarth.com", "twitchtv.com", "twitch.tv", "newjtv.com", "jtvnw.net", "wdtinc.com", "imapweather.com", "facebook.com", "starcrafting.com"], "streamName": "jtv_yv5WRBFg0ftTWUEO", "expiration": 1374431364, "geo_ip": "124.169.218.124", "server": "video2-2.lax01"}',
30 + u'connect': u'rtmp://199.9.254.158/app',
31 + u'broadcast_id': 6230376752L,
32 + u'bitrate': 768,
33 + u'display': u'480p',
34 + u'find_type': u'dist'
35 + },
36 + u'720p+':
37 + {u'node': u'video6-1.lax01',
38 + u'needed_info': u'',
39 + u'play': u'jtv_fGEW0H_C_0Dfy2bI',
40 + u'meta_game': u'The Legend of Zelda: Ocarina of Time',
41 + u'cluster': u'lax01',
42 + u'type': u'live',
43 + u'broadcast_part': 6,
44 + u'persistent': u'true',
45 + u'video_height': 720,
46 + u'token': u'88830ed1f5bab63eb512e01d45698106ecb500b1:{"swfDomains": ["justin.tv", "jtvx.com", "xarth.com", "twitchtv.com", "twitch.tv", "newjtv.com", "jtvnw.net", "wdtinc.com", "imapweather.com", "facebook.com", "starcrafting.com"], "streamName": "jtv_fGEW0H_C_0Dfy2bI", "expiration": 1374431364, "geo_ip": "124.169.218.124", "server": "video6-1.lax01"}',
47 + u'connect': u'rtmp://199.9.254.138/app',
48 + u'broadcast_id': 6230375632L,
49 + u'bitrate': 723.296875,
50 + u'display': u'720p+',
51 + u'find_type': u'dist'
52 + }
53 + }

blha303 revised this gist 1374423352. Go to revision

1 file changed, 13 insertions, 11 deletions

jtvrtmp.py

@@ -1,14 +1,17 @@
1 1 #!/usr/bin/env python2
2 2
3 - # rtmpdump parameter generator for justin.tv/twitch.tv streams v4
3 + # rtmpdump parameter generator for justin.tv/twitch.tv streams v5
4 4 # * Usage: jtvrtmp.py channelname [quality]
5 5 # where channelname is the channel name ([twitch/justin].tv/channelname)
6 6 # and quality is an optional parameter with a valid quality setting
7 - # (360p, 480p, 720p, etc) If quality isn't present, it selects 360p.
8 - # * if quality is live, it picks the quality setting with a +, which
7 + # (360p, 480p, 720p, etc) If quality isn't present, it selects 'live'.
8 + # * if quality is 'live', it picks the quality setting with a +, which
9 9 # is a restream of the video being sent to jtv, no transcoding involved.
10 10 #
11 11 # Changelog:
12 + # v5: Changed to default to 'live' quality instead of 360p, as some streams
13 + # don't have 360p transcoding; made the code in main() slightly cleaner,
14 + # possibly shorter.
12 15 # v4: Add shebang line
13 16 # v3: Removed unicode copyright symbol
14 17 # v2: Added exception catching for when a stream is offline or the specified
@@ -33,7 +36,7 @@ def getstreaminfo(channelname):
33 36 newd[i['display']] = i
34 37 return newd
35 38
36 - def buildcmdline(channelname, data, quality="360p"):
39 + def buildcmdline(channelname, data, quality="live"):
37 40 if quality == "live":
38 41 for i in data:
39 42 if "+" in i:
@@ -46,20 +49,19 @@ def buildcmdline(channelname, data, quality="360p"):
46 49 justinlegacyparams = '-j "%s" ' % data["token"].replace('"', r'\"')
47 50 else:
48 51 justinlegacyparams = ""
49 - out = '-r "%s/%s" %s--swfVfy "%s" -v -o -' % (data["connect"], data["play"], justinlegacyparams, getswfaddr(channelname))
52 + out = '-r "%s/%s" %s--swfVfy "%s" -o -' % (data["connect"], data["play"], justinlegacyparams, getswfaddr(channelname))
50 53 return out
51 54
52 55 def main():
53 56 if len(argv) < 2:
54 - print "Usage: %s channelname [quality]"
57 + print "Usage: %s channelname [quality]" % argv[0]
55 58 exit(2)
56 - if len(argv) > 2:
57 - quality = argv[2]
58 - else:
59 - quality = "360p"
60 59 channelname = argv[1]
61 60 data = getstreaminfo(channelname)
62 - print buildcmdline(channelname, data, quality=quality)
61 + if len(argv) > 2:
62 + print buildcmdline(channelname, data, quality=argv[2])
63 + else:
64 + print buildcmdline(channelname, data)
63 65
64 66 if __name__ == "__main__":
65 67 main()

blha303 revised this gist 1374173653. Go to revision

1 file changed, 1 insertion, 1 deletion

jtvrtmp.sh

@@ -11,4 +11,4 @@ die () {
11 11
12 12 (( $# == 1 )) || die "Usage: jtvrtmp.sh channelname [quality]"
13 13 (( $# == 2 )) || echo "rtmpdump $(python jtvrtmp.py $1) | vlc -" | sh; exit
14 - (( $# == 3 )) || echo "rtmpdump $(python jtvrtmp.py $1 $2) | vlc -" | sh; exit
14 + (( $# == 3 )) || echo "rtmpdump $(python jtvrtmp.py $1 $2) | vlc -" | sh; exit

blha303 revised this gist 1374173577. Go to revision

2 files changed, 18 insertions, 1 deletion

jtvrtmp.py

@@ -1,4 +1,6 @@
1 - # rtmpdump parameter generator for justin.tv/twitch.tv streams v3
1 + #!/usr/bin/env python2
2 +
3 + # rtmpdump parameter generator for justin.tv/twitch.tv streams v4
2 4 # * Usage: jtvrtmp.py channelname [quality]
3 5 # where channelname is the channel name ([twitch/justin].tv/channelname)
4 6 # and quality is an optional parameter with a valid quality setting
@@ -7,6 +9,7 @@
7 9 # is a restream of the video being sent to jtv, no transcoding involved.
8 10 #
9 11 # Changelog:
12 + # v4: Add shebang line
10 13 # v3: Removed unicode copyright symbol
11 14 # v2: Added exception catching for when a stream is offline or the specified
12 15 # quality setting is unavailable.

jtvrtmp.sh(file created)

@@ -0,0 +1,14 @@
1 + #!/bin/bash
2 + # Quality setting in this file doesn't work at the moment. I'm open to suggestions.
3 + # Copyright 2013 Steven Smith (blha303). All Rights Reserved.
4 + # New BSD license
5 + # http://www.opensource.org/licenses/BSD-3-Clause
6 +
7 + die () {
8 + echo >&2 "$@"
9 + exit 1
10 + }
11 +
12 + (( $# == 1 )) || die "Usage: jtvrtmp.sh channelname [quality]"
13 + (( $# == 2 )) || echo "rtmpdump $(python jtvrtmp.py $1) | vlc -" | sh; exit
14 + (( $# == 3 )) || echo "rtmpdump $(python jtvrtmp.py $1 $2) | vlc -" | sh; exit

Steven Smith revised this gist 1374171489. Go to revision

1 file changed, 3 insertions, 2 deletions

jtvrtmp.py

@@ -1,4 +1,4 @@
1 - # rtmpdump parameter generator for justin.tv/twitch.tv streams v2
1 + # rtmpdump parameter generator for justin.tv/twitch.tv streams v3
2 2 # * Usage: jtvrtmp.py channelname [quality]
3 3 # where channelname is the channel name ([twitch/justin].tv/channelname)
4 4 # and quality is an optional parameter with a valid quality setting
@@ -7,11 +7,12 @@
7 7 # is a restream of the video being sent to jtv, no transcoding involved.
8 8 #
9 9 # Changelog:
10 + # v3: Removed unicode copyright symbol
10 11 # v2: Added exception catching for when a stream is offline or the specified
11 12 # quality setting is unavailable.
12 13 # v1: Initial release
13 14 #
14 - # Copyright © 2013 Steven Smith (blha303). All Rights Reserved.
15 + # Copyright 2013 Steven Smith (blha303). All Rights Reserved.
15 16 # New BSD license
16 17 # http://www.opensource.org/licenses/BSD-3-Clause
17 18

Steven Smith revised this gist 1374171311. Go to revision

1 file changed, 11 insertions, 2 deletions

jtvrtmp.py

@@ -1,10 +1,16 @@
1 - # rtmpdump parameter generator for justin.tv/twitch.tv streams
1 + # rtmpdump parameter generator for justin.tv/twitch.tv streams v2
2 2 # * Usage: jtvrtmp.py channelname [quality]
3 3 # where channelname is the channel name ([twitch/justin].tv/channelname)
4 4 # and quality is an optional parameter with a valid quality setting
5 5 # (360p, 480p, 720p, etc) If quality isn't present, it selects 360p.
6 6 # * if quality is live, it picks the quality setting with a +, which
7 7 # is a restream of the video being sent to jtv, no transcoding involved.
8 + #
9 + # Changelog:
10 + # v2: Added exception catching for when a stream is offline or the specified
11 + # quality setting is unavailable.
12 + # v1: Initial release
13 + #
8 14 # Copyright © 2013 Steven Smith (blha303). All Rights Reserved.
9 15 # New BSD license
10 16 # http://www.opensource.org/licenses/BSD-3-Clause
@@ -28,7 +34,10 @@ def buildcmdline(channelname, data, quality="360p"):
28 34 for i in data:
29 35 if "+" in i:
30 36 quality = i
31 - data = data[quality]
37 + try:
38 + data = data[quality]
39 + except KeyError:
40 + return '; echo "-------------"; echo "Couldn\'t find stream info for %s, maybe the stream is offline?"; echo "-------------" #' % quality
32 41 if not "live-cdn" in data["connect"] and not "justintvlivefs" in data["connect"]:
33 42 justinlegacyparams = '-j "%s" ' % data["token"].replace('"', r'\"')
34 43 else:

Steven Smith revised this gist 1374167823. Go to revision

1 file changed, 52 insertions

jtvrtmp.py(file created)

@@ -0,0 +1,52 @@
1 + # rtmpdump parameter generator for justin.tv/twitch.tv streams
2 + # * Usage: jtvrtmp.py channelname [quality]
3 + # where channelname is the channel name ([twitch/justin].tv/channelname)
4 + # and quality is an optional parameter with a valid quality setting
5 + # (360p, 480p, 720p, etc) If quality isn't present, it selects 360p.
6 + # * if quality is live, it picks the quality setting with a +, which
7 + # is a restream of the video being sent to jtv, no transcoding involved.
8 + # Copyright © 2013 Steven Smith (blha303). All Rights Reserved.
9 + # New BSD license
10 + # http://www.opensource.org/licenses/BSD-3-Clause
11 +
12 + from urllib2 import urlopen
13 + import json
14 + from sys import argv, exit
15 +
16 + def getswfaddr(channelname):
17 + return urlopen("http://www.justin.tv/widgets/live_embed_player.swf?channel=" + channelname).geturl().split("&userAgent")[0]
18 +
19 + def getstreaminfo(channelname):
20 + data = json.loads(urlopen("http://usher.justin.tv/find/%s.json?type=any" % channelname.lower()).read())
21 + newd = {}
22 + for i in data:
23 + newd[i['display']] = i
24 + return newd
25 +
26 + def buildcmdline(channelname, data, quality="360p"):
27 + if quality == "live":
28 + for i in data:
29 + if "+" in i:
30 + quality = i
31 + data = data[quality]
32 + if not "live-cdn" in data["connect"] and not "justintvlivefs" in data["connect"]:
33 + justinlegacyparams = '-j "%s" ' % data["token"].replace('"', r'\"')
34 + else:
35 + justinlegacyparams = ""
36 + out = '-r "%s/%s" %s--swfVfy "%s" -v -o -' % (data["connect"], data["play"], justinlegacyparams, getswfaddr(channelname))
37 + return out
38 +
39 + def main():
40 + if len(argv) < 2:
41 + print "Usage: %s channelname [quality]"
42 + exit(2)
43 + if len(argv) > 2:
44 + quality = argv[2]
45 + else:
46 + quality = "360p"
47 + channelname = argv[1]
48 + data = getstreaminfo(channelname)
49 + print buildcmdline(channelname, data, quality=quality)
50 +
51 + if __name__ == "__main__":
52 + main()
Newer Older