Play images and video from Synology PhotoStation server

photostation_api.py 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. import os,os.path
  2. import requests, urllib, urlparse
  3. import json
  4. headers2dict = lambda h: dict([l.strip().split(": ") for l in h.strip().splitlines()])
  5. class PhotoStationAPI():
  6. def __init__(self, ps_url):
  7. self.url = "%s/webapi/"%ps_url
  8. self.sid = ""
  9. self.user = ""
  10. self.headers = headers2dict("""
  11. User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:51.0) Gecko/20100101 Firefox/51.0
  12. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  13. Accept-Language: en-US,en;q=0.5
  14. Referer: %s/photo/
  15. """%ps_url)
  16. def login(self,user,password):
  17. data = "api=SYNO.PhotoStation.Auth&method=login&version=1&username=%s&password=%s&remember_me=on"%(user,password)
  18. js = self._call2("auth.php",data)
  19. if not js["success"]:
  20. raise Exception("Can not login")
  21. self.sid=js["data"]["sid"]
  22. self.user = user
  23. return self.sid
  24. def logout(self):
  25. pass
  26. def get_album_list( self, album_id="",
  27. offset=0,limit=-1,
  28. additional="album_permission,photo_exif,video_codec,video_quality,thumb_size,file_location,item_count,album_sorting", #album_permission,photo_exif,video_codec,video_quality,thumb_size,file_location
  29. sort_by="preference", # 'filename', 'takendate', 'createdate', 'preference', 'default'
  30. sort_direction="asc"
  31. ):
  32. data = "api=SYNO.PhotoStation.Album&method=list&version=1&offset=%s&limit=%s&recursive=false&type=album,photo,video&additional=%s&sort_direction=%s&sort_by=%s&ignore=thumbnail&id=%s"%(
  33. offset,limit,additional,sort_direction,sort_by,album_id)
  34. js = self._call2("album.php",data)
  35. if js["success"] and "data" in js:
  36. return js["data"]
  37. else:
  38. raise Exception("Error reading album list")
  39. def get_album_list2( self, album_id="",
  40. offset=0,limit=-1,
  41. additional="album_permission,photo_exif,video_codec,video_quality,thumb_size,file_location,item_count,album_sorting", #album_permission,photo_exif,video_codec,video_quality,thumb_size,file_location
  42. sort_by="preference", # filename,
  43. sort_direction="asc"
  44. ):
  45. lst = []
  46. js = self.get_album_list(album_id,offset,limit,additional,sort_by,sort_direction)
  47. for f in js["items"]:
  48. if f["type"] == u"photo":
  49. name = f["info"]["name"]
  50. desc = [
  51. f["info"]["description"],
  52. f["additional"]["file_location"],
  53. f["info"]["takendate"],
  54. "%sx%s"%(f["info"]["resolutionx"],f["info"]["resolutiony"]),
  55. "%s %s"%(f["additional"]["photo_exif"]["camera"],f["additional"]["photo_exif"]["camera_model"]),
  56. "%s %s %s"%(f["additional"]["photo_exif"]["aperture"],f["additional"]["photo_exif"]["exposure"],f["additional"]["photo_exif"]["focal_length"])
  57. ]
  58. elif f["type"] == u"video":
  59. name = f["info"]["name"]
  60. desc = [
  61. f["info"]["description"],
  62. f["additional"]["file_location"],
  63. ]
  64. elif f["type"] == u"album":
  65. name = f["info"]["name"]
  66. desc = [
  67. f["info"]["description"],
  68. f["additional"]["file_location"],
  69. "%s photos, %s videos"%(f["additional"]["item_count"]["photo"],f["additional"]["item_count"]["video"])
  70. ]
  71. desc = filter(None, desc)
  72. desc = "\n".join(desc)
  73. img = self.get_thumb_url(f["id"],"small")
  74. lst.append([
  75. f["info"]["name"].encode("utf8"),
  76. f["id"].encode("utf8"), #id
  77. img.encode("utf8"), # thumburl
  78. desc.encode("utf8"), # desc
  79. #f["type"].encode("utf8")
  80. ])
  81. return lst
  82. def get_album_info(self,album_id=""):
  83. data = "id=%s&additional=album_sorting,item_count&api=SYNO.PhotoStation.Album&method=getinfo&version=1&ps_username=%s"%(
  84. album_id,self.user)
  85. js = self._call2("album.php",data)
  86. if js["success"] and "data" in js:
  87. return js["data"]
  88. else:
  89. raise Exception("Error reading album info")
  90. def get_category(self, category_id=""):
  91. if category_id:
  92. # api=SYNO.PhotoStation.Category&method=list&version=1&offset=0&limit=1000
  93. data = "id=%s&api=SYNO.PhotoStation.Category&method=listitem&version=1&offset=0&limit=500&additional=album_permission,thumb_size" % category_id
  94. else:
  95. data = "api=SYNO.PhotoStation.Category&method=list&version=1&offset=0&limit=1000"
  96. js = self._call2("category.php",data)
  97. if js["success"] and "data" in js:
  98. return js["data"]
  99. else:
  100. raise Exception("Error reading album info")
  101. def get_smart_album(self, id=""):
  102. if id:
  103. #TODO
  104. data = "id=%s&api=SYNO.PhotoStation.Category&method=listitem&version=1&offset=0&limit=500&additional=album_permission,thumb_size" % id
  105. else:
  106. #sort_by=title&sort_direction=desc&api=SYNO.PhotoStation.SmartAlbum&method=list&version=1&offset=0&limit=500&additional=thumb_size
  107. data = "sort_by=title&sort_direction=desc&api=SYNO.PhotoStation.SmartAlbum&method=list&version=1&offset=0&limit=500&additional=thumb_size"
  108. js = self._call2("smart_album.php",data)
  109. if js["success"] and "data" in js:
  110. return js["data"]
  111. else:
  112. raise Exception("Error reading album info")
  113. def get_photo_info(self,id):
  114. data = "id=%s&version=1&api=SYNO.PhotoStation.Photo&method=getinfo&ps_username=%s&additional=album_permission,photo_exif,video_codec,video_quality,thumb_size,file_location,item_count,album_sorting"%(id,self.user)
  115. js = self._call2("photo.php",data)
  116. if js["success"] and "data" in js:
  117. return js["data"][0]
  118. else:
  119. raise Exception("Error reading photo/video info")
  120. def get_thumb(self,id,size="peview",rotate=0 ):
  121. # siz = preview|small|large
  122. data = "api=SYNO.PhotoStation.Thumb&method=get&version=1&size=%s&id=%s&rotate_version=%s"%(size,id,rotate)
  123. content = self._call2("thumb.php",data)
  124. return content
  125. def get_thumb_url(self,id,size="small",rotate=0 ):
  126. # http://home.blue.lv/photo/webapi/thumb.php?api=SYNO.PhotoStation.Thumb&method=get&version=1&size=large&id=photo_323031372f323031372d30312d787820537475626169_53353032303031372e4a5047&rotate_version=0&thumb_sig=&PHPSESSID=p51g2ssj3b2o3legsoqfqdc8r3
  127. url = "%sthumb.php?api=SYNO.PhotoStation.Thumb&method=get&version=1&size=%s&id=%s&rotate_version=%s&thumb_sig=&PHPSESSID=%s"%(
  128. self.url, size, id, rotate, self.sid )
  129. return url
  130. def get_video_streams(self,id):
  131. urls = []
  132. js = self.get_photo_info(id)
  133. if not js: return urls
  134. name = js["info"]["name"]
  135. for q in js["additional"]["video_quality"]:
  136. s = {}
  137. quality_id = q["id"]
  138. data = "api=SYNO.PhotoStation.Download&method=getvideo&version=1&id=%s&quality_id=%s"%(id,quality_id)
  139. url = "%s/download.php/%s?%s&PHPSESSID=%s"%(self.url,name,data,self.sid)
  140. s["name"] = name
  141. s["url"] = url
  142. s["img"] = self.get_thumb_url(id, size='small')
  143. s["quality"] = "%sx%s"%(q["resolutionx"],q["resolutiony"])
  144. s["bitrate"] = q["video_bitrate"]
  145. s["desc"] = "%s %s\n%s\n%sx%s %s/%s"%(js["info"]["name"],js["info"]["description"],
  146. js["info"]["takendate"],
  147. js["additional"]["video_codec"]["resolutionx"],js["additional"]["video_codec"]["resolutiony"],js["additional"]["video_codec"]["vcodec"],js["additional"]["video_codec"]["acodec"])
  148. urls.append(s)
  149. return urls
  150. def _call(self,path,data):
  151. url = self.url+path
  152. if isinstance(data,basestring):
  153. data = urlparse.parse_qs(data)
  154. if self.sid:
  155. self.headers["Cookie"] = "PHPSESSID=%s;"%self.sid
  156. try:
  157. r = requests.post(url,data,headers=self.headers)
  158. js = json.loads(r.content)
  159. return js
  160. except Exception as e:
  161. return {}
  162. def _call2(self,path,data):
  163. "GET request to PhotoStation API"
  164. if isinstance(data,dict):
  165. data = urllib.urlencode(data)
  166. if self.sid:
  167. if not "PHPSESSID" in data:
  168. data = data +"&PHPSESSID=%s"%self.sid
  169. #self.headers["Cookie"] = "PHPSESSID=%s;"%self.sid
  170. try:
  171. url = self.url + path +"?"+data
  172. print url
  173. r = requests.get(url,headers=self.headers)
  174. js = json.loads(r.content)
  175. return js
  176. except Exception as e:
  177. return {}
  178. def _check_url(self,url):
  179. if self.sid:
  180. if not "PHPSESSID" in url:
  181. self.headers["Cookie"] = "PHPSESSID=%s; photo_remember_me=1"%self.sid
  182. pass
  183. r = requests.get(url,headers=self.headers)
  184. if r.status_code <> 200 or "text/plain" in r.headers["Content-Type"] and "error" in r.content:
  185. return False
  186. else:
  187. return True
  188. if __name__ == "__main__":
  189. ps = PhotoStationAPI("http://home.blue.lv/photo")
  190. print ps.login("user","Kaskade7")
  191. js = ps.get_category("category_1")
  192. album_id = u'album_323031372f323031372d30312d313320536c69646f73616e61'
  193. js = ps.get_album_info(album_id)
  194. js = ps.get_album_list2(album_id)
  195. photo_id = "photo_323031372f323031372d30312d787820537475626169_53353032303031372e4a5047"
  196. js = ps.get_photo_info(photo_id)
  197. url = ps.get_thumb_url(photo_id,"large")
  198. print url
  199. print ps._check_url(url)
  200. video_id = "video_323031372f323031372d30312d7878205374756261692f636c697073_30303136342e4d5453"
  201. js = ps.get_photo_info(video_id)
  202. urls = ps.get_video_streams(video_id)
  203. print urls[0]["url"]
  204. a=1