Skip to content

Commit

Permalink
Migrate source code to python version 3
Browse files Browse the repository at this point in the history
This is not trivial cause this project strongly relies on twisted project and most of the calls targeting the twisted module now requires string bytes...so...we must adapt to the new situation:

  - Most of the changes has been tested and the media server is working as expected.
  - Some minor fixes has been applied
  - Some lines has been shortened.
  - The log module has been rewrote to support coloring and the format of the log messages now is the same for inherited classes of Loggable class and non Loggable classes.
  - Rewrote most of the tests to adapt to new python version

Note: all the plugins and backends should be reviewed and fully tested.
  • Loading branch information
opacam committed Aug 5, 2018
1 parent 79e984e commit 92d886a
Show file tree
Hide file tree
Showing 84 changed files with 1,649 additions and 1,311 deletions.
16 changes: 8 additions & 8 deletions bin/cohen
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ def daemonize():
os.setsid()
if os.fork(): # launch child and...
os._exit(0) # kill off parent again.
os.umask(077)
os.umask(0o77)
null = os.open('/dev/null', os.O_RDWR)
for i in range(3):
try:
os.dup2(null, i)
except OSError, e:
except OSError as e:
if e.errno != errno.EBADF:
raise
os.close(null)
Expand Down Expand Up @@ -78,7 +78,7 @@ if __name__ == '__main__':
from coherence.base import Plugins
# hack: avoid plugins are displaying there help message
sys.argv = sys.argv[:1]
p = Plugins().keys()
p = list(Plugins().keys())
p.sort()
self.epilog = 'Available backends are: %s' % ', '.join(p)
optparse.OptionParser.print_help(self, file)
Expand Down Expand Up @@ -108,15 +108,15 @@ if __name__ == '__main__':
try:
daemonize()
except:
print traceback.format_exc()
print(traceback.format_exc())

config = {}

if options.configfile:
try:
config = ConfigObj(options.configfile)
except IOError:
print "Config file %r not found, ignoring" % options.configfile
print("Config file %r not found, ignoring" % options.configfile)
pass

if 'logging' not in config:
Expand All @@ -126,7 +126,7 @@ if __name__ == '__main__':
config['logging']['logfile'] = options.logfile

# copy options passed by -o/--option into config
for k, v in options.options.items():
for k, v in list(options.options.items()):
if k == 'logfile':
continue
config[k] = v
Expand All @@ -142,7 +142,7 @@ if __name__ == '__main__':
from twisted.internet import glib2reactor
glib2reactor.install()
except AssertionError:
print "error installing glib2reactor"
print("error installing glib2reactor")

if options.plugins:
plugins = config.get('plugin')
Expand Down Expand Up @@ -172,7 +172,7 @@ if __name__ == '__main__':
try:
plugins.append(plugin)
except AttributeError:
print "mixing commandline plugins and configfile does not work with the old config file format"
print("mixing commandline plugins and configfile does not work with the old config file format")

from twisted.internet import reactor

Expand Down
24 changes: 13 additions & 11 deletions coherence/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

# Copyright 2007,, Frank Scholz <coherence@beebits.net>
from lxml import etree
from functools import cmp_to_key

import time
from coherence.extern.simple_plugin import Plugin
Expand Down Expand Up @@ -211,7 +212,7 @@ def __init__(self, *args, **kwargs):
self.item.res.append(res)
"""
log.Loggable.__init__(self)
self.name = u'my_name' # the basename of a file, the album title,
self.name = 'my_name' # the basename of a file, the album title,
# the artists name,...
# is expected to be unicode
self.item = None
Expand Down Expand Up @@ -350,7 +351,7 @@ def __init__(self, parent, title):
self.sorted = False

def childs_sort(x, y):
return cmp(x.name, y.name)
return cmp_to_key(x.name, y.name)
self.sorting_method = childs_sort

def register_child(self, child, external_id=None):
Expand Down Expand Up @@ -381,7 +382,8 @@ def remove_child(self, child, external_id=None, update=True):

def get_children(self, start=0, end=0):
if not self.sorted:
self.children.sort(cmp=self.sorting_method)
self.children = sorted(
self.children.sort, key=cmp_to_key(self.sorting_method))
self.sorted = True
if end != 0:
return self.children[start:end]
Expand Down Expand Up @@ -423,7 +425,7 @@ def __init__(self, parent, title, external_id=None, refresh=0, childrenRetriever
self.children_retrieval_campaign_in_progress = False
self.childrenRetriever_params = kwargs
self.childrenRetriever_params['parent'] = self
self.has_pages = (self.childrenRetriever_params.has_key('per_page'))
self.has_pages = ('per_page' in self.childrenRetriever_params)

self.external_id = None
self.external_id = external_id
Expand Down Expand Up @@ -453,10 +455,10 @@ def update_children(self, new_children, old_children):
# let's classify the item between items to be removed,
# to be updated or to be added
self.debug("Refresh pass 1:%d %d", len(new_children), len(old_children))
for id, item in old_children.items():
for id, item in list(old_children.items()):
children_to_be_removed[id] = item
for id, item in new_children.items():
if old_children.has_key(id):
for id, item in list(new_children.items()):
if id in old_children:
#print(id, "already there")
children_to_be_replaced[id] = old_children[id]
del children_to_be_removed[id]
Expand All @@ -468,10 +470,10 @@ def update_children(self, new_children, old_children):
# to the list of items
self.debug("Refresh pass 2: %d %d %d", len(children_to_be_removed), len(children_to_be_replaced), len(children_to_be_added))
# Remove relevant items from Container children
for id, item in children_to_be_removed.items():
for id, item in list(children_to_be_removed.items()):
self.remove_child(item, external_id=id, update=False)
# Update relevant items from Container children
for id, item in children_to_be_replaced.items():
for id, item in list(children_to_be_replaced.items()):
old_item = item
new_item = new_children[id]
replaced = False
Expand All @@ -482,7 +484,7 @@ def update_children(self, new_children, old_children):
self.remove_child(old_item, external_id=id, update=False)
self.add_child(new_item, external_id=id, update=False)
# Add relevant items to COntainer children
for id, item in children_to_be_added.items():
for id, item in list(children_to_be_added.items()):
self.add_child(item, external_id=id, update=False)

self.update_id += 1
Expand Down Expand Up @@ -586,7 +588,7 @@ def remove_item(self, item):
item.store = None

def get_by_id(self, id):
if isinstance(id, basestring):
if isinstance(id, str):
id = id.split('@', 1)
id = id[0].split('.')[0]
try:
Expand Down
19 changes: 8 additions & 11 deletions coherence/backends/ampache_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def md5(s):

VIDEO_CONTAINER_ID = 200

from urlparse import urlsplit
from urllib.parse import urlsplit


class ProxySong(utils.ReverseProxyResource):
Expand Down Expand Up @@ -416,11 +416,8 @@ def __init__(self, store, element):
self.url = element.find('url').text

seconds = int(element.find('time').text)
hours = seconds / 3600
seconds = seconds - hours * 3600
minutes = seconds / 60
seconds = seconds - minutes * 60
self.duration = ("%d:%02d:%02d") % (hours, minutes, seconds)
self.duration = time.strftime(
'%H:%M:%S', time.gmtime(seconds))

self.bitrate = 0

Expand Down Expand Up @@ -624,10 +621,10 @@ def __repr__(self):

def get_by_id(self, id):
self.info("looking for id %r", id)
if isinstance(id, basestring):
if isinstance(id, str):
id = id.split('@', 1)
id = id[0]
if isinstance(id, basestring) and id.startswith('artist_all_tracks_'):
if isinstance(id, str) and id.startswith('artist_all_tracks_'):
try:
return self.containers[id]
except:
Expand All @@ -649,7 +646,7 @@ def got_auth_response(self, response, renegotiate=False):
self.info("got_auth_response %r", response)
try:
response = etree.fromstring(response)
except SyntaxError, msg:
except SyntaxError as msg:
self.warning('error parsing ampache answer %r', msg)
raise SyntaxError('error parsing ampache answer %r' % msg)
try:
Expand Down Expand Up @@ -944,7 +941,7 @@ def build_response(tm):
wmc_mapping = getattr(self, "wmc_mapping", None)
if(kwargs.get('X_UPnPClient', '') == 'XBox' and
wmc_mapping != None and
wmc_mapping.has_key(ObjectID)):
ObjectID in wmc_mapping):
""" fake a Windows Media Connect Server
"""
root_id = wmc_mapping[ObjectID]
Expand Down Expand Up @@ -1036,7 +1033,7 @@ def proceed(result):

def main():
def got_result(result):
print "got_result"
print("got_result")

def call_browse(ObjectID=0, StartingIndex=0, RequestedCount=0):
r = f.backend.upnp_Browse(BrowseFlag='BrowseDirectChildren',
Expand Down
11 changes: 9 additions & 2 deletions coherence/backends/appletrailers_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
This is a Media Backend that allows you to access the Trailers from Apple.com
"""
from lxml import etree
from functools import cmp_to_key

from coherence.backend import BackendItem, BackendStore
from coherence.upnp.core import DIDLLite
Expand Down Expand Up @@ -221,8 +222,14 @@ def upnp_init(self):
self.server.connection_manager_server.set_variable( \
0, 'SourceProtocolInfo', ['http-get:*:video/quicktime:*', 'http-get:*:video/mp4:*'])
self.container = Container(ROOT_ID, -1, self.name)
trailers = self.trailers.values()
trailers.sort(cmp=lambda x, y: cmp(x.get_name().lower(), y.get_name().lower()))
trailers = list(self.trailers.values())
# trailers.sort(cmp=lambda x, y: cmp(
# x.get_name().lower(), y.get_name().lower()))
trailers = sorted(
trailers,
key=lambda x, y: cmp_to_key(
x.get_name().lower(),
y.get_name().lower()))
self.container.children = trailers

def __repr__(self):
Expand Down
4 changes: 2 additions & 2 deletions coherence/backends/audiocd_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def extractAudioCdInfo (self):
self.disc_title = query_info['title'].encode('utf-8')
tracks = {}
for i in range(track_count):
tracks[i + 1] = read_info['TTITLE' + `i`].decode('ISO-8859-1').encode('utf-8')
tracks[i + 1] = read_info['TTITLE' + repr(i)].decode('ISO-8859-1').encode('utf-8')

self.name = self.disc_title

Expand All @@ -147,7 +147,7 @@ def childs_sort(x, y):

self.set_root_item(root_item)

for number, title in tracks.items():
for number, title in list(tracks.items()):
item = TrackItem(self.device_name, number, "Unknown", title)
external_id = "%s_%d" % (disc_id, number)
root_item.add_child(item, external_id=external_id)
Expand Down
11 changes: 5 additions & 6 deletions coherence/backends/axiscam_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
# for the RTP payload type identifier
#

from sets import Set

from coherence.upnp.core.DIDLLite import classChooser, Container, Resource, DIDLElement

Expand Down Expand Up @@ -142,7 +141,7 @@ def __repr__(self):
return str(self.__class__).split('.')[-1]

def append(self, obj, parent):
if isinstance(obj, basestring):
if isinstance(obj, str):
mimetype = 'directory'
else:
protocol, network, content_type, info = obj['protocol'].split(':')
Expand Down Expand Up @@ -175,7 +174,7 @@ def len(self):
return len(self.store)

def get_by_id(self, id):
if isinstance(id, basestring):
if isinstance(id, str):
id = id.split('@', 1)
id = id[0]
id = int(id)
Expand All @@ -195,9 +194,9 @@ def upnp_init(self):
self.current_connection_id = None
parent = self.append('AxisCam', None)

source_protocols = Set()
source_protocols = set()

for k, v in self.config.items():
for k, v in list(self.config.items()):
if isinstance(v, dict):
v['name'] = k
source_protocols.add(v['protocol'])
Expand All @@ -214,7 +213,7 @@ def main():
f = AxisCamStore(None)

def got_upnp_result(result):
print "upnp", result
print("upnp", result)

#f.upnp_init()
#print f.store
Expand Down
12 changes: 6 additions & 6 deletions coherence/backends/banshee_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
import re
import os
import time
from urlparse import urlsplit
import urllib2
from urllib.parse import urlsplit
import urllib.request, urllib.error, urllib.parse

import mimetypes
mimetypes.init()
Expand Down Expand Up @@ -102,8 +102,8 @@ def sql_execute(self, request, *params, **kw):
t0 = time.time()
debug_msg = request
if params:
debug_msg = u"%s params=%r" % (request, params)
debug_msg = u''.join(debug_msg.splitlines())
debug_msg = "%s params=%r" % (request, params)
debug_msg = ''.join(debug_msg.splitlines())
if debug_msg:
self.debug('QUERY: %s', debug_msg)

Expand Down Expand Up @@ -500,7 +500,7 @@ def get_resources(self):
return statinfo, resources

def get_path(self):
return urllib2.unquote(self.location[7:].encode('utf-8'))
return urllib.parse.unquote(self.location[7:].encode('utf-8'))

def get_id(self):
return "track.%d" % self.itemID
Expand Down Expand Up @@ -850,7 +850,7 @@ def release(self):

def get_by_id(self, item_id):
self.info("get_by_id %s", item_id)
if isinstance(item_id, basestring) and item_id.find('.') > 0:
if isinstance(item_id, str) and item_id.find('.') > 0:
item_id = item_id.split('@', 1)
item_type, item_id = item_id[0].split('.')[:2]
item_id = int(item_id)
Expand Down
Loading

0 comments on commit 92d886a

Please sign in to comment.