story.py 61 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181
  1. # THIS FILE IS A PART OF VCStudio
  2. # PYTHON 3
  3. import os
  4. import datetime
  5. import json
  6. # This is for the export. We need a window for it. Maybe I will make my own.
  7. import gi
  8. gi.require_version('Gtk', '3.0')
  9. from gi.repository import Gtk
  10. import zipfile
  11. import re
  12. from settings import talk
  13. from studio import checklist
  14. #import checklist
  15. def get_legacy(project_location):
  16. # This function will read the .bos (Blender-Organizer Story) files. Primarily
  17. # to convert them to .vcss (VCStudio Story) files.
  18. # The concept is similar. But in order to work with them outside of reading
  19. # and writting the file directly. I want to make a dictionary format with in
  20. # the python. (Similarly to how I did with checklists and analytics)
  21. # In the .bos file there was what I thought was clever at the time but
  22. # ultimatly a dumb idea to make EVENTS and not scenes. The idea was that in
  23. # a single event could be multiple scenes. But in reality it very rearly used
  24. # and creating scenes with in events created a potential user error situation.
  25. # So I want to get rid of all the EVENTS and read Scenes directly. Unless
  26. # the event is empty of scenes. In which case to create a scene with the
  27. # text of that event.
  28. data = {
  29. "fraction": 0.0, # Percentage of the Scenes finished.
  30. "camera" : [0,0], # The position of where the user left
  31. "selected": [], # List of selected items in the story editor
  32. "active": None, # Active item.
  33. "scenes" : {}, # List of scenes.
  34. "arrows" : [], # List of connections. (A LIST. NOT DICT.)
  35. "links" : [], # List of links to files or assets.
  36. "markers": {}, # List of markers.
  37. "events" : {} # List of frame like containers. (It will have similar)
  38. } # function as events. But will have no text data with in
  39. # it. It will be more like Blender's node editor's Frame.
  40. # Even tho I want to change radically the idea of events. We still need to
  41. # think in the events terms. Because we are parsing and old file. Funny how
  42. # I will need to write this thing twice. For both file-types.
  43. # I can't read it the way I read most other files. Because it's not line-
  44. # based. But more something like HTML file. So it's going to be a little
  45. # issue.
  46. bos = open(project_location+"/pln/main.bos")
  47. bos = bos.read()
  48. cx, cy = 1, 1
  49. if "</camera>" in bos:
  50. camera = bos[bos.find("<camera>")+8:]
  51. camera = camera[:camera.find("</camera>")]
  52. camera = camera.split(",")
  53. for num, val in enumerate(camera):
  54. try:
  55. camera[num] = float(val)
  56. except:
  57. camera[num] = 0.0
  58. try:
  59. cx = float(camera[2])
  60. cy = float(camera[3])
  61. except:
  62. pass
  63. print (cy, cx)
  64. # Some stupid me made decision early in a story editor's life to use
  65. # per-pixel X coordinates and per line Y coordinates. Which is something
  66. # like 100 times the difference. Okay 50 ish is a default value for Y.
  67. # I'm not going to do anything about it right now. Maximum a bit later.
  68. camera = [camera[0], camera[1]] # I don't want scale to exist here.
  69. data["camera"] = camera
  70. # Events. They are very important. Now in the Blender-Organizer I used an
  71. # HTML like format to mark shots and assets with in the text. But it's not
  72. # a very efficient way of doing it. Because it requiered constant parsing
  73. # of the text in real time. Which caused quite a noticable lag. And also
  74. # every time I needed to read a part of it. I had to do parsing of the text.
  75. # So I guess the VCSS file format will be designed to deal with this kind of
  76. # thing. I'm actually more toward putting the text into separate files. Simple
  77. # text documents. And making the VCSS a linking system.
  78. if "</event>" in bos:
  79. for event in bos.split("</event>")[:-1]:
  80. event = event[event.find("<event>")+8:]
  81. # Now we have text of the event. Let's parse out of it folowing
  82. # stuff. We will need it if there are multiple scenes. Or when
  83. # there are no scenes at all.
  84. eventname = event[event.find('"')+1:event.replace('"'," ",1).find('"')]
  85. # Let's parse the coordinates.
  86. c = event[event.find('[')+1:event.find(']')]
  87. c = c.split(",")
  88. eventpositon = [float(c[0])*cx,float(c[2])*cy]
  89. eventsize = [float(c[1])*cx,60.0]
  90. # Now since we know the name of the event and the sizes. We can
  91. # start parsing the scenes from the text with in the event.
  92. eventtext = event[event.find(']')+2:-1]
  93. # Now basically have to do the same exact thing with <scene> and
  94. # later with <shot>, <item> to make all work.
  95. # But first let's record a scene if it has no scene in it.
  96. if not "<scene>" in eventtext:
  97. if eventname in data["scenes"]:
  98. eventname = eventname + "_copy"
  99. data["scenes"][eventname] = {
  100. "fraction":0.0, # Percentage
  101. "position":eventpositon,
  102. "size":eventsize,
  103. "parent":"", # For when it's in a Frame (Event)
  104. "shots":[[
  105. "text_block",[["text", eventtext]]
  106. ]]
  107. }
  108. else:
  109. # If there are more then 1 scene per event. We want to create
  110. # an event, frame thing for them.
  111. aos = eventtext.count("<scene>")
  112. parent = "" #This will be it's name
  113. if aos > 1:
  114. parent = eventname
  115. data["events"][eventname] = {
  116. "position":eventpositon,
  117. "size":[0,0]
  118. }
  119. # Now let's continue parsing the scenes.
  120. for num, scene in enumerate(eventtext.split("</scene>")[:-1]):
  121. scenename = scene[scene.find('"')+1:scene.replace('"'," ",1).find('"')]
  122. scenename = scenename.replace(" ", "_")
  123. scenetext = scene[scene.replace('"', " ", 1).find('"')+1:-1]
  124. scenesize = [eventsize[0] / aos , eventsize[1]]
  125. sceneposition = [eventpositon[0] + scenesize[0]*num,
  126. eventpositon[1]]
  127. data["scenes"][scenename] = {
  128. "fraction":0.0, # Percentage
  129. "position":sceneposition,
  130. "size":scenesize,
  131. "parent":parent, # For when it's in a Frame (Event)
  132. "shots":[[
  133. "text_block",[["text", scenetext]]
  134. ]]
  135. }
  136. # Parsing the text inside the scenes... OMG. Finding the SHOTS.
  137. for scenename in data["scenes"]:
  138. scenetext = data["scenes"][scenename]["shots"][0][1][0][1]
  139. # Okay so we have both scene name and scene text. So it's
  140. # time so send this data into the main data thingy, right?
  141. # Wrong. Now we need to parse pointers to the shots, assets
  142. # and other stuff. This file format gonna take a while.
  143. # If you look into the history of this file on NotABug there
  144. # Will be a version with a novel-long article here in the
  145. # comments. I was toying with ideas of how to make stuff
  146. # work. I will not change the whole system a bit from what
  147. # I though there. (Using text pointers) and make it more like
  148. # list of shot_block or text_block. Which both are just text.
  149. # But one has a bit more metadata then the other. And inside
  150. # which you can specify links, frases, images and simple text...
  151. # So first of all we need to break the text up into shots.
  152. shots = []
  153. sa = scenetext.count("<shot>") # Shots amount
  154. ts = scenetext # Copy of the scene text to butcher
  155. for s in range(sa):
  156. # If the first part of the scene is not a shot.
  157. if not ts.startswith("<shot>"):
  158. shots.append([
  159. "text_block", [["text",ts[:ts.find("<shot>")]]]
  160. ])
  161. # Now we need to erase the part from the ts.
  162. ts = ts[ts.find("<shot>")+6:]
  163. # Now we can parse the shot itself. We need the name.
  164. if ts.count('"') > 1:
  165. shotname = ts[ts.find('"')+1:ts.replace('"', " ", 1).find('"')]
  166. ts = ts[ts.replace('"', " ", 1).find('"')+1:]
  167. else:
  168. shotname = "Unnamed"
  169. # Put it also into the list.
  170. shots.append([
  171. "shot_block", shotname, [["text",ts[:ts.find("</shot>")]]]
  172. ])
  173. # And erase it.
  174. ts = ts[ts.find("</shot>")+7:]
  175. shots.append([
  176. "text_block", [["text",ts]]
  177. ])
  178. # Now I want to get a fraction from a scene.
  179. shotsfractions = []
  180. for shot in shots:
  181. if shot[0] == "shot_block":
  182. # Let's see if it has a checklist.
  183. if os.path.exists(project_location\
  184. +"/rnd/"+scenename+"/"+shot[1]+"/shot.progress"):
  185. check = checklist.get_list(project_location\
  186. +"/rnd/"+scenename+"/"+shot[1]+"/shot.progress")
  187. shotsfractions.append(check["fraction"])
  188. else:
  189. folder = project_location\
  190. +"/rnd/"+scenename+"/"+shot[1]
  191. try:
  192. if len(os.listdir(folder+"/rendered")) > 0:
  193. shotsfractions.append(1.0)
  194. elif len(os.listdir(folder+"/test_rnd")) > 0:
  195. shotsfractions.append(0.8)
  196. elif len(os.listdir(folder+"/opengl")) > 0:
  197. shotsfractions.append(0.6)
  198. elif len(os.listdir(folder+"/storyboard")) > 0:
  199. shotsfractions.append(0.4)
  200. elif len(os.listdir(folder+"/extra")) > 0:
  201. shotsfractions.append(0.2)
  202. except:
  203. shotsfractions.append(0.0)
  204. # Writting parsed verions of the scenes. With all the shots
  205. data["scenes"][scenename]["shots"] = shots
  206. # Now that we have SHOTS Fractions we need a scene fraction.
  207. try:
  208. data["scenes"][scenename]["fraction"] = \
  209. sum(shotsfractions) / len(shotsfractions)
  210. except:
  211. data["scenes"][scenename]["fraction"] = 0.0
  212. # Now since we have shots and we have their factors. I'd like to do
  213. # parsing of the text parts inside shots and texts between shots to
  214. # find all the images, links and phrases.
  215. for scenename in data["scenes"]:
  216. for shotnum, shot in enumerate(data["scenes"][scenename]["shots"]):
  217. # Our shot data could be either text_block ot shot_block
  218. # and they are formatted a little bit differently.
  219. if shot[0] == "shot_block":
  220. text = shot[2][0][1]
  221. else:
  222. text = shot[1][0][1]
  223. # Now keep in mind that similar stuff has to be done again
  224. # when we saving the parsed text back
  225. textblock = []
  226. # This requires some extreme cleverness. Since I already hate
  227. # this file format. WHY IS IT SO COMPLICATED?!
  228. part = ""
  229. skip = 0
  230. for num, letter in enumerate(text):
  231. if num >= skip:
  232. if part.endswith("<item>"):
  233. # putting the normal text in
  234. if part[skip:].replace("<item>", ""):
  235. textblock.append([
  236. "text", part[skip:].replace("<item>", "")
  237. ])
  238. # parsing the item
  239. itempart = text[num:text[num:].find("</item>")+num]
  240. link = itempart[itempart.find('"')+1:
  241. itempart.replace('"', " ", 1).find('"')]
  242. if link.startswith("/dev"):
  243. link = link[4:]
  244. itemtext = itempart[itempart.replace('"', " ", 1).find('"')+1:]
  245. # puttin the link into the list
  246. textblock.append([
  247. "link", link, itemtext
  248. ])
  249. # skiping to after the item
  250. skip = text[num:].find("</item>")+6
  251. part = ""
  252. # Images. With the <>
  253. elif part.endswith("<image>"):
  254. # putting the normal text in
  255. if part[skip:].replace("<image>", ""):
  256. textblock.append([
  257. "text", part[skip:].replace("<image>", "")
  258. ])
  259. # parsing the item
  260. link = text[num:text[num:].find("</image>")+num]
  261. textblock.append([
  262. "image", link
  263. ])
  264. # skiping to after the item
  265. skip = text[num:].find("</image>")+7
  266. part = ""
  267. # Images. With the [] (a little older version but still
  268. # should be supported by default)
  269. elif part.endswith("[image]"):
  270. # putting the normal text in
  271. if part[skip:].replace("[image]", ""):
  272. textblock.append([
  273. "text", part[skip:].replace("[image]", "")
  274. ])
  275. # parsing the item
  276. link = text[num:text[num:].find("[/image]")+num]
  277. textblock.append([
  278. "image", link
  279. ])
  280. # skiping to after the item
  281. skip = text[num:].find("[/image]")+7
  282. part = ""
  283. # Now let's figure out the frases. Because it has a
  284. # text based thing in the beginning. Or maybe it's going
  285. # to be anything that was before it? Hmm...
  286. elif part.endswith(" - ["):
  287. # putting the normal text in
  288. if part[skip:].replace(" - [", ""):
  289. textblock.append([
  290. "text", part[skip:].replace(" - [", "")
  291. ])
  292. # I designed the - [ thing to type conversation
  293. # easier. Example:
  294. # John - [Hello, World.]
  295. # This would be John saying the words "Hello, World."
  296. # but with the inclusion of <item> it became quite a
  297. # problem. Because I would like John to be a link.
  298. # It ended up looking something like:
  299. #<itme>"/dev/chr/John"John</item> - [Hello, World.]
  300. # This is a bit of a problem. Because any link that
  301. # marked as <item> is already in a textblock list.
  302. character = []
  303. # If it's a link
  304. if textblock[-1][0] == "link":
  305. character = textblock[-1]
  306. del textblock[-1]
  307. # If it's just a text
  308. elif textblock[-1][0] == "text":
  309. character = textblock[-1][1]
  310. character = character[character.rfind("\n")+1:]
  311. if character:
  312. textblock[-1][1] = textblock[-1][1].replace(character, "")
  313. character = ["text", character]
  314. # Now let's get the frase
  315. frase = text[num:text[num:].find("]")+num]
  316. # If any character. Put it into a textblock
  317. if character:
  318. textblock.append([
  319. "frase", character, frase
  320. ])
  321. # skiping to after the item
  322. skip = text[num:].find("]")
  323. part = ""
  324. else:
  325. part = part + letter
  326. # LAST PART (WHY DIDN'T I THINK ABOUT ERLIER OMG)
  327. textblock.append([
  328. "text", part[skip:]
  329. ])
  330. if shot[0] == "shot_block":
  331. data["scenes"][scenename]["shots"][shotnum][2] = textblock
  332. else:
  333. data["scenes"][scenename]["shots"][shotnum][1] = textblock
  334. # Okay this was event. LOL. This is a large function to parse them all.
  335. # Crazy complex bastards. Anyway. It's over. And we have to only do the
  336. # easy rest of it. Untill we will get to calculating the project. Because
  337. # we need to filter out all scenes that are not in the main chain.
  338. # Let's start with hard thing first. So to feel good when having to do the
  339. # easy stuff in the end. ARROWS. (connections between scenes)
  340. # To make it work. We need to fisrt of all earase all the stuff before
  341. # the first arrow. Because <arrow> could be mentioned in the script it
  342. # self. Similarly <image> is too. So it's good to complitelly clear bos
  343. # out of anything that came before.
  344. bos = bos[bos.rfind("</event>"):]
  345. # From here everything is PER LINE BASED... YEAH!!!!
  346. bos = bos.split("\n")
  347. for line in bos:
  348. # This is arrows...
  349. if line.startswith("<arrow>"):
  350. arrow = line[line.find("<")+7:line.rfind("</")].split(" --> ")
  351. newarrow = []
  352. for i in arrow:
  353. i = i.split(",")
  354. if i[0] != "-1":
  355. newarrow.append([
  356. "scene", i[1][1:-1]
  357. ])
  358. else:
  359. newarrow.append(
  360. i[1][1:-1]
  361. )
  362. data["arrows"].append(newarrow)
  363. # And this is links. Formerly known as Images. Initially the idea was to
  364. # just store images. But later. I started adding assets like this as well.
  365. elif line.startswith("<image>"):
  366. stuff = line.split(",")
  367. link = stuff[3][1:-1]
  368. coordinates = []
  369. try:
  370. coordinates.append(float(stuff[0].replace("<image>", ""))*cx)
  371. except:
  372. coordinates.append(0.0)
  373. try:
  374. coordinates.append(float(stuff[1])*cy)
  375. except:
  376. coordinates.append(0.0)
  377. # Lately the primary reason to use <image> was linking assets directly
  378. # into story editor's space. But it was a hack. Basically I was
  379. # linking a preview image. Preview.png or Preview.jpg from the
  380. # renders of the asset. And the drawer of the images in the story-
  381. # editor already knew to redirect the clicks to a different function.
  382. # But for VCStudio I want to link to asset or a file.
  383. linktype = "file"
  384. if "/renders/Preview." in link:
  385. linktype = "asset"
  386. link = link[:link.rfind("/renders/Preview.")].replace("/dev", "")
  387. data["links"].append([
  388. linktype, link, coordinates, ""
  389. ])
  390. # Markers. I nearly forgot about the markers. In the Story-Editor of
  391. # Blender-Orgaznier they were something like markers in VSE in Blender.
  392. # Vertical lines visible on every altitude.
  393. elif line.startswith("<marker>"):
  394. marker = line.replace("<marker>", "").replace("</marker>", "").split(",")
  395. try:
  396. markloc = float(marker[0])
  397. except:
  398. markloc = 0.0
  399. markstring = marker[1].replace('"', "")
  400. # I do want to do something quite interesting with markers in the
  401. # VCStudio tho. I want them to be like little nodes with text while
  402. # in the frame. And be like directional guides. Sticking to the
  403. # edges of the screen when outside the screen. Something like items
  404. # that are outside of the map in the videogames. So you can see
  405. # which direction are they. For this markers will need to have
  406. # both X and Y coordinates.
  407. data["markers"][markstring] = [markloc, 0.0, ""]
  408. # For now the Y is 0. Because we are reading from the old version.
  409. # Okay we've got the Arrows data. Pretty much all the data. Now we just
  410. # need to calculate the scenes fraction. For that we need to recreate the
  411. # train of scenes. From START till END.
  412. fractions = []
  413. lastarrow = 'start'
  414. for i in data["arrows"]:
  415. if lastarrow == "end":
  416. break
  417. for arrow in data["arrows"]:
  418. if arrow[0] == lastarrow:
  419. lastarrow = arrow[1]
  420. if arrow[1] != "end":
  421. fractions.append(
  422. data["scenes"][arrow[1][1]]["fraction"]
  423. )
  424. else:
  425. break
  426. # FINAL STUFF...
  427. try:
  428. data["fraction"] = sum(fractions) / len(fractions)
  429. except:
  430. data["fraction"] = 0.0
  431. #save(project_location, data)
  432. #data = load(project_location)
  433. return data
  434. def get_asset_data(win, name, force=False):
  435. # This function will return a data of about the asset.
  436. if name not in win.assets or force:
  437. data = {
  438. "fraction":0.0
  439. }
  440. try:
  441. # Now let's get a fraction of the asset
  442. if os.path.exists(win.project+"/ast/"+name+".blend"):
  443. data["fraction"] = 1.0
  444. else:
  445. check = checklist.get_list(win.project+"/dev/"+name+"/asset.progress")
  446. data["fraction"] = check["fraction"]
  447. except:
  448. pass
  449. win.assets[name] = data
  450. return win.assets[name]
  451. def save(project, story):
  452. # This is a save of the new VCStudio. Which is using a standard file format.
  453. # so people could parse the data themselves. ( Or because I'm lazy )
  454. try:
  455. os.mkdir(project+'/pln/')
  456. except:
  457. pass
  458. with open(project+'/pln/story.vcss', 'w') as fp:
  459. json.dump(story, fp, sort_keys=True, indent=4)
  460. def load(project):
  461. # This is a convertion back from JSON data to out beloved python dict
  462. try:
  463. with open(project+'/pln/story.vcss') as json_file:
  464. data = json.load(json_file)
  465. except:
  466. # If there is no file.
  467. data = {
  468. "fraction": 0.0, # Percentage of the Scenes finished.
  469. "camera" : [0,0], # The position of where the user left
  470. "selected": [], # List of selected items in the story editor
  471. "active": None, # Active item.
  472. "scenes" : {}, # List of scenes.
  473. "arrows" : [], # List of connections. (A LIST. NOT DICT.)
  474. "links" : [], # List of links to files or assets.
  475. "markers": {}, # List of markers.
  476. "events" : {} # List of frame like containers. (It will have similar)
  477. } # function as events. But will have no text data with in
  478. # it. It will be more like Blender's node editor's Frame.
  479. # I forgot to update the scenes analytics lol.
  480. for scenename in data["scenes"]:
  481. shotsfractions = []
  482. shotslengths = []
  483. textlength = 0
  484. prevtextpos = 0
  485. for shot in data["scenes"][scenename]["shots"]:
  486. for t in shot[-1]:
  487. textlength = textlength + len(t[-1])
  488. if shot[0] == "shot_block":
  489. # Let's see if it has a checklist.
  490. if os.path.exists(project\
  491. +"/rnd/"+scenename+"/"+shot[1]+"/shot.progress"):
  492. check = checklist.get_list(project\
  493. +"/rnd/"+scenename+"/"+shot[1]+"/shot.progress")
  494. shotsfractions.append(check["fraction"])
  495. else:
  496. folder = project\
  497. +"/rnd/"+scenename+"/"+shot[1]
  498. try:
  499. if len(os.listdir(folder+"/rendered")) > 0:
  500. shotsfractions.append(1.0)
  501. elif len(os.listdir(folder+"/test_rnd")) > 0:
  502. shotsfractions.append(0.8)
  503. elif len(os.listdir(folder+"/opengl")) > 0:
  504. shotsfractions.append(0.6)
  505. elif len(os.listdir(folder+"/storyboard")) > 0:
  506. shotsfractions.append(0.4)
  507. elif len(os.listdir(folder+"/extra")) > 0:
  508. shotsfractions.append(0.2)
  509. except:
  510. shotsfractions.append(0.0)
  511. shotslengths.append( textlength - prevtextpos )
  512. prevtextpos = textlength
  513. # Adjusting fractions based on the length of the text marked.
  514. lastlen = 0
  515. for t in data["scenes"][scenename]["shots"][-1][-1]:
  516. lastlen = lastlen + len(t[-1])
  517. try:
  518. avgtxtlng = sum(shotslengths) / len(shotslengths)
  519. except:
  520. avgtxtlng = 1
  521. avgtxtlng = min(1, avgtxtlng)
  522. sf = []
  523. for n, i in enumerate(shotsfractions):
  524. try:
  525. l = shotslengths[n]
  526. except:
  527. l = 0
  528. nf = l / (textlength - lastlen) * i
  529. sf.append(nf)
  530. # If the last block isn't shot_block. It's safe to assume that
  531. # not all of the shots are yet marked in the text. Therefor we
  532. # want to estimate how much is marked.
  533. multiply_by = 1
  534. if not data["scenes"][scenename].get("no_more_shots") and data["scenes"][scenename]["shots"][-1][0] == "text_block":
  535. try:
  536. multiply_by = (textlength - lastlen) / textlength
  537. except:
  538. multiply_by = 1
  539. try:
  540. data["scenes"][scenename]["fraction"] = \
  541. ( sum(sf) ) * multiply_by
  542. except:
  543. data["scenes"][scenename]["fraction"] = 0.0
  544. fractions = []
  545. lastarrow = 'start'
  546. for i in data["arrows"]:
  547. if lastarrow == "end":
  548. break
  549. for arrow in data["arrows"]:
  550. if arrow[0] == lastarrow:
  551. lastarrow = arrow[1]
  552. if arrow[1] != "end":
  553. fractions.append(
  554. data["scenes"][arrow[1][1]]["fraction"]
  555. )
  556. else:
  557. break
  558. # FINAL STUFF...
  559. try:
  560. data["fraction"] = sum(fractions) / len(fractions)
  561. except:
  562. data["fraction"] = 0.0
  563. return data
  564. def undo_record(win):
  565. # This function will record undo of the story. The undo will be basically
  566. # copies of the script JSON file as a whole. The /pln/story.vcss
  567. # This function will not read the files. It's not nessesary. But it will
  568. # make a copy of the whole thing. So I guess we need to implement some kind
  569. # of limiter.
  570. try:
  571. limit = int(win.settings["Undo_Limit"])
  572. # Now let's advance the undo history 1 forward.
  573. win.undo_index = min(len(win.undo_history)+1, limit)
  574. # Let's read our file. Which might not exist.
  575. f = open(win.project+'/pln/story.vcss')
  576. win.undo_history.append(f.read())
  577. # And now let's limit our selves
  578. win.undo_history = win.undo_history[0-limit:]
  579. except:
  580. pass
  581. # Following 2 functions are a big buggy at the moment. I will aprecieate if
  582. # someone going to look at them. Maybe it's to do with studio/studio_storyLayer.py
  583. # where I was testing them. But they work. Just not every time.
  584. def undo(win):
  585. # This function will do the undo. Tho this function will not nessesarily
  586. # sens the Ctrl - Z by it self. It should be activated from any place
  587. # in the UI.
  588. # Let's move our index back 1 way.
  589. win.undo_index = max(0, win.undo_index-1)
  590. # Now let's read our data
  591. d = win.undo_history[win.undo_index]
  592. # And let's save the data back to the file
  593. f = open(win.project+'/pln/story.vcss', "w")
  594. f.write(d)
  595. f.close()
  596. # And let's refresh story
  597. win.story = load(win.project)
  598. def redo(win):
  599. # This function will do the redo. Tho this function will not nessesarily
  600. # sens the Ctrl - Y by it self. It should be activated from any place
  601. # in the UI.
  602. # Let's move our index back 1 way.
  603. win.undo_index = min(len(win.undo_history)-1, win.undo_index+1)
  604. # Now let's read our data
  605. d = win.undo_history[win.undo_index]
  606. # And let's save the data back to the file
  607. f = open(win.project+'/pln/story.vcss', "w")
  608. f.write(d)
  609. f.close()
  610. # And let's refresh story
  611. win.story = load(win.project)
  612. def export_to_odt(win):
  613. # This function will export the story to the ODT format. The ODT format is
  614. # basically a zip file. With images and other stuff. And a context.xml file
  615. # where you have all the styles and the text with task of style. This is
  616. # very handy since we need to only make a simple text document with a folder
  617. # of images.
  618. # First let's call a file save dialog. I'm going to use the standard Gtk
  619. # one. Maybe will make my own.
  620. folderchooser = Gtk.FileChooserDialog(talk.text("export_tooltip"),
  621. None,
  622. Gtk.FileChooserAction.SAVE,
  623. (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
  624. Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
  625. folderchooser.set_default_response(Gtk.ResponseType.OK)
  626. response = folderchooser.run()
  627. if response == Gtk.ResponseType.OK:
  628. savefilename = folderchooser.get_filename()
  629. else:
  630. folderchooser.destroy()
  631. return
  632. folderchooser.destroy()
  633. # We are going to start the folder for this converion. For this in the
  634. # new_file directory we have an example ODT file. It's basically a zip
  635. # file that we want to use. Let's unzip it.
  636. referencefile = "/new_file/reference.odt"
  637. # Let's clear the folder from previouw parsing if such exists.
  638. try:
  639. os.system("rm -rf /tmp/odt_export")
  640. except:
  641. pass
  642. # And make the folder again.
  643. os.makedirs("/tmp/odt_export")
  644. # Now let's unzip our contents into there.
  645. ref = zipfile.ZipFile(os.getcwd()+referencefile, "r")
  646. ref.extractall("/tmp/odt_export")
  647. ref.close()
  648. # Now there will be the Pictures directory that contains the test image
  649. # of Moria standing in front of the window and looking at the racetrack.
  650. # we don't need it. But we need the folder it self.
  651. try:
  652. os.remove("/tmp/odt_export/Pictures/1000020100000780000004380D1DEC99F7A147C7.png")
  653. except:
  654. pass
  655. # By the way. If you want to look at this image. Just into the reference.odt
  656. # you will see it there in the text.
  657. # Now we going to start writing to the context.xml file. So let's open it.
  658. save = open("/tmp/odt_export/content.xml", "w")
  659. # First stuff that we are going to write are the headers. So LibreOffice
  660. # will not start going nutz. And saying how corrupter the file is.
  661. save.write('''<?xml version="1.0" encoding="UTF-8"?><office:document-content xmlns:officeooo="http://openoffice.org/2009/office" xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:ooo="http://openoffice.org/2004/office" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" office:version="1.2"><office:scripts/><office:font-face-decls><style:font-face style:name="Lohit Devanagari1" svg:font-family="&apos;Lohit Devanagari&apos;"/><style:font-face style:name="Mitra Mono" svg:font-family="&apos;Mitra Mono&apos;" style:font-pitch="fixed"/><style:font-face style:name="Nimbus Mono PS" svg:font-family="&apos;Nimbus Mono PS&apos;" style:font-pitch="fixed"/><style:font-face style:name="Liberation Serif" svg:font-family="&apos;Liberation Serif&apos;" style:font-family-generic="roman" style:font-pitch="variable"/><style:font-face style:name="Liberation Sans" svg:font-family="&apos;Liberation Sans&apos;" style:font-family-generic="swiss" style:font-pitch="variable"/><style:font-face style:name="Lohit Devanagari" svg:font-family="&apos;Lohit Devanagari&apos;" style:font-family-generic="system" style:font-pitch="variable"/><style:font-face style:name="Noto Sans CJK SC" svg:font-family="&apos;Noto Sans CJK SC&apos;" style:font-family-generic="system" style:font-pitch="variable"/><style:font-face style:name="Noto Serif CJK SC" svg:font-family="&apos;Noto Serif CJK SC&apos;" style:font-family-generic="system" style:font-pitch="variable"/></office:font-face-decls><office:automatic-styles><style:style style:name="P1" style:family="paragraph" style:parent-style-name="Footer"><style:text-properties officeooo:paragraph-rsid="0028a095"/></style:style><style:style style:name="P2" style:family="paragraph" style:parent-style-name="Standard"><style:text-properties style:font-name="Nimbus Mono PS" officeooo:rsid="0017b0ac" officeooo:paragraph-rsid="0017b0ac"/></style:style><style:style style:name="frase_name" style:family="paragraph" style:parent-style-name="Standard"><style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/><style:text-properties style:font-name="Nimbus Mono PS" fo:font-weight="bold" officeooo:rsid="0017b0ac" officeooo:paragraph-rsid="0017b0ac" style:font-weight-asian="bold" style:font-weight-complex="bold"/></style:style><style:style style:name="P4" style:family="paragraph" style:parent-style-name="Standard"><style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/><style:text-properties style:font-name="Nimbus Mono PS" fo:font-size="22pt" fo:font-weight="bold" officeooo:rsid="0017b0ac" officeooo:paragraph-rsid="0017b0ac" style:font-size-asian="19.25pt" style:font-weight-asian="bold" style:font-size-complex="22pt" style:font-weight-complex="bold"/></style:style><style:style style:name="P5" style:family="paragraph" style:parent-style-name="Standard"><style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/><style:text-properties style:font-name="Nimbus Mono PS" fo:font-size="22pt" fo:font-weight="bold" officeooo:rsid="001af4f2" officeooo:paragraph-rsid="001af4f2" style:font-size-asian="19.25pt" style:font-weight-asian="bold" style:font-size-complex="22pt" style:font-weight-complex="bold"/></style:style><style:style style:name="P6" style:family="paragraph" style:parent-style-name="Standard"><style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/><style:text-properties style:font-name="Nimbus Mono PS" fo:font-size="18pt" fo:font-weight="bold" officeooo:rsid="001af4f2" officeooo:paragraph-rsid="001af4f2" style:font-size-asian="15.75pt" style:font-weight-asian="bold" style:font-size-complex="18pt" style:font-weight-complex="bold"/></style:style><style:style style:name="P7" style:family="paragraph" style:parent-style-name="Standard"><style:paragraph-properties fo:text-align="center" style:justify-single-word="false"/><style:text-properties style:font-name="Nimbus Mono PS" fo:font-size="14pt" fo:font-weight="bold" officeooo:rsid="001af4f2" officeooo:paragraph-rsid="001af4f2" style:font-size-asian="12.25pt" style:font-weight-asian="bold" style:font-size-complex="14pt" style:font-weight-complex="bold"/></style:style><style:style style:name="P8" style:family="paragraph" style:parent-style-name="Standard"><style:text-properties officeooo:paragraph-rsid="0017b0ac"/></style:style><style:style style:name="P9" style:family="paragraph" style:parent-style-name="Standard"><style:text-properties fo:color="#666666" style:font-name="Nimbus Mono PS" fo:font-size="10.5pt" fo:font-style="italic" officeooo:rsid="0017b0ac" officeooo:paragraph-rsid="0017b0ac" style:font-size-asian="10.5pt" style:font-style-asian="italic" style:font-size-complex="10.5pt" style:font-style-complex="italic"/></style:style><style:style style:name="frase" style:family="paragraph" style:parent-style-name="Standard" style:master-page-name=""><loext:graphic-properties draw:fill="none"/><style:paragraph-properties fo:margin-left="1in" fo:margin-right="1in" fo:text-align="justify" style:justify-single-word="false" fo:text-indent="0in" style:auto-text-indent="false" style:page-number="auto" fo:background-color="transparent"/><style:text-properties style:font-name="Nimbus Mono PS" officeooo:rsid="0017b0ac" officeooo:paragraph-rsid="0017b0ac"/></style:style><style:style style:name="P11" style:family="paragraph" style:parent-style-name="Standard"><loext:graphic-properties draw:fill="none"/><style:paragraph-properties fo:margin-left="1in" fo:margin-right="1in" fo:text-align="justify" style:justify-single-word="false" fo:text-indent="0in" style:auto-text-indent="false" fo:background-color="transparent"/><style:text-properties style:font-name="Nimbus Mono PS" officeooo:rsid="0017b0ac" officeooo:paragraph-rsid="0017b0ac"/></style:style><style:style style:name="scene_name" style:family="paragraph" style:parent-style-name="Standard"><style:paragraph-properties fo:text-align="start" style:justify-single-word="false" fo:break-before="page"/><style:text-properties style:font-name="Nimbus Mono PS" fo:font-weight="bold" officeooo:rsid="0017b0ac" officeooo:paragraph-rsid="0017b0ac" style:font-weight-asian="bold" style:font-weight-complex="bold"/></style:style><style:style style:name="P13" style:family="paragraph" style:parent-style-name="Standard"><loext:graphic-properties draw:fill="none"/><style:paragraph-properties fo:margin-left="0in" fo:margin-right="1in" fo:text-align="justify" style:justify-single-word="false" fo:text-indent="0in" style:auto-text-indent="false" fo:background-color="transparent"/><style:text-properties style:font-name="Nimbus Mono PS" officeooo:rsid="0017b0ac" officeooo:paragraph-rsid="0024426e"/></style:style><style:style style:name="image_style" style:family="paragraph" style:parent-style-name="Standard" style:master-page-name=""><loext:graphic-properties draw:fill="none"/><style:paragraph-properties fo:margin-left="0in" fo:margin-right="0in" fo:text-align="justify" style:justify-single-word="false" fo:text-indent="0in" style:auto-text-indent="false" style:page-number="auto" fo:background-color="transparent"/><style:text-properties style:font-name="Nimbus Mono PS" officeooo:rsid="0017b0ac" officeooo:paragraph-rsid="0028a095"/></style:style><style:style style:name="P15" style:family="paragraph" style:parent-style-name="Standard"><loext:graphic-properties draw:fill="none"/><style:paragraph-properties fo:margin-left="0in" fo:margin-right="0in" fo:text-align="justify" style:justify-single-word="false" fo:text-indent="0in" style:auto-text-indent="false" fo:background-color="transparent"/><style:text-properties style:font-name="Nimbus Mono PS" officeooo:rsid="0017b0ac" officeooo:paragraph-rsid="0028a095"/></style:style><style:style style:name="P16" style:family="paragraph" style:parent-style-name="Standard"><loext:graphic-properties draw:fill="none"/><style:paragraph-properties fo:margin-left="0in" fo:margin-right="0in" fo:text-align="justify" style:justify-single-word="false" fo:text-indent="0in" style:auto-text-indent="false" fo:background-color="transparent"/><style:text-properties style:font-name="Nimbus Mono PS" officeooo:rsid="0028a095" officeooo:paragraph-rsid="0028a095"/></style:style><style:style style:name="P17" style:family="paragraph" style:parent-style-name="Standard"><loext:graphic-properties draw:fill="none"/><style:paragraph-properties fo:margin-left="0in" fo:margin-right="0in" fo:text-align="center" style:justify-single-word="false" fo:text-indent="0in" style:auto-text-indent="false" fo:background-color="transparent"/><style:text-properties style:font-name="Nimbus Mono PS" officeooo:rsid="0028a095" officeooo:paragraph-rsid="0028a095" fo:background-color="#808080"/></style:style><style:style style:name="T1" style:family="text"><style:text-properties fo:font-weight="bold" fo:background-color="transparent" loext:char-shading-value="0" style:font-weight-asian="bold" style:font-weight-complex="bold"/></style:style><style:style style:name="T2" style:family="text"><style:text-properties fo:font-weight="bold" fo:background-color="#ffffff" loext:char-shading-value="0" style:font-weight-asian="bold" style:font-weight-complex="bold"/></style:style><style:style style:name="T3" style:family="text"><style:text-properties style:font-name="Nimbus Mono PS" officeooo:rsid="0017b0ac"/></style:style><style:style style:name="T4" style:family="text"><style:text-properties style:font-name="Nimbus Mono PS" officeooo:rsid="0017cba4"/></style:style><style:style style:name="shot_1" style:family="text"><style:text-properties style:font-name="Nimbus Mono PS" officeooo:rsid="0017b0ac" fo:background-color="#ffb66c" loext:char-shading-value="0"/></style:style><style:style style:name="shot_2" style:family="text"><style:text-properties style:font-name="Nimbus Mono PS" officeooo:rsid="0017b0ac" fo:background-color="#ffe994" loext:char-shading-value="0"/></style:style><style:style style:name="shot_3" style:family="text"><style:text-properties style:font-name="Nimbus Mono PS" officeooo:rsid="0017b0ac" fo:background-color="#e0c2cd" loext:char-shading-value="0"/></style:style><style:style style:name="shot_4" style:family="text"><style:text-properties style:font-name="Nimbus Mono PS" officeooo:rsid="0017b0ac" fo:background-color="#b4c7dc" loext:char-shading-value="0"/></style:style><style:style style:name="shot_5" style:family="text"><style:text-properties style:font-name="Nimbus Mono PS" officeooo:rsid="0017b0ac" fo:background-color="#afd095" loext:char-shading-value="0"/></style:style><style:style style:name="T10" style:family="text"><style:text-properties style:font-name="Nimbus Mono PS" officeooo:rsid="0028a095"/></style:style><style:style style:name="T11" style:family="text"><style:text-properties style:font-name="Nimbus Mono PS" fo:font-weight="bold" officeooo:rsid="0028a095" fo:background-color="#ffffff" loext:char-shading-value="0" style:font-weight-asian="bold" style:font-weight-complex="bold"/></style:style><style:style style:name="T12" style:family="text"><style:text-properties style:font-name="Nimbus Mono PS" fo:font-weight="normal" officeooo:rsid="0028a095" fo:background-color="#ffffff" loext:char-shading-value="0" style:font-weight-asian="normal" style:font-weight-complex="normal"/></style:style><style:style style:name="T13" style:family="text"><style:text-properties officeooo:rsid="0028a095"/></style:style><style:style style:name="T14" style:family="text"><style:text-properties fo:background-color="transparent" loext:char-shading-value="0"/></style:style><style:style style:name="T15" style:family="text"><style:text-properties fo:background-color="transparent" loext:char-shading-value="0"/></style:style><style:style style:name="fr1" style:family="graphic" style:parent-style-name="Graphics"><style:graphic-properties style:mirror="none" fo:clip="rect(0in, 0in, 0in, 0in)" draw:luminance="0%" draw:contrast="0%" draw:red="0%" draw:green="0%" draw:blue="0%" draw:gamma="100%" draw:color-inversion="false" draw:image-opacity="100%" draw:color-mode="standard"/></style:style></office:automatic-styles><office:body><office:text text:use-soft-page-breaks="true"><office:forms form:automatic-focus="false" form:apply-design-mode="false"/><text:sequence-decls><text:sequence-decl text:display-outline-level="0" text:name="Illustration"/><text:sequence-decl text:display-outline-level="0" text:name="Table"/><text:sequence-decl text:display-outline-level="0" text:name="Text"/><text:sequence-decl text:display-outline-level="0" text:name="Drawing"/><text:sequence-decl text:display-outline-level="0" text:name="Figure"/></text:sequence-decls><text:p text:style-name="P4"/><text:p text:style-name="P4"/><text:p text:style-name="P4"/><text:p text:style-name="P4"/><text:p text:style-name="P4"/><text:p text:style-name="P4"/><text:p text:style-name="P4"/><text:p text:style-name="P4"/><text:p text:style-name="P4"/><text:p text:style-name="P4"/><text:p text:style-name="P4"/>''')
  662. # Now let's write the name of the project
  663. save.write('<text:p text:style-name="P5">'+win.analytics["name"]+'</text:p>')
  664. # Now let's write the director's name
  665. save.write('<text:p text:style-name="P6">'+win.analytics["director"]+'</text:p>')
  666. # Now let's write the comment
  667. save.write('<text:p text:style-name="P7">'+win.analytics["status"]+'</text:p>')
  668. # Now let's write scenes them selves. We don't want all scene but only
  669. # ones in the main chain. So we are ignoring the scenes that are meant for
  670. # tests and such.
  671. lastarrow = 'start'
  672. for i in win.story["arrows"]:
  673. if lastarrow == "end":
  674. break
  675. for arrow in win.story["arrows"]:
  676. if arrow[0] == lastarrow:
  677. lastarrow = arrow[1]
  678. if arrow[1] != "end":
  679. # Here is our scene name.
  680. save.write('<text:p text:style-name="scene_name">'+arrow[1][1]+'</text:p>')
  681. save.write('<text:p text:style-name="P2"/>')
  682. # We need to get all the assets of the scene and write them
  683. # net under the scene name.
  684. assets = get_assets(win, arrow[1][1])
  685. for cur in ["chr", "veh", "loc", "obj"]:
  686. t = talk.text(cur)+": "
  687. for a in assets:
  688. if cur in a:
  689. t = t +a[a.rfind("/")+1:]+", "
  690. t = t[:-2]
  691. if t != talk.text(cur):
  692. save.write('<text:p text:style-name="P9">'+t+'</text:p>')
  693. # Now let's actually write the text of the scene in there.
  694. scene = win.story["scenes"][arrow[1][1]]["shots"]
  695. save.write('<text:p text:style-name="P2"/>')
  696. save.write('<text:p text:style-name="P8">')
  697. for block in scene:
  698. # For just text parts.
  699. if block[0] == "text_block":
  700. style = "T3"
  701. else:
  702. # If It's a shot. I want the shot color marking
  703. shotis = block[1]
  704. rcolors = [
  705. "shot_1",
  706. "shot_2",
  707. "shot_3",
  708. "shot_4",
  709. "shot_5"
  710. ]
  711. # Getting the color. It's not always works.
  712. if "shot_colors" not in win.story:
  713. win.story["shot_colors"] = {}
  714. surl = "/"+arrow[1][1]+"/"+shotis
  715. if surl not in win.story["shot_colors"]:
  716. win.story["shot_colors"][surl] = rcolors[len(win.story["shot_colors"]) % len(rcolors)]
  717. style = win.story["shot_colors"][surl]
  718. # Now let's itterate over the text in the block.
  719. for text in block[-1]:
  720. # For just regular parts we have our regular text
  721. if text[0] == "text" or text[0] == "link":
  722. for line in re.split("(\n)",text[-1]):
  723. if line == "\n":
  724. save.write('</text:p>')
  725. save.write('<text:p text:style-name="P8">')
  726. else:
  727. save.write('<text:span text:style-name="'+style+'">'+line+'</text:span>')
  728. # For frases it's a bit different
  729. elif text[0] == "frase":
  730. # We gonna close the simle text part.
  731. save.write('</text:p>')
  732. # We gonna make a new line
  733. save.write('<text:p text:style-name="P2"/>')
  734. # We gonna write the name of the speaker
  735. save.write('<text:p text:style-name="frase_name">'+text[1][-1]+'</text:p>')
  736. # Then we open the text type
  737. save.write('<text:p text:style-name="frase">')
  738. # Then we write the frase
  739. for line in re.split("(\n)",text[-1]):
  740. if line == "\n":
  741. save.write('</text:p>')
  742. save.write('<text:p text:style-name="frase">')
  743. else:
  744. save.write('<text:span text:style-name="'+style+'">'+line+'</text:span>')
  745. # Then we close the frase
  746. save.write('</text:p>')
  747. # And open back the other one
  748. save.write('<text:p text:style-name="P8">')
  749. # Now let's make images work. Okay. So there is our
  750. # images folder. We need to copy our image to there
  751. # first.
  752. elif text[0] == "image":
  753. # The image could be relative or absolute.
  754. tmpurl = text[-1].replace("/", "_")
  755. if os.path.exists(win.project+text[-1]):
  756. f = open(win.project+text[-1], "rb")
  757. else:
  758. f = open(text[-1], "rb")
  759. s = open("/tmp/odt_export/Pictures/"+tmpurl, "wb")
  760. s.write(f.read())
  761. f.close()
  762. s.close()
  763. # Now we have a copy of the image in the
  764. # file. We need to draw it.
  765. save.write('</text:p>')
  766. save.write('<text:p text:style-name="P13"/><text:p text:style-name="image_style"><draw:frame draw:style-name="fr1" draw:name="Image1" text:anchor-type="char" svg:width="6.6929in" svg:height="3.7646in" draw:z-index="0">')
  767. save.write('<draw:image xlink:href="Pictures/'+tmpurl+'" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" loext:mime-type="image/png"/>')
  768. save.write('</draw:frame></text:p>')
  769. save.write('<text:p text:style-name="P8">')
  770. # There is a problem tho. We need to write
  771. # the existance of this file into another
  772. # place. if we don't do that. LibreOffice will
  773. # think that the ODT is broken. So it will fix
  774. # it and all gonna be great. Still not the best
  775. # experience for the users. Please somebody
  776. # figure it out.
  777. save.write('</text:p>')
  778. # After every scene is want a little line in the bottom
  779. save.write('<text:p text:style-name="P2"/>')
  780. save.write('<text:p text:style-name="P2"/>')
  781. save.write('<text:p text:style-name="P17"><text:s text:c="66"/></text:p>')
  782. else:
  783. break
  784. # Now let's close the file
  785. save.write('</office:text></office:body></office:document-content>')
  786. save.close()
  787. # And now let's make the odt file in the given savefilename place
  788. def zip_odt(src, dst):
  789. zf = zipfile.ZipFile("%s.odt" % (dst), "w", zipfile.ZIP_DEFLATED) #changed file extension
  790. abs_src = os.path.abspath(src)
  791. for dirname, subdirs, files in os.walk(src):
  792. for filename in files:
  793. absname = os.path.abspath(os.path.join(dirname, filename))
  794. arcname = absname[len(abs_src) + 1:]
  795. zf.write(absname, arcname)
  796. zf.close()
  797. zip_odt("/tmp/odt_export", savefilename)
  798. def get_assets(win, scene):
  799. # This funtion will get the assets from any given scene.
  800. assets = []
  801. # Now let's read through the scene.
  802. for shot in win.story["scenes"][scene]["shots"]:
  803. # Let's read the shot
  804. for asset in shot[-1]:
  805. # Ignore text
  806. if asset[0] == "text":
  807. continue
  808. # It's it's a staight up link. Just add it.
  809. if asset[0] == "link" and asset[1] not in assets:
  810. assets.append(asset[1])
  811. # It's a link in a frase.
  812. if asset[0] == "frase" and asset[1][0] == "link" and asset[1][1] not in assets:
  813. assets.append(asset[1][1])
  814. return assets