Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

initial import

  • Loading branch information...
commit 84888be3e0b99c28aa828a0687718c2b5ff3fdfd 0 parents
Andrei Savu authored
4 .gitignore
... ... @@ -0,0 +1,4 @@
  1 +*.pyc
  2 +*.swp
  3 +*.mp3
  4 +tags
18 api.py
... ... @@ -0,0 +1,18 @@
  1 +
  2 +class do_about:
  3 + def GET(self):
  4 + return render.api.about(render)
  5 +
  6 +class do_search:
  7 + def GET(self, format):
  8 + if not format:
  9 + format = '.json'
  10 + query = None
  11 + try:
  12 + query = web.input('q').q
  13 + except Exception,e:
  14 + pass
  15 +
  16 + return 'search api' + format + ' q=' + query
  17 +
  18 +
3  db/reset.sh
... ... @@ -0,0 +1,3 @@
  1 +#! /bin/bash
  2 +
  3 +mysql -u root music < schema.sql
13 db/schema.sql
... ... @@ -0,0 +1,13 @@
  1 +
  2 +drop table if exists `ms_files`;
  3 +create table `ms_files` (
  4 + `id` integer not null auto_increment,
  5 + `title` varchar(128) not null,
  6 + `artist` varchar(128) not null,
  7 + `album` varchar(128) not null,
  8 + `year` integer not null,
  9 + `filename` varchar(128) not null,
  10 + `date` timestamp not null,
  11 + primary key(`id`)
  12 +)engine=InnoDb
  13 +
7 doc/player
... ... @@ -0,0 +1,7 @@
  1 +
  2 +<object type="application/x-shockwave-flash" data="player_mp3.swf" width="200" height="20">
  3 + <param name="movie" value="player_mp3.swf" />
  4 + <param name="FlashVars" value="mp3=test.mp3" />
  5 +</object>
  6 +
  7 +
17 doc/readme
... ... @@ -0,0 +1,17 @@
  1 +
  2 +Layers:
  3 + app layer - web.py - stateless
  4 + load balancer + dns roundrobin
  5 + - allocation not application controller
  6 +
  7 +Database:
  8 + mysql optimized for read intesive envs
  9 + master - slave replication
  10 +
  11 +Search:
  12 + solr server ( cluster if needed )
  13 +
  14 +Storage:
  15 + distributed with public visible nodes and
  16 + links to other background nodes
  17 +
91 index.py
... ... @@ -0,0 +1,91 @@
  1 +
  2 +import web, cgi, settings
  3 +import api, storage, search
  4 +import lib.mp3 as mp3
  5 +
  6 +urls = (
  7 + '^/$', 'do_index',
  8 + '^/about[/]?$', 'do_about',
  9 + '^/search[/]?$', 'do_search',
  10 + '^/upload[/]?$', 'do_upload',
  11 + '^/upload/error[/]?', 'do_upload_error',
  12 + '^/api/about[/]?$', 'api.do_about',
  13 + '^/api/search(.*)$', 'api.do_search',
  14 + '^/media/(\d+)$', 'do_media'
  15 +)
  16 +
  17 +app = web.application(urls, globals())
  18 +
  19 +db = web.database(dbn = settings.DB_TYPE,
  20 + host = settings.DB_HOST,
  21 + db = settings.DB_NAME,
  22 + user = settings.DB_USER,
  23 + pw = settings.DB_PASSW)
  24 +
  25 +api.render = render = web.template.render(settings.TEMPLATE_FOLDER, base='base')
  26 +
  27 +class do_index:
  28 + def GET(self):
  29 + files = db.select('ms_files', order='date desc', limit=10)
  30 + return render.index(files)
  31 +
  32 +class do_about:
  33 + def GET(self):
  34 + return render.about(title='About')
  35 +
  36 +class do_search:
  37 + def GET(self):
  38 + input = web.input(q='')
  39 + q = input.q
  40 + files = db.select('ms_files', order='date desc', limit=10)
  41 + return render.search(files, query=q, title='Search')
  42 +
  43 +class do_upload_error:
  44 + def GET(self):
  45 + return render.upload_error(title='Upload error')
  46 +
  47 +class do_upload:
  48 + def GET(self):
  49 + return render.upload(title='Upload')
  50 +
  51 + def get_mp3_info(self, file):
  52 + file.seek(0)
  53 + info = mp3.mp3info(fp=file)
  54 + file.seek(0)
  55 + info.update(mp3.get_mp3tag(fp=file))
  56 + return info
  57 +
  58 + def POST(self):
  59 + cgi.maxlen = settings.MAX_UP_FILE_SIZE
  60 +
  61 + input = web.input(file={})
  62 + if input.file.file:
  63 + try:
  64 + info = self.get_mp3_info(input.file.file)
  65 + info['FILENAME'] = input.file.filename
  66 + except:
  67 + web.seeother('/upload/error')
  68 +
  69 + id = storage.save(info, input.file.file, db)
  70 + search.update(id, info)
  71 +
  72 + raise web.seeother('/')
  73 +
  74 +class do_media:
  75 + def GET(self, id):
  76 + path = "/static/upload/%d.mp3" % int(id)
  77 + raise web.seeother(path)
  78 +
  79 +
  80 +def notfound():
  81 + return web.notfound(render.notfound(render))
  82 +app.notfound = notfound
  83 +
  84 +
  85 +def internalerror():
  86 + return web.internalerror(render.internalerror(render))
  87 +app.internalerror = internalerror
  88 +
  89 +if __name__ == "__main__":
  90 + app.run()
  91 +
0  lib/__init__.py
No changes.
437 lib/mp3.py
... ... @@ -0,0 +1,437 @@
  1 +#! /usr/local/bin/python --
  2 +
  3 +"""
  4 +usage: %(progname)s [args]
  5 +
  6 + --cat [files] -- categorize a bunch of files
  7 +
  8 + mp3info(filename)
  9 + - reads the mp3 header and returns a dictionary containing
  10 + these fields:
  11 +
  12 + VERSION
  13 + MM - number of minutes
  14 + SS - number of seconds
  15 + STEREO - 0-mono, 1-stereo
  16 + LAYER - MPEG layer 2 or 3
  17 + MODE
  18 + COPYRIGHT
  19 + BITRATE
  20 + FREQUENCY
  21 +
  22 + get_mp3tag(filename)
  23 + - finds the id3 tag of the mp3 and returns a dictionary
  24 + containing these fields: TITLE, ARTIST, ALBUM, YEAR, COMMENT
  25 +
  26 + get_xing_header(filename)
  27 + - returns the XING header (flags, frames, bytes) of the mp3 or
  28 + None.
  29 +
  30 + Categorize(fn)
  31 + - creates a directory called 'cats' with three subdirectories
  32 + 'GENRE_ARTIST', 'GENRE', and 'ARTIST'. It reads the ID3 tag
  33 + off of the mp3 and creates a three symlinks in this
  34 + directory structure. All files without ID3 tags will have a
  35 + genre and artist of 'Unknown'.
  36 +
  37 +
  38 +"""
  39 +
  40 +import os, sys, string, time, getopt
  41 +
  42 +mp3_genres = ['Blues', 'Classic Rock', 'Country', 'Dance',
  43 + 'Disco', 'Funk', 'Grunge', 'Hip-Hop', 'Jazz',
  44 + 'Metal', 'New Age', 'Oldies', 'Other', 'Pop',
  45 + 'R&B', 'Rap', 'Reggae', 'Rock', 'Techno',
  46 + 'Industrial', 'Alternative', 'Ska', 'Death Metal',
  47 + 'Pranks', 'Soundtrack', 'Euro-Techno', 'Ambient',
  48 + 'Trip-Hop', 'Vocal', 'Jazz+Funk', 'Fusion', 'Trance',
  49 + 'Classical', 'Instrumental', 'Acid', 'House', 'Game',
  50 + 'Sound Clip', 'Gospel', 'Noise', 'AlternRock', 'Bass',
  51 + 'Soul', 'Punk', 'Space', 'Meditative',
  52 + 'Instrumental Pop', 'Instrumental Rock', 'Ethnic',
  53 + 'Gothic', 'Darkwave', 'Techno-Industrial', 'Electronic',
  54 + 'Pop-Folk', 'Eurodance', 'Dream', 'Southern Rock',
  55 + 'Comedy', 'Cult', 'Gangsta', 'Top 40', 'Christian Rap',
  56 + 'Pop/Funk', 'Jungle', 'Native American', 'Cabaret',
  57 + 'New Wave', 'Psychadelic', 'Rave', 'Showtunes',
  58 + 'Trailer', 'Lo-Fi', 'Tribal', 'Acid Punk',
  59 + 'Acid Jazz', 'Polka', 'Retro', 'Musical',
  60 + 'Rock & Roll', 'Hard Rock', ]
  61 +
  62 +winamp_genres = mp3_genres + \
  63 + ['Folk','Folk-Rock','National Folk','Swing','Fast Fusion','Bebob','Latin',
  64 + 'Revival','Celtic','Bluegrass','Avantgarde','Gothic Rock','Progressive Rock',
  65 + 'Psychedelic Rock','Symphonic Rock','Slow Rock','Big Band','Chorus',
  66 + 'Easy Listening','Acoustic','Humour','Speech','Chanson','Opera',
  67 + 'Chamber Music','Sonata','Symphony','Booty Bass','Primus','Porn Groove',
  68 + 'Satire','Slow Jam','Club','Tango','Samba','Folklore','Ballad',
  69 + 'Power Ballad','Rhythmic Soul','Freestyle','Duet','Punk Rock','Drum Solo',
  70 + 'Acapella','Euro-House','Dance Hall']
  71 +
  72 +
  73 +t_bitrate = [
  74 + [
  75 + [0,32,48,56,64,80,96,112,128,144,160,176,192,224,256],
  76 + [0,8,16,24,32,40,48,56,64,80,96,112,128,144,160],
  77 + [0,8,16,24,32,40,48,56,64,80,96,112,128,144,160]
  78 + ],
  79 + [
  80 + [0,32,64,96,128,160,192,224,256,288,320,352,384,416,448],
  81 + [0,32,48,56,64,80,96,112,128,160,192,224,256,320,384],
  82 + [0,32,40,48,56,64,80,96,112,128,160,192,224,256,320]
  83 + ]
  84 + ]
  85 +
  86 +t_sampling_freq = [
  87 + [22050, 24000, 16000],
  88 + [44100, 48000, 32000]
  89 + ]
  90 +
  91 +frequency_tbl = {0:22050,1:24000,2:16000,3:44100,4:48000,5:32000,6:64000}
  92 +
  93 +
  94 +def getword(fp, off):
  95 + fp.seek(off, 0)
  96 + word = fp.read(4)
  97 + return word
  98 +
  99 +def get_l4 (s):
  100 + return reduce (lambda a,b: ((a<<8) + b), map (long, map (ord, s)))
  101 +
  102 +def get_xing_header (f):
  103 + where = f.tell()
  104 + try:
  105 + f.seek(0)
  106 + b = f.read(8192)
  107 + i = string.find (b, 'Xing')
  108 + if i > 0:
  109 + # 32-bit fields; "Xing", flags, frames, bytes, 100 toc
  110 + i = i + 4
  111 + flags = get_l4 (b[i:i+4]); i = i + 4
  112 + frames = get_l4 (b[i:i+4]); i = i + 4
  113 + bytes = get_l4 (b[i:i+4]); i = i + 4
  114 + return flags, frames, bytes
  115 + else:
  116 + return None
  117 + finally:
  118 + f.seek (where)
  119 +
  120 +MPG_MD_STEREO = 0
  121 +MPG_MD_JOINT_STEREO = 1
  122 +MPG_MD_DUAL_CHANNEL = 2
  123 +MPG_MD_MONO = 3
  124 +
  125 +def get_newhead (word):
  126 + word = get_l4 (word)
  127 + if (word & (1<<20)):
  128 + if (word & (1<<19)):
  129 + lsf = 0
  130 + else:
  131 + lsf = 1
  132 + mpeg25 = 0
  133 + else:
  134 + lsf = 1
  135 + mpeg25 = 1
  136 + lay = 4 - ((word>>17)&3)
  137 + if mpeg25:
  138 + sampling_frequency = 6 + ((word>>10) & 3)
  139 + else:
  140 + sampling_frequency = ((word>>10)&3) + (lsf * 3)
  141 + error_protection = ((word>>16)&1) ^ 1
  142 + bitrate_index = (word>>12) & 0xf
  143 + padding = ((word >> 9) & 0x1)
  144 + extension = ((word >> 8) & 0x1)
  145 + mode = ((word >> 6) & 0x3)
  146 + mode_ext = ((word >> 4) & 0x3)
  147 + copyright = ((word >> 3) & 0x1)
  148 + original = ((word >> 2) & 0x1)
  149 + emphasis = word & 0x3
  150 +
  151 + if mode == MPG_MD_MONO:
  152 + stereo = 1
  153 + else:
  154 + stereo = 2
  155 +
  156 +
  157 + return locals()
  158 + import pprint
  159 + pprint.pprint (locals())
  160 +
  161 +def get_head(word):
  162 + if len(word) != 4:
  163 + return {}
  164 + l = ord(word[0])<<24|ord(word[1])<<16|ord(word[2])<<8|ord(word[3])
  165 +
  166 + id = (l>>19) & 1
  167 + layer = (l>>17) & 3
  168 + protection_bit = (l>>16) & 1
  169 + bitrate_index = (l>>12) & 15
  170 + sampling_freq = (l>>10) & 3
  171 + padding_bit = (l>>9) & 1
  172 + private_bit = (l>>8) & 1
  173 + mode = (l>>6) & 3
  174 + mode_extension = (l>>4) & 3
  175 + copyright = (l>>3) & 1
  176 + original = (l>>2) & 1
  177 + emphasis = (l>>0) & 1
  178 + version_index = (l>>19) & 3
  179 + bytes = l
  180 +
  181 +## for k,v in vars().items():
  182 +## print k,v
  183 +
  184 + try:
  185 + bitrate = t_bitrate[id][3-layer][bitrate_index]
  186 + except IndexError:
  187 + bitrate = 0
  188 +
  189 + try:
  190 + fs = t_sampling_freq[id][sampling_freq]
  191 + except IndexError:
  192 + fs = 0
  193 +
  194 + return vars()
  195 +
  196 +def is_mp3(h):
  197 + #if h['bytes'] == -1: return 0
  198 + if not (h['bitrate_index'] == 0 or \
  199 + h['version_index'] == 1 or \
  200 + ((h['bytes'] & 0xFFE00000) != 0xFFE00000) or \
  201 + (not h['fs']) or \
  202 + (not h['bitrate'])):
  203 + return 1
  204 + return 0
  205 +
  206 +def get_v2head(fp):
  207 + fp.seek(0,0)
  208 + word = fp.read(3)
  209 + if word != "ID3": return 0
  210 +
  211 + bytes = fp.read(2)
  212 + major_version = ord(bytes[0])
  213 + minor_version = ord(bytes[1])
  214 +
  215 + version = "ID3v2.%d.%d" % (major_version, minor_version)
  216 + bytes = fp.read(1)
  217 + unsync = (ord(bytes)>>7) & 1
  218 + ext_header = (ord(bytes)>>6) & 1
  219 + experimental = (ord(bytes)>>5) & 1
  220 +
  221 + bytes = fp.read(4)
  222 + tagsize = 0
  223 +
  224 + for i in range(4):
  225 + tagsize = tagsize + ord(bytes[3-i])*128*i
  226 +
  227 + if ext_header:
  228 + ext_header_size = ext_header_size + 10
  229 + bytes = fp.read(4)
  230 +
  231 + return vars()
  232 +
  233 +def mp3info(fn=None, fp=None):
  234 + if not fn and not fp:
  235 + return {}
  236 + off = 0
  237 + eof = 0
  238 + h = 0
  239 + i = 0
  240 + tot = 4096
  241 +
  242 + if fn and os.stat(fn)[6] == 0:
  243 + return {}
  244 +
  245 + if not fp:
  246 + fp = open(fn)
  247 + word = getword(fp, off)
  248 +
  249 + if off==0:
  250 + id3v2 = get_v2head(fp)
  251 + if id3v2:
  252 + off = off + id3v2['tagsize']
  253 + tot = tot + off
  254 + word = getword(fp, off)
  255 +
  256 + nh = get_newhead (word)
  257 +
  258 + vbr = 0
  259 + xh = get_xing_header (fp)
  260 + if xh:
  261 + flags, xing_frames, xing_bytes = xh
  262 + if (flags & 0x08):
  263 + vbr = 1
  264 +
  265 + try:
  266 + if vbr:
  267 + tpf = float([0,384,1152,1152][int(nh['lay'])])
  268 + tpf = tpf / ([44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000][int(nh['sampling_frequency'])] << nh['lsf'])
  269 + except IndexError,e:
  270 + return {}
  271 + while 1:
  272 + h = get_head(word)
  273 + if not h: break
  274 + off=off+1
  275 + word = getword(fp, off)
  276 + if off>tot:
  277 + return {}
  278 + if is_mp3(h): break
  279 +
  280 +
  281 + fp.seek(0, 2)
  282 + eof = fp.tell()
  283 +
  284 + try:
  285 + fp.seek(-128, 2)
  286 + except IOError, reason:
  287 + return {}
  288 +
  289 +
  290 + if h['id']:
  291 + h['mean_frame_size'] = (144000. * h['bitrate']) / h['fs']
  292 + else:
  293 + h['mean_frame_size'] = (72000. * h['bitrate']) / h['fs']
  294 +
  295 + h['layer'] = h['mode']
  296 + h['freq_idx'] = 3*h['id'] + h['sampling_freq']
  297 +
  298 + h['length'] = ((1.0*eof-off) / h['mean_frame_size']) * ((115200./2)*(1.+h['id']))/(1.0*h['fs'])
  299 + h['secs'] = int(h['length'] / 100);
  300 +
  301 +
  302 + i = {}
  303 + i['VERSION'] = h['id']
  304 + i['MM'] = int(h['secs']/60)
  305 + i['SS'] = h['secs']%60
  306 + i['STEREO'] = not(h['mode'] == 3)
  307 + if h['layer'] >= 0:
  308 + if h['layer'] == 3:
  309 + i['LAYER'] = 2
  310 + else:
  311 + i['LAYER'] = 3
  312 + else:
  313 + i['LAYER'] = ''
  314 + i['MODE'] = h['mode']
  315 + i['COPYRIGHT'] = h['copyright']
  316 + if h['bitrate'] >=0:
  317 + i['BITRATE'] = h['bitrate']
  318 + else:
  319 + i['BITRATE'] = ''
  320 + if h['freq_idx'] >= 0:
  321 + i['FREQUENCY'] = frequency_tbl[h['freq_idx']]
  322 + else:
  323 + i['FREQUENCY'] = ''
  324 +
  325 + return i
  326 +
  327 +def get_mp3tag(fn=None, fp=None):
  328 + if not fn and not fp:
  329 + return {}
  330 + if fn and os.stat(fn)[6] == 0:
  331 + return {}
  332 +
  333 + try:
  334 + if not fp:
  335 + fp = open(fn)
  336 + except IOError, reason:
  337 + return {}
  338 +
  339 + try:
  340 + fp.seek(-128, 2)
  341 + except IOError, reason:
  342 + return {}
  343 +
  344 + line = None
  345 + while 1:
  346 + l = fp.readline()
  347 + if not l: break
  348 + line = l
  349 +
  350 + id = {}
  351 + if line[:3] == 'TAG':
  352 + v1 = 1
  353 + i = 0; j = i + 3
  354 + #id['d1'] = string.strip(line[i:j])
  355 + i = j; j = i + 30
  356 + id['TITLE'] = string.strip(line[i:j])
  357 + i = j; j = i + 30
  358 + id['ARTIST'] = string.strip(line[i:j])
  359 + i = j; j = i + 30
  360 + id['ALBUM'] = string.strip(line[i:j])
  361 + i = j; j = i + 4
  362 + id['YEAR'] = string.strip(line[i:j])
  363 + i = j; j = i + 28
  364 + id['COMMENT'] = string.strip(line[i:j])
  365 +
  366 + genre = ord(line[-1])
  367 + try:
  368 + id['GENRE'] = winamp_genres[ord(line[-1])]
  369 + except IndexError:
  370 + id['GENRE'] = "Unknown"
  371 +
  372 +
  373 + return id
  374 +
  375 +def Categorize(fn):
  376 + i1 = mp3info(fn)
  377 + i2 = get_mp3tag(fn)
  378 +
  379 + path1 = "cats/GENRE_ARIST/%s/%s" % (i2.get('GENRE', "Unknown"), i2.get('ARTIST', "Unknown"))
  380 + path2 = "cats/GENRE/%s" % (i2.get('GENRE', "Unknown"), )
  381 + path3 = "cats/ARIST/%s" % (i2.get('ARTIST', "Unknown"), )
  382 +
  383 + path1 = string.replace(path1, "\0", "_")
  384 + path1 = string.replace(path1, " ", "_")
  385 + path2 = string.replace(path2, "\0", "_")
  386 + path2 = string.replace(path2, " ", "_")
  387 + path3 = string.replace(path3, "\0", "_")
  388 + path3 = string.replace(path3, " ", "_")
  389 +
  390 + if not os.path.isdir(path1):
  391 + os.makedirs(path1)
  392 + if not os.path.isdir(path2):
  393 + os.makedirs(path2)
  394 + if not os.path.isdir(path3):
  395 + os.makedirs(path3)
  396 + base, ffn = os.path.split(fn)
  397 +
  398 + try: os.symlink(fn, os.path.join(path1, ffn))
  399 + except: pass
  400 + try: os.symlink(fn, os.path.join(path2, ffn))
  401 + except: pass
  402 + try: os.symlink(fn, os.path.join(path3, ffn))
  403 + except: pass
  404 +
  405 +def usage(progname):
  406 + print __doc__ % vars()
  407 +
  408 +def main(argv, stdout, environ):
  409 + progname = argv[0]
  410 + list, args = getopt.getopt(argv[1:], "", ["help", "cat"])
  411 +
  412 + if len(args) == 0:
  413 + usage(progname)
  414 + return
  415 + for (field, val) in list:
  416 + if field == "--help":
  417 + usage(progname)
  418 + return
  419 + elif field == "--cat":
  420 + for fn in args:
  421 + Categorize(fn)
  422 + return
  423 +
  424 + for fn in args:
  425 + print fn
  426 + i1 = mp3info(fn)
  427 + for k,v in i1.items():
  428 + print k,v
  429 +
  430 + i2 = get_mp3tag(fn)
  431 + for k,v in i2.items():
  432 + print k,v
  433 + print
  434 +
  435 +
  436 +if __name__ == "__main__":
  437 + main(sys.argv, sys.stdout, os.environ)
7 search.py
... ... @@ -0,0 +1,7 @@
  1 +
  2 +import web
  3 +
  4 +def update(id, info):
  5 + web.debug('Adding to search')
  6 + pass
  7 +
13 settings.py
... ... @@ -0,0 +1,13 @@
  1 +"""
  2 +A bunch of application related settings
  3 +"""
  4 +
  5 +TEMPLATE_FOLDER = 'templates/'
  6 +MAX_UP_FILE_SIZE = 10 * 1024 * 1024 # 10 MB
  7 +
  8 +DB_TYPE = 'mysql'
  9 +DB_HOST = 'localhost'
  10 +DB_NAME = 'music'
  11 +DB_USER = 'root'
  12 +DB_PASSW = ''
  13 +
BIN  static/player_mp3.swf
Binary file not shown
3  static/style.css
... ... @@ -0,0 +1,3 @@
  1 +
  2 +
  3 +
15 storage.py
... ... @@ -0,0 +1,15 @@
  1 +
  2 +import web
  3 +
  4 +def db_save(info, db):
  5 + return db.insert('ms_files', filename=info['FILENAME'])
  6 +
  7 +def save(info, fp, db):
  8 + id = db_save(info, db)
  9 + dest = "static/upload/%d.mp3" % id
  10 + f = open(dest, 'w')
  11 + fp.seek(0)
  12 + f.write(fp.read())
  13 + f.close()
  14 + return id
  15 +
9 templates/about.html
... ... @@ -0,0 +1,9 @@
  1 +$def with(title=None)
  2 +$var title: $title
  3 +
  4 +<h3>About MusicShare</h3>
  5 +
  6 +Nice &amp; simple:<br />
  7 +- search and listen<br />
  8 +- or upload
  9 +
6 templates/api/about.html
... ... @@ -0,0 +1,6 @@
  1 +$def with (title=None)
  2 +$var title: $title
  3 +
  4 +<h3>API: Developer docs</h3>
  5 +
  6 +
30 templates/base.html
... ... @@ -0,0 +1,30 @@
  1 +$def with (content)
  2 +<html>
  3 + <head>
  4 +$if 'title' in content and content.title:
  5 + <title>$content.title - MusicShare</title>
  6 +$else:
  7 + <title>MusicShare - Find any music</title>
  8 +
  9 + <link rel="stylesheet" type="text/css" href="/static/style.css" />
  10 + </head>
  11 + <body>
  12 + <div class="header">
  13 + <h1>MusicShare - Find any music</h1>
  14 + </div>
  15 + <div class="content">
  16 +$:content
  17 + </div>
  18 + <div class="footer">
  19 + <ul>
  20 + <li><a href="/">home</a></li>
  21 + <li><a href="/upload">upload</a></li>
  22 + <li><a href="/api/about">api</a></li>
  23 + <li><a href="/search">search</a></li>
  24 + <li><a href="/about">about</a></li>
  25 + </ul>
  26 + </div>
  27 +
  28 + </body>
  29 +</html>
  30 +
33 templates/index.html
... ... @@ -0,0 +1,33 @@
  1 +$def with (files=[], title=None)
  2 +$var title: $title
  3 +
  4 +<form class="searchForm" action="/search" method="GET">
  5 +<fieldset>
  6 +<input type="text" name="q" />
  7 +<button type="submit">Search</button>
  8 +</fieldset>
  9 +</form>
  10 +
  11 +<form class="uploadForm" action="/upload" method="POST" enctype="multipart/form-data">
  12 +<fieldset>
  13 +<input type="file" name="file" />
  14 +<button type="submit">Upload</button>
  15 +</fieldset>
  16 +</form>
  17 +
  18 +<h1>Last 10 uploaded</h1>
  19 +$for file in files:
  20 + $if file.title:
  21 + <b>Title:</b> $file.title <br />
  22 + $if file.artist:
  23 + <b>Artist:</b> $file.artist <br />
  24 + $if file.album:
  25 + <b>Album:</b> $file.album <br />
  26 + <b>Original:</b> $file.filename <br />
  27 + <b>Player:</b>
  28 + <object type="application/x-shockwave-flash" data="/static/player_mp3.swf" width="200" height="20">
  29 + <param name="movie" value="/static/player_mp3.swf" />
  30 + <param name="FlashVars" value="mp3=/media/$(file.id)&amp;showstop=1" />
  31 + </object>
  32 + <hr />
  33 +
5 templates/internalerror.html
... ... @@ -0,0 +1,5 @@
  1 +$def with (render=None)
  2 +
  3 +MusicShare Error: Internal server error
  4 +
  5 +
5 templates/notfound.html
... ... @@ -0,0 +1,5 @@
  1 +$def with (render=None)
  2 +
  3 +MusicShare Error: Requested page was not found
  4 +
  5 +
21 templates/search.html
... ... @@ -0,0 +1,21 @@
  1 +$def with(files, query='', title=None)
  2 +$var title: $title
  3 +
  4 +<form action="/search" method="GET">
  5 +<fieldset>
  6 +<input type="text" name="q" value="$query" />
  7 +<button type="submit">Search</button>
  8 +</fieldset>
  9 +</form>
  10 +
  11 +$for file in files:
  12 + <b>Original:</b> $file.filename<br />
  13 + <b>Player:</b>
  14 + <object type="application/x-shockwave-flash" data="/static/player_mp3.swf" width="200" height="20">
  15 + <param name="movie" value="/static/player_mp3.swf" />
  16 + <param name="FlashVars" value="mp3=/media/$(file.id)&amp;showstop=1" />
  17 + </object>
  18 + <hr />
  19 +
  20 +
  21 +
11 templates/upload.html
... ... @@ -0,0 +1,11 @@
  1 +$def with (title=None)
  2 +$var title: $title
  3 +
  4 +<form action="/upload" method="POST" enctype="multipart/form-data">
  5 +<fieldset>
  6 +<input type="file" name="file" />
  7 +<button type="submit">Upload</button>
  8 +</fieldset>
  9 +</form>
  10 +
  11 +
5 templates/upload_error.html
... ... @@ -0,0 +1,5 @@
  1 +$def with (title=None)
  2 +$var title: $title
  3 +
  4 +Upload error. Check file format and try again.
  5 +
1  web
1  webpy
... ... @@ -0,0 +1 @@
  1 +Subproject commit 7b97f55dd23f31e58ed01122f9e65899455ec824

0 comments on commit 84888be

Please sign in to comment.
Something went wrong with that request. Please try again.