This repository has been archived by the owner on Jan 2, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathzeyaclient.py
executable file
·154 lines (141 loc) · 5.08 KB
/
zeyaclient.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2009 Phil Sung
#
# This file is part of Zeya.
#
# Zeya is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# Zeya is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Zeya. If not, see <http://www.gnu.org/licenses/>.
# Console frontend to Zeya server.
#
# Usage:
# zeyaclient.py http://server.local:8080
#
# zeyaclient prompts the user for a query, and all songs matching the query are
# played. The query may be matched against the title, artist, or album of the
# song.
import getopt
import os
import readline # Modifies the behavior of raw_input.
import signal
import subprocess
import sys
import time
import urllib2
try:
import json
json.dumps
except (ImportError, AttributeError):
import simplejson as json
class BadArgsError(Exception):
"""
Error due to incorrect command-line invocation of this program.
"""
def __init__(self, message):
self.error_message = message
def __str__(self):
return "Error: %s" % (self.error_message,)
def song_matches(query, song):
"""
Return True if the query matches the given song.
A query might look like "help, the beatles"
A song is considered to match if each comma-separated component of the
query appears somewhere in one of the song's metadata fields. Matching is
case-insensitive.
"""
parts = [part.strip() for part in query.lower().split(",")]
return all(part in song['album'].lower() or part in song['title'].lower() \
or part in song['artist'].lower() for part in parts)
# TODO: refactor the parts that directly interact with the server into a
# separate module.
def run(server_path):
try:
library_file = urllib2.urlopen(server_path + "/getlibrary")
except ValueError, e:
print "Error: %r is not a valid server name." % (server_path,)
if not server_path.lower().startswith("http://"):
print "Don't forget to precede the server name with 'http://'."
print "(The full error text was: '%s')" % (e,)
sys.exit(1)
except urllib2.URLError, e:
print "Error: %s" % (e.reason,)
sys.exit(1)
library_data = json.loads(library_file.read())['library']
print "Loaded %d songs from library." % (len(library_data),)
print 'You can issue queries like: "Beatles" or "help, the beatles"'
while True:
# Prompt user for a query...
try:
query = raw_input("\rQuery? ")
except:
# User pressed C-d or C-c.
print
break
if not query:
break
# ...then play all the songs we can find that match the query.
matching_songs = \
[song for song in library_data if song_matches(query, song)]
for song in matching_songs:
print "\r%s - %s" % (song['title'], song['artist'])
song_url = "%s/getcontent?key=%d" % (server_path, song['key'])
p = subprocess.Popen(["/usr/bin/ogg123", "-q", song_url])
try:
p.communicate()
except KeyboardInterrupt:
# After a single ^C, skip to the next song.
os.kill(p.pid, signal.SIGTERM)
try:
time.sleep(0.5)
except KeyboardInterrupt:
# If ^C^C is typed (within 0.5 sec) then break out back to
# the prompt.
break
def get_options(remaining_args):
"""
Parse the arguments and return a tuple (show_help, server), or raise
BadArgsError if the invocation was not valid.
show_help: whether user requested help information
server: Zeya server to connect to
"""
help_msg = False
try:
opts, file_list = getopt.getopt(remaining_args, "h", ["help"])
except getopt.GetoptError, e:
raise BadArgsError(e.msg)
for flag, value in opts:
if flag in ("-h", "--help"):
help_msg = True
if help_msg:
# With --help, it's ok if the user enters no server path.
return (help_msg, None)
# file_list should contain be a singleton list with the remote server.
if len(file_list) == 0:
raise BadArgsError("Expected server path")
if len(file_list) > 1:
raise BadArgsError("Unexpected argument after server")
return (help_msg, file_list[0])
def print_usage():
print "Usage: %s http://server:8080" % (os.path.basename(sys.argv[0]),)
if __name__ == "__main__":
try:
show_help, server = get_options(sys.argv[1:])
except BadArgsError, e:
print e
print_usage()
sys.exit(1)
if show_help:
print_usage()
sys.exit(0)
run(server)