story.py 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134
  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. for shot in data["scenes"][scenename]["shots"]:
  483. if shot[0] == "shot_block":
  484. # Let's see if it has a checklist.
  485. if os.path.exists(project\
  486. +"/rnd/"+scenename+"/"+shot[1]+"/shot.progress"):
  487. check = checklist.get_list(project\
  488. +"/rnd/"+scenename+"/"+shot[1]+"/shot.progress")
  489. shotsfractions.append(check["fraction"])
  490. else:
  491. folder = project\
  492. +"/rnd/"+scenename+"/"+shot[1]
  493. try:
  494. if len(os.listdir(folder+"/rendered")) > 0:
  495. shotsfractions.append(1.0)
  496. elif len(os.listdir(folder+"/test_rnd")) > 0:
  497. shotsfractions.append(0.8)
  498. elif len(os.listdir(folder+"/opengl")) > 0:
  499. shotsfractions.append(0.6)
  500. elif len(os.listdir(folder+"/storyboard")) > 0:
  501. shotsfractions.append(0.4)
  502. elif len(os.listdir(folder+"/extra")) > 0:
  503. shotsfractions.append(0.2)
  504. except:
  505. shotsfractions.append(0.0)
  506. try:
  507. data["scenes"][scenename]["fraction"] = \
  508. sum(shotsfractions) / len(shotsfractions)
  509. except:
  510. data["scenes"][scenename]["fraction"] = 0.0
  511. fractions = []
  512. lastarrow = 'start'
  513. for i in data["arrows"]:
  514. if lastarrow == "end":
  515. break
  516. for arrow in data["arrows"]:
  517. if arrow[0] == lastarrow:
  518. lastarrow = arrow[1]
  519. if arrow[1] != "end":
  520. fractions.append(
  521. data["scenes"][arrow[1][1]]["fraction"]
  522. )
  523. else:
  524. break
  525. # FINAL STUFF...
  526. try:
  527. data["fraction"] = sum(fractions) / len(fractions)
  528. except:
  529. data["fraction"] = 0.0
  530. return data
  531. def undo_record(win):
  532. # This function will record undo of the story. The undo will be basically
  533. # copies of the script JSON file as a whole. The /pln/story.vcss
  534. # This function will not read the files. It's not nessesary. But it will
  535. # make a copy of the whole thing. So I guess we need to implement some kind
  536. # of limiter.
  537. try:
  538. limit = int(win.settings["Undo_Limit"])
  539. # Now let's advance the undo history 1 forward.
  540. win.undo_index = min(len(win.undo_history)+1, limit)
  541. # Let's read our file. Which might not exist.
  542. f = open(win.project+'/pln/story.vcss')
  543. win.undo_history.append(f.read())
  544. # And now let's limit our selves
  545. win.undo_history = win.undo_history[0-limit:]
  546. except:
  547. pass
  548. # Following 2 functions are a big buggy at the moment. I will aprecieate if
  549. # someone going to look at them. Maybe it's to do with studio/studio_storyLayer.py
  550. # where I was testing them. But they work. Just not every time.
  551. def undo(win):
  552. # This function will do the undo. Tho this function will not nessesarily
  553. # sens the Ctrl - Z by it self. It should be activated from any place
  554. # in the UI.
  555. # Let's move our index back 1 way.
  556. win.undo_index = max(0, win.undo_index-1)
  557. # Now let's read our data
  558. d = win.undo_history[win.undo_index]
  559. # And let's save the data back to the file
  560. f = open(win.project+'/pln/story.vcss', "w")
  561. f.write(d)
  562. f.close()
  563. # And let's refresh story
  564. win.story = load(win.project)
  565. def redo(win):
  566. # This function will do the redo. Tho this function will not nessesarily
  567. # sens the Ctrl - Y by it self. It should be activated from any place
  568. # in the UI.
  569. # Let's move our index back 1 way.
  570. win.undo_index = min(len(win.undo_history)-1, win.undo_index+1)
  571. # Now let's read our data
  572. d = win.undo_history[win.undo_index]
  573. # And let's save the data back to the file
  574. f = open(win.project+'/pln/story.vcss', "w")
  575. f.write(d)
  576. f.close()
  577. # And let's refresh story
  578. win.story = load(win.project)
  579. def export_to_odt(win):
  580. # This function will export the story to the ODT format. The ODT format is
  581. # basically a zip file. With images and other stuff. And a context.xml file
  582. # where you have all the styles and the text with task of style. This is
  583. # very handy since we need to only make a simple text document with a folder
  584. # of images.
  585. # First let's call a file save dialog. I'm going to use the standard Gtk
  586. # one. Maybe will make my own.
  587. folderchooser = Gtk.FileChooserDialog(talk.text("export_tooltip"),
  588. None,
  589. Gtk.FileChooserAction.SAVE,
  590. (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
  591. Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
  592. folderchooser.set_default_response(Gtk.ResponseType.OK)
  593. response = folderchooser.run()
  594. if response == Gtk.ResponseType.OK:
  595. savefilename = folderchooser.get_filename()
  596. else:
  597. folderchooser.destroy()
  598. return
  599. folderchooser.destroy()
  600. # We are going to start the folder for this converion. For this in the
  601. # new_file directory we have an example ODT file. It's basically a zip
  602. # file that we want to use. Let's unzip it.
  603. referencefile = "/new_file/reference.odt"
  604. # Let's clear the folder from previouw parsing if such exists.
  605. try:
  606. os.system("rm -rf /tmp/odt_export")
  607. except:
  608. pass
  609. # And make the folder again.
  610. os.makedirs("/tmp/odt_export")
  611. # Now let's unzip our contents into there.
  612. ref = zipfile.ZipFile(os.getcwd()+referencefile, "r")
  613. ref.extractall("/tmp/odt_export")
  614. ref.close()
  615. # Now there will be the Pictures directory that contains the test image
  616. # of Moria standing in front of the window and looking at the racetrack.
  617. # we don't need it. But we need the folder it self.
  618. try:
  619. os.remove("/tmp/odt_export/Pictures/1000020100000780000004380D1DEC99F7A147C7.png")
  620. except:
  621. pass
  622. # By the way. If you want to look at this image. Just into the reference.odt
  623. # you will see it there in the text.
  624. # Now we going to start writing to the context.xml file. So let's open it.
  625. save = open("/tmp/odt_export/content.xml", "w")
  626. # First stuff that we are going to write are the headers. So LibreOffice
  627. # will not start going nutz. And saying how corrupter the file is.
  628. 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"/>''')
  629. # Now let's write the name of the project
  630. save.write('<text:p text:style-name="P5">'+win.analytics["name"]+'</text:p>')
  631. # Now let's write the director's name
  632. save.write('<text:p text:style-name="P6">'+win.analytics["director"]+'</text:p>')
  633. # Now let's write the comment
  634. save.write('<text:p text:style-name="P7">'+win.analytics["status"]+'</text:p>')
  635. # Now let's write scenes them selves. We don't want all scene but only
  636. # ones in the main chain. So we are ignoring the scenes that are meant for
  637. # tests and such.
  638. lastarrow = 'start'
  639. for i in win.story["arrows"]:
  640. if lastarrow == "end":
  641. break
  642. for arrow in win.story["arrows"]:
  643. if arrow[0] == lastarrow:
  644. lastarrow = arrow[1]
  645. if arrow[1] != "end":
  646. # Here is our scene name.
  647. save.write('<text:p text:style-name="scene_name">'+arrow[1][1]+'</text:p>')
  648. save.write('<text:p text:style-name="P2"/>')
  649. # We need to get all the assets of the scene and write them
  650. # net under the scene name.
  651. assets = get_assets(win, arrow[1][1])
  652. for cur in ["chr", "veh", "loc", "obj"]:
  653. t = talk.text(cur)+": "
  654. for a in assets:
  655. if cur in a:
  656. t = t +a[a.rfind("/")+1:]+", "
  657. t = t[:-2]
  658. if t != talk.text(cur):
  659. save.write('<text:p text:style-name="P9">'+t+'</text:p>')
  660. # Now let's actually write the text of the scene in there.
  661. scene = win.story["scenes"][arrow[1][1]]["shots"]
  662. save.write('<text:p text:style-name="P2"/>')
  663. save.write('<text:p text:style-name="P8">')
  664. for block in scene:
  665. # For just text parts.
  666. if block[0] == "text_block":
  667. style = "T3"
  668. else:
  669. # If It's a shot. I want the shot color marking
  670. shotis = block[1]
  671. rcolors = [
  672. "shot_1",
  673. "shot_2",
  674. "shot_3",
  675. "shot_4",
  676. "shot_5"
  677. ]
  678. # Getting the color. It's not always works.
  679. if "shot_colors" not in win.story:
  680. win.story["shot_colors"] = {}
  681. surl = "/"+arrow[1][1]+"/"+shotis
  682. if surl not in win.story["shot_colors"]:
  683. win.story["shot_colors"][surl] = rcolors[len(win.story["shot_colors"]) % len(rcolors)]
  684. style = win.story["shot_colors"][surl]
  685. # Now let's itterate over the text in the block.
  686. for text in block[-1]:
  687. # For just regular parts we have our regular text
  688. if text[0] == "text" or text[0] == "link":
  689. for line in re.split("(\n)",text[-1]):
  690. if line == "\n":
  691. save.write('</text:p>')
  692. save.write('<text:p text:style-name="P8">')
  693. else:
  694. save.write('<text:span text:style-name="'+style+'">'+line+'</text:span>')
  695. # For frases it's a bit different
  696. elif text[0] == "frase":
  697. # We gonna close the simle text part.
  698. save.write('</text:p>')
  699. # We gonna make a new line
  700. save.write('<text:p text:style-name="P2"/>')
  701. # We gonna write the name of the speaker
  702. save.write('<text:p text:style-name="frase_name">'+text[1][-1]+'</text:p>')
  703. # Then we open the text type
  704. save.write('<text:p text:style-name="frase">')
  705. # Then we write the frase
  706. for line in re.split("(\n)",text[-1]):
  707. if line == "\n":
  708. save.write('</text:p>')
  709. save.write('<text:p text:style-name="frase">')
  710. else:
  711. save.write('<text:span text:style-name="'+style+'">'+line+'</text:span>')
  712. # Then we close the frase
  713. save.write('</text:p>')
  714. # And open back the other one
  715. save.write('<text:p text:style-name="P8">')
  716. # Now let's make images work. Okay. So there is our
  717. # images folder. We need to copy our image to there
  718. # first.
  719. elif text[0] == "image":
  720. # The image could be relative or absolute.
  721. tmpurl = text[-1].replace("/", "_")
  722. if os.path.exists(win.project+text[-1]):
  723. f = open(win.project+text[-1], "rb")
  724. else:
  725. f = open(text[-1], "rb")
  726. s = open("/tmp/odt_export/Pictures/"+tmpurl, "wb")
  727. s.write(f.read())
  728. f.close()
  729. s.close()
  730. # Now we have a copy of the image in the
  731. # file. We need to draw it.
  732. save.write('</text:p>')
  733. 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">')
  734. save.write('<draw:image xlink:href="Pictures/'+tmpurl+'" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad" loext:mime-type="image/png"/>')
  735. save.write('</draw:frame></text:p>')
  736. save.write('<text:p text:style-name="P8">')
  737. # There is a problem tho. We need to write
  738. # the existance of this file into another
  739. # place. if we don't do that. LibreOffice will
  740. # think that the ODT is broken. So it will fix
  741. # it and all gonna be great. Still not the best
  742. # experience for the users. Please somebody
  743. # figure it out.
  744. save.write('</text:p>')
  745. # After every scene is want a little line in the bottom
  746. save.write('<text:p text:style-name="P2"/>')
  747. save.write('<text:p text:style-name="P2"/>')
  748. save.write('<text:p text:style-name="P17"><text:s text:c="66"/></text:p>')
  749. else:
  750. break
  751. # Now let's close the file
  752. save.write('</office:text></office:body></office:document-content>')
  753. save.close()
  754. # And now let's make the odt file in the given savefilename place
  755. def zip_odt(src, dst):
  756. zf = zipfile.ZipFile("%s.odt" % (dst), "w", zipfile.ZIP_DEFLATED) #changed file extension
  757. abs_src = os.path.abspath(src)
  758. for dirname, subdirs, files in os.walk(src):
  759. for filename in files:
  760. absname = os.path.abspath(os.path.join(dirname, filename))
  761. arcname = absname[len(abs_src) + 1:]
  762. zf.write(absname, arcname)
  763. zf.close()
  764. zip_odt("/tmp/odt_export", savefilename)
  765. def get_assets(win, scene):
  766. # This funtion will get the assets from any given scene.
  767. assets = []
  768. # Now let's read through the scene.
  769. for shot in win.story["scenes"][scene]["shots"]:
  770. # Let's read the shot
  771. for asset in shot[-1]:
  772. # Ignore text
  773. if asset[0] == "text":
  774. continue
  775. # It's it's a staight up link. Just add it.
  776. if asset[0] == "link" and asset[1] not in assets:
  777. assets.append(asset[1])
  778. # It's a link in a frase.
  779. if asset[0] == "frase" and asset[1][0] == "link" and asset[1][1] not in assets:
  780. assets.append(asset[1][1])
  781. return assets