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