3edb9abe9555838b19e64223f4226783e320d429
[WebKit-https.git] / LayoutTests / http / tests / media / resources / serve_video.py
1 #!/usr/bin/env python3
2
3 # This script is based on the work done by gadgetguru
4 # <david@vuistbijl.nl> at
5 # https://github.com/gadgetguru/PHP-Streaming-Audio and released
6 # under the Public Domain.
7
8 import json
9 import math
10 import os
11 import sys
12 import time
13 from datetime import datetime
14 from urllib.parse import parse_qs
15
16 https = os.environ.get('HTTPS', None)
17
18 radio_url = ''
19 if https is None:
20     radio_url += 'https://'
21 else:
22     radio_url += 'http://'
23 radio_url += '{}{}'.format(os.environ.get('HTTP_HOST', ''), os.environ.get('REQUEST_URI', ''))
24
25 query = parse_qs(os.environ.get('QUERY_STRING', ''), keep_blank_values=True)
26
27 name = query.get('name', [''])[0]
28 media_directory = ''
29 if name != '':
30     media_directory = os.path.abspath(os.path.dirname(name))
31 file_name = name
32
33 # Set Variables
34 settings = {
35     'chunkSize': int(query.get('chunkSize', [1024 * 256])[0]),
36     'databaseFile': 'metadata.db',
37     'httpStatus': '500 Internal Server Error',
38     'mediaDirectory': media_directory,
39     'mimeType': query.get('type', [''])[0],
40     'radioGenre': 'Rock',
41     'radioName': 'WebKit Test Radio',
42     'radioUrl': radio_url,
43     'setContentLength': query.get('content-length', ['yes'])[0],
44     'setIcyData': query.get('icy-data', ['no'])[0],
45     'supportRanges': query.get('ranges', ['yes'])[0],
46     'stallOffset': int(query.get('stallOffset', [0])[0]),
47     'stallDuration': int(query.get('stallDuration', [2])[0]),
48 }
49
50
51 def answering():
52     sys.stdout.write(
53         'status: {}\r\n'
54         'Connection: close\r\n'.format(settings['httpStatus'][0:3])
55     )
56
57     if settings['httpStatus'].startswith('500'):
58         sys.stdout.write(
59             'Content-Type: text/html\r\n\r\n'
60             '<html><body><h1>{}</h1><p/></body></html>'.format(settings['httpStatus'])
61         )
62         sys.stdout.flush()
63         sys.exit(0)
64
65     file_size = os.path.getsize(file_name)
66     last_modified = datetime.utcnow()
67     sys.stdout.write(
68         'Last-Modified: {} GMT\r\n'
69         'Cache-Control: no-cache\r\n'
70         'Etag: "{}-{}"\r\n'.format(last_modified.strftime('%a, %d %b %Y %H:%M:%S'), file_size, str(os.stat(file_name).st_mtime).split('.')[0])
71     )
72
73     if settings['setIcyData'] == 'yes':
74         bit_rate = math.ceil(play_files[len(play_files) - 1]['mimeType'] / 1000)
75         if settings['mimeType'] == '':
76             settings['mimeType'] = play_files[len(play_files) - 1]['mimeType']
77
78         sys.stdout.write(
79             'icy-notice1: <BR>This stream requires a shoutcast/icecast compatible player.<BR>\r\n'
80             'icy-notice2: WebKit Stream Test<BR>\r\n'
81             'icy-name: {name}\r\n'
82             'icy-genre: {genre}\r\n'
83             'icy-url: {url}\r\n'
84             'icy-pub: 1\r\n'
85             'icy-br: {rate}\r\n'.format(name=settings['radioName'], genre=settings['radioGenre'], url=settings['radioUrl'], rate=bit_rate)
86         )
87
88     sys.stdout.write('Content-Type: {}\r\n'.format(settings['mimeType']))
89
90     if settings['supportRanges'] != 'no':
91         sys.stdout.write('Accept-Ranges: bytes\r\n')
92     if settings['setContentLength'] != 'no':
93         sys.stdout.write('Content-Length: {}\r\n'.format(end - start + 1))
94     if content_range is not None:
95         sys.stdout.write('Content-Range: bytes {}-{}/{}\r\n'.format(start, end, file_size))
96     sys.stdout.write('\r\n')
97
98     offset = start
99     open_file = open(file_name, 'rb')
100     content = open_file.read()
101
102     stalled_once = False
103     while offset <= end:
104         read_size = min(settings['chunkSize'], (end - offset) + 1)
105         stall_now = False
106         if not stalled_once and settings['stallOffset'] >= offset and settings['stallOffset'] < offset + read_size:
107             read_size = min(settings['chunkSize'], settings['stallOffset'] - offset)
108             stall_now = True
109
110         buff = content[offset:read_size]
111         read_length = len(buff)
112
113         sys.stdout.buffer.write(buff)
114         sys.stdout.flush()
115         offset += read_length
116
117         if stall_now:
118             time.sleep(settings['stallDuration'])
119             stalled_once = True
120
121     open_file.close()
122     sys.exit(0)
123
124
125 if query.get('name', [None])[0] is None:
126     sys.stderr.write('You have not specified a \'name\' parameter.\n')
127     answering()
128
129 if not os.path.isfile(file_name):
130     sys.stderr.write('The file \'{}\' doesn\'t exist.\n'.format(file_name))
131     answering()
132 settings['databaseFile'] = settings['mediaDirectory'] + '/' + settings['databaseFile']
133
134 if settings['setIcyData'] != 'yes' and settings['mimeType'] == '':
135     sys.stderr.write('You have not specified a \'type\' parameter.\n')
136     answering()
137
138 if settings['setIcyData'] == 'yes':
139     if not os.path.isfile(settings['databaseFile']):
140         # If the metadata database file doesn't exist it has to
141         # be create previously.
142         #
143         # Check the instructions about how to create it from the
144         # create-id3-db.php script file in this same directory.
145
146         sys.stderr.write('The metadata database doesn\'t exist. To create one, check the script \'create-id3-db.php\'.\n')
147         answering()
148
149     play_files = {}
150     with open(settings['databaseFile'], 'r') as file:
151         play_files = json.loads(file.read())
152     sys.stderr.write('\n{}\n'.format(play_files))
153
154     file_in_db = False
155     for play_file in play_files:
156         if file_name.split('/')[-1] == play_file['fileName']:
157             file_in_db = True
158             break
159
160     if not file_in_db:
161         sys.stderr.write('The requested file is not in the database.\n')
162         answering()
163
164 # We have everything that's needed to send the media file
165 file_size = os.path.getsize(file_name)
166 if settings['stallOffset'] > file_size:
167     sys.stderr.write('The \'stallOffset\' offset parameter is greater than file size ({}).\n'.format(file_size))
168     answering()
169
170 start = 0
171 end = file_size - 1
172 content_range = None
173 if settings['supportRanges'] != 'no' and os.environ.get('HTTP_RANGE', None) is not None:
174     content_range = os.environ.get('HTTP_RANGE')
175 if content_range is not None:
176     rng = content_range[len('bytes='):].split('-')
177     start = int(rng[0])
178     if len(rng) > 1 and rng[1] != '':
179         end = int(rng[1])
180     settings['httpStatus'] = '206 Partial Content'
181 else:
182     settings['httpStatus'] = '200 OK'
183
184 answering()