Upload files to 'studio'

This commit is contained in:
Jeison Yehuda Amihud (Blender Dumbass) 2020-12-25 14:20:34 +00:00
parent 92b66f0cc7
commit 6f309f0784
10 changed files with 726 additions and 34 deletions

View file

@ -363,6 +363,22 @@ def layer(win):
# And before we start a little settings icon.
# Documentation entry
def do():
def after(win, var):
pass
studio_dialogs.help(win, "help", after, SEARCH=talk.text("documentation_analytics"))
UI_elements.roundrect(layer, win,
win.current["w"]/4*3-110,
225,
40,
40,
10,
do,
"question")
# Settings
def do():
win.url = "settings_layer"

View file

@ -809,6 +809,22 @@ def layer(win):
### SCENES ###
# Documentation entry
def do():
def after(win, var):
pass
studio_dialogs.help(win, "help", after, SEARCH=talk.text("documentation_assets"))
UI_elements.roundrect(layer, win,
win.current["w"]-40-win.current["w"]/4-50,
win.current["h"]-80,
40,
40,
10,
do,
"question")
# CANCEl
def do():

View file

@ -28,6 +28,7 @@ from UI import UI_color
from studio import studio_dialogs
from studio import analytics
from studio import story
from studio import history
def layer(win, call):
@ -98,6 +99,23 @@ def layer(win, call):
win.current["h"]-200,
10)
# Documentation entry
def do():
def after(win, var):
pass
studio_dialogs.help(win, "help", after, SEARCH=talk.text("documentation_link_assets"))
UI_elements.roundrect(layer, win,
win.current["w"]/2-250,
win.current["h"]-140,
40,
40,
10,
do,
"question")
# Exit button
def do():
win.current["calls"][call]["var"] = False
@ -458,6 +476,9 @@ def layer(win, call):
autolink.write("Proxy : "+obj+"\n")
autolink.close()
# And a history entry real quick
history.record(win, "/dev"+win.current["asset_configure"]["asset"]+"/autolink.data", "[Updated]")
# And we need to quit the window. So...
win.current["calls"][call]["var"] = False

View file

@ -68,15 +68,22 @@ from studio import studio_nodes
from UI import UI_elements
from UI import UI_color
#################
################################################### ########################
# # #
from studio import studio_file_selectLayer # # These modules he- #
from studio import studio_asset_selectLayer # # re. Are the modu- #
from studio import studio_shot_linkLayer # # les that are ac- #
from studio import studio_asset_configureLayer # # tual UI of dialo- #
from studio import studio_renderLayer # # gs that I was ta- #
from studio import studio_vseLayer # # lking about at #
from UI import UI_helpDialog # # the top. #
# # #
################################################### ########################
from studio import studio_file_selectLayer
from studio import studio_asset_selectLayer
from studio import studio_shot_linkLayer
from studio import studio_asset_configureLayer
from studio import studio_renderLayer
# ^
# |
#################
# Who does that?
def file_select(win, name, call, force=False, IMAGE=True, BLEND=False, VIDEO=True,
FILE=False, CHR=True, VEH=True, LOC=True, OBJ=True, RND=False, FOLDER=False,
@ -252,8 +259,6 @@ def render(win, name, call, filename="", force=False):
# This function going to launch a window that shows all current renders and
# confuge them.
print("Test 2")
if name not in win.current["calls"]:
win.current["calls"][name] = {
"var" :None, # This is the variable that we are waiting for
@ -277,3 +282,42 @@ def render(win, name, call, filename="", force=False):
# Let's clear the LMB just in case
win.previous["LMB"] = False
def vse(win, name, call, filename="", force=False):
# This function going to select vse blend files.
if name not in win.current["calls"]:
win.current["calls"][name] = {
"var" :None, # This is the variable that we are waiting for
"call":call, # This is what it's going to run when it's done
"url" :"vse",
"back":win.url,# This is where it's going to come back when it's done
"draw":studio_vseLayer.layer
}
# Let's clear the LMB just in case
win.previous["LMB"] = False
def help(win, name, call, filename="", force=False, SEARCH=""):
# This function going to select vse blend files.
if name not in win.current["calls"]:
win.current["calls"][name] = {
"var" :None, # This is the variable that we are waiting for
"call":call, # This is what it's going to run when it's done
"url" :"help",
"back":win.url,# This is where it's going to come back when it's done
"draw":UI_helpDialog.layer
}
# Let's clear the LMB just in case
win.previous["LMB"] = False
win.text["in_help"] = {
"text" :SEARCH, # Actuall text you are editing.
"cursor":[len(str(SEARCH)),len(str(SEARCH))], # Cursor
"insert":False, # Whether the insert mode is on
"scroll":"in_help_search_scroll" # If multiline. The pointer for the scroll value.
}

View file

@ -98,6 +98,8 @@ def run(project, win):
win.checklists = {}
win.blink = False # Cursor blinking thing.
win.renders = {} # List of current active renders.
win.undo_history = []
win.undo_index = 0
if pm_project.is_legacy(project):
win.story = story.get_legacy(project)
@ -276,25 +278,24 @@ def pmdrawing(pmdrawing, main_layer, win):
win.calllayer = False
remlater = []
try:
for call in win.current["calls"]:
if win.current["calls"][call]["var"] == None:
Layers.append([win.current["calls"][call]["draw"](win, call)])
win.url = win.current["calls"][call]["url"]
win.calllayer = True
else:
win.url = win.current["calls"][call]["back"]
win.current["calls"][call]["call"](win, win.current["calls"][call]["var"])
win.textactive = ""
remlater.append(call)
for call in list(win.current["calls"].keys()):
for call in remlater:
if win.current["calls"][call]["var"] == None:
Layers.append([win.current["calls"][call]["draw"](win, call)])
win.url = win.current["calls"][call]["url"]
win.calllayer = True
del win.current["calls"][call]
except:
raise() # WHILE DEVELOPING DIALOGS SET THIS TO raise()
else:
win.url = win.current["calls"][call]["back"]
win.current["calls"][call]["call"](win, win.current["calls"][call]["var"])
win.textactive = ""
remlater.append(call)
for call in remlater:
del win.current["calls"][call]
Layers.append([UI_testing.layer(win)])
Layers.append([win.tooltip_surface])

View file

@ -104,7 +104,21 @@ def layer(win, call):
win.current["h"]-200,
10)
# Documentation entry
def do():
def after(win, var):
pass
studio_dialogs.help(win, "help", after, SEARCH=talk.text("documentation_render"))
UI_elements.roundrect(layer, win,
win.current["w"]/2-250,
win.current["h"]-140,
40,
40,
10,
do,
"question")
is_rendering = False # This will determen whether
@ -162,7 +176,8 @@ def layer(win, call):
10,
button=do,
icon="cancel",
tip=talk.text("cancel"))
tip=talk.text("cancel"),
url="render")
x = win.current["w"]/2-250 + 10

View file

@ -4,6 +4,7 @@
import os
import datetime
import re
import json
# GTK module ( Graphical interface
import gi
@ -420,6 +421,17 @@ def layer(win):
putscroll = 0
# Before we start. Let's bullet proof this. So if the scene does not exist
# we will be transferred back to story editor and not stuck.
try:
win.story["scenes"][scene]["shots"]
except:
win.url = "story_editor"
win.cur = ""
return surface
# Okay if nothing is in the scene we want to create a text block of 1 character
# into the scene. Because if it will have 0 characters it will be automatically
# deleted.
@ -452,6 +464,8 @@ def layer(win):
# while text has just 2. ['text_block', [<text>]]. So you can see me refencing
# the block[-1] a lot. To get the text parts of the block.
for num, block in enumerate(win.story["scenes"][scene]["shots"]):
@ -1279,6 +1293,13 @@ def layer(win):
pointer[1] = pointer[0]
#win.current["keys"].remove(65364)
# In order for history to work properly I don't want to save history on
# each button press. But rather ones every time the scene is edited. So
if "scene_edited_already" not in win.current:
win.current["scene_edited_already"] = False
# I'm going to implement some basic clipboard. It will need love in future.
# I just need for now something that will "work" so to speak. See where I
# use the clipboard variable later to learn how it all works.
@ -1304,6 +1325,12 @@ def layer(win):
if win.current["key_letter"] and not win.textactive:
# Here I'm going to implement history
if not win.current["scene_edited_already"]:
win.current["scene_edited_already"] = True
history.record(win, "/rnd"+win.cur, "[Edited]")
#print("------{ BEFORE }--------")
#print(win.story["scenes"][scene]["shots"])
#print("------{ }------")
@ -1315,14 +1342,17 @@ def layer(win):
for u, stuff in enumerate(selecwords):
part, p, num, n = stuff
block = win.story["scenes"][scene]["shots"][num]
try:
block = win.story["scenes"][scene]["shots"][num]
except:
break
try:
ORD = ord(win.current["key_letter"])
except:
ORD = 0
#print(ORD, "ORD") # Output the value of the button.
print(ORD, "ORD") # Output the value of the button.
# Now some of the ORDs I found to be weird characters. Like on
# CTRL - C or CTRL - V. So I want to filter them out. So there
@ -1341,6 +1371,24 @@ def layer(win):
#print(MAX, "MAX")
#print()
# Here I want to record undo history for the script writer. I am going
# to ignore the letters. And only forcus on other simbols such as
# spacebars and various commands.
nonhistory = "1234567890qwertyuiopasdfghjklzxcvbnnmQWERTYUIOPASDFGHJKLZXCVBNM"
if win.current["key_letter"] not in nonhistory and ORD not in [26, 25]:
story.undo_record(win)
# Now let's retrive out data.
# UNDO
if ORD == 26:
story.undo(win)
# REDO
if ORD == 25:
story.redo(win)
if u == 0:
@ -1533,7 +1581,7 @@ def layer(win):
# Making stuff happen
elif u < len(selecwords)-1:
if ORD not in [12, 19, 3, 22]: # 3 = Crtl - C, 22 = Ctrl - V
if ORD not in [12, 19, 3, 22, 26, 25]: # 3 = Crtl - C, 22 = Ctrl - V
part[-1] = ""
if ORD == 19:
@ -1547,7 +1595,7 @@ def layer(win):
MIN = min(len(part[-1])-1, max(0, min(pointer[1], pointer[0])-p))
MAX = max(0, min(len(part[-1])-1, max(pointer[1], pointer[0])-p))
if ORD not in [12, 19, 3, 22]:
if ORD not in [12, 19, 3, 22, 26, 25]:
part[-1] = part[-1][MAX:]
if ORD == 19:
@ -1685,6 +1733,22 @@ def layer(win):
40,
10)
# Documentation entry
def do():
def after(win, var):
pass
studio_dialogs.help(win, "help", after, SEARCH=talk.text("documentation_script_writer"))
UI_elements.roundrect(layer, win,
win.current["w"]/4,
win.current["h"]-50,
40,
40,
10,
do,
"question")
# Here in the bottom I want to introduce little things to go between scenes
# back and forth.
@ -1748,6 +1812,7 @@ def layer(win):
pass
win.story["selected"] = []
win.scroll["script"] = 0
win.current["scene_edited_already"] = False
UI_elements.roundrect(layer, win,
win.current["w"]-40-win.current["w"]/4,
@ -2396,6 +2461,85 @@ def layer(win):
numofis = len(fouricons[win.current[shotis+"_active_folder"]])
# Here as you can see I made the the rectangle a 100 pixels high
# this is for 1 simple reason. To be able to draw analytics.
# But.
# There are like one million problems with it. Let's go over them.
# * There are multiple blend files each one with analytics data.
# * Reading JSON files and drawing graphs is not a trivial thing
# requiring cashing of the image. Such as in Analytics window.
# Bull will see.
# * During rendeing the DATA changes. And I want to see the up
# to date data at all times.
# Let's try to implement it the simple way. Reading everything
# live. I've done that in Blender-Organizer. But the data was
# way simpler.
alldata = {}
largest = 1
# WAIT extra might not exists.
try:
for filename in os.listdir(win.project+"/rnd"+win.cur+"/extra"):
if filename.endswith(".blend.json"):
# I want to now open JSON FILE for each one and parse
# it. Actually when I think about it. It's not supposed
# to be that bad. But this computer has an SSD. LOL.
data = {}
try:
with open(win.project+"/rnd"+win.cur+"/extra/"+filename) as json_file:
data = json.load(json_file)
# Since I'm inside try I can do some wild stuff here.
data = data["analytics"][win.current[shotis+"_active_folder"]]
alldata[filename.replace(".json", "")] = data
for i in data:
if data[i] > largest:
largest = data[i]
except:
pass
except:
pass
# So let's now read through all the data that we have
for n, blend in enumerate(alldata):
data = alldata[blend]
# I want each new file to have it's own color. So
UI_color.set(layer, win, rcolors[n % len(rcolors)])
layer.move_to(
x+5,
y+win.scroll["script_shots"]+current_Y_shots+100
)
# Now let's draw everything.
for numb, fil in enumerate(fouricons[win.current[shotis+"_active_folder"]]):
if str(numb+1) in data:
layer.line_to(
x+5+(width-10)/numofis*(numb+0.5),
(y+win.scroll["script_shots"]+current_Y_shots+100)-(100/largest*data[str(numb+1)])
)
layer.stroke()
# Funny that it didn't really stuck too much like this. Please
# let me know if on your machine this way of doing it is hell
# a slow.
for numb, fil in enumerate(fouricons[win.current[shotis+"_active_folder"]]):
@ -2413,6 +2557,21 @@ def layer(win):
if int(win.current["mx"]) in range(int(x+5+(width-10)/numofis*numb),int(x+5+(width-10)/numofis*numb+(width-10)/numofis))\
and int(win.current["my"]) in range(int(y+win.scroll["script_shots"]+current_Y_shots), int(y+win.scroll["script_shots"]+current_Y_shots+100)):
# Before we are doing the logic. I want to give user a
# tooltip about render analytics. So let's do this.
tip = ""
for n, blend in enumerate(alldata):
data = alldata[blend]
if str(numb+1) in data:
tip = tip + blend+" "+UI_math.timestring(data[str(numb+1)]/1000000)+"\n"
if tip:
tip = tip[:-1] # Removing the last \n
UI_elements.tooltip(win, tip)
# Now let's do the fill and other stuff
fill = True
if win.current["LMB"]:

View file

@ -30,6 +30,7 @@ from studio import story
from studio import analytics
from studio import history
from studio import studio_dialogs
from studio import history
def link_from_script(win):
@ -129,6 +130,10 @@ def layer(win, call):
if not win.current["linking_asset_data"]["fraction"]:
def do():
# Before we start let's record the history for it.
history.record(win, win.project+win.current["linking_asset_data"]["linking_to"], "[Linked]")
if not win.current["linking_asset_data"]["process"]:
# So this is an apply button. I know it's a little not in the right
@ -174,6 +179,22 @@ def layer(win, call):
tip=talk.text("checked"),
url="asset_link")
# Documentation entry
def do():
def after(win, var):
pass
studio_dialogs.help(win, "help", after, SEARCH=talk.text("documentation_link_assets"))
UI_elements.roundrect(layer, win,
300,
50,
40,
40,
10,
do,
"question")
# CANCEl
if not win.current["linking_asset_data"]["process"]:
def do():

View file

@ -15,6 +15,7 @@ import cairo
# Own modules
from settings import settings
from settings import oscalls
from settings import talk
from project_manager import pm_project
@ -531,7 +532,12 @@ def layer(win):
# Edit Video
def do():
print("Edit Video")
def after(win, var):
if var:
print(var)
oscalls.file_open(win, var)
studio_dialogs.vse(win, "VSEs", after)
UI_elements.roundrect(layer, win,
5,
@ -677,7 +683,7 @@ def layer(win):
# Sounds / Music
def do():
print("Sounds / Music")
os.system("xdg-open "+win.project+"/mus")
UI_elements.roundrect(layer, win,
win.current["w"]-45,
@ -690,6 +696,23 @@ def layer(win):
talk.text("mus"),
url="story_editor")
# Help
def do():
def after(win, var):
pass
studio_dialogs.help(win, "help", after, SEARCH=talk.text("documentation_story_editor"))
UI_elements.roundrect(layer, win,
win.current["w"]-45,
win.current["h"]-125,
40,
40,
10,
do,
"question",
url="story_editor")
# Folder
def do():
os.system("xdg-open "+win.project)
@ -1090,9 +1113,21 @@ def layer(win):
if savenow:
# The undo history is quite a new adition so the limit setting might
# not exists. So let's make it if so.
if "Undo_Limit" not in win.settings:
win.settings["Undo_Limit"] = 32
settings.write("Undo_Limit", 32)
# Now let's run the history record.
story.undo_record(win)
story.save(win.project, win.story)
analytics.save(win.project, win.analytics)
# Need to reload the story to reload the fractions of the scenes.
win.story = story.load(win.project)
########### TIMES RECORDING FOR PERFONMANCE MEASURING #############
fif = datetime.datetime.now()
@ -1110,7 +1145,15 @@ def layer(win):
UI_elements.animate("cameraX", win, win.story["camera"][0],nex, force=True)
UI_elements.animate("cameraY", win, win.story["camera"][1],ney, force=True)
# Undo
if 65507 in win.current["keys"] and 122 in win.current["keys"] and not win.textactive:
story.undo(win)
win.current["keys"] = []
# Redo
if 65507 in win.current["keys"] and 121 in win.current["keys"] and not win.textactive:
story.redo(win)
win.current["keys"] = []
# Grab
if 103 in win.current["keys"] and win.story["selected"] and not win.textactive:

356
studio/studio_vseLayer.py Normal file
View file

@ -0,0 +1,356 @@
# THIS FILE IS A PART OF VCStudio
# PYTHON 3
import os
import datetime
import json
from subprocess import *
# GTK module ( Graphical interface
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import Gdk
import cairo
# Own modules
from settings import settings
from settings import talk
from settings import fileformats
from settings import oscalls
from project_manager import pm_project
#UI modules
from UI import UI_elements
from UI import UI_color
from UI import UI_math
# Studio
from studio import studio_dialogs
from studio import analytics
from studio import story
def layer(win, call):
##########################################################################
# This file will select a VSE blend file. And create ones when needed.
# I'm making it as a dialog (see: studio/studio_dialogs.py ) because I
# want to be able to use this UI in various places in VCStudio.
# The launching of the file will be done by a host Layer.
##########################################################################
# Making the layer
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, win.current['w'],
win.current['h'])
layer = cairo.Context(surface)
#text setting
layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
UI_color.set(layer, win, "dark_overdrop")
layer.rectangle(
0,
0,
win.current["w"],
win.current["h"],
)
layer.fill()
UI_color.set(layer, win, "node_background")
UI_elements.roundrect(layer, win,
win.current["w"]/2-250,
100,
500,
win.current["h"]-200,
10)
# Exit button
def do():
win.current["calls"][call]["var"] = False
UI_elements.roundrect(layer, win,
win.current["w"]/2+210,
win.current["h"]-140,
40,
40,
10,
button=do,
icon="cancel",
tip=talk.text("cancel"))
x = win.current["w"]/2-250 + 10
y = 170
width = 500 - 20
height = win.current["h"]-270
# Search
UI_elements.image(layer, win, "settings/themes/"\
+win.settings["Theme"]+"/icons/search.png",
x+width/4,
y-50,
40,
40)
UI_elements.text(layer, win, "in_vses",
x+width/4+50,
y-50,
width/2-50,
40)
# Clip
UI_elements.roundrect(layer, win,
x,
y,
width,
height-60,
10,
fill=False)
layer.clip()
clip = [
x,
y,
width,
height-60]
# Little testing thing. Make it True to see where it's clipping.
if False:
# Background
UI_color.set(layer, win, "dark_overdrop")
layer.rectangle(x,y,width, height)
layer.fill()
# Setting up the scroll
if "vse" not in win.scroll:
win.scroll["vse"] = 0
current_Y = 0
# There is not going to be any launch buttons. Basically you click on a
# file in the list and it's launched. ( Or out_putted into parent function )
# And since the contents of those blend files are not model based. Let's not
# draw tiles of squares, but rather a simple list would be fine.
# I think something like
#####################################################
# #
# [____SEARCH____] #
# #
# [] NAME_OF_FILE.BLEND #
# [] NAME_OF_FILE.BLEND #
# [] NAME_OF_FILE.BLEND #
# [] NAME_OF_FILE.BLEND #
# [] NAME_OF_FILE.BLEND #
# [] NAME_OF_FILE.BLEND #
# [] NAME_OF_FILE.BLEND #
# [] NAME_OF_FILE.BLEND #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# X #
#####################################################
# Of course It's going to look better in the end. I'm not great with ASCII
# art. And of course to add a blend file you will look for it in a search
# and it will give you to make one or copy one. Like usual.
blends = []
for blend in os.listdir(win.project+"/rnd"):
if blend.endswith(".blend"):
blends.append(blend)
# I just did this because there could not be a file to begin with. And I
# want to make one if such doesn't exists.
if not blends:
newname = "sequence.blend"
oscalls.copy_file(
win,
os.getcwd()+"/new_file/seq.blend",
"/rnd/",
newname)
# Okay since now we have our file let's list them. Or it. Yeah.
newcreate = win.text["in_vses"]["text"].replace("/","_").replace(" ", "_")\
.replace('"',"_").replace("(","_").replace(")","_").replace("'","_")\
.replace("[","_").replace("]","_").replace("{","_").replace("}","_")
for blend in blends:
# Search
if newcreate and newcreate.lower() not in blend.lower():
continue
# LAUNCH BUTTON
def do():
win.current["calls"][call]["var"] = "/rnd/"+blend
UI_elements.roundrect(layer, win,
x,
y+current_Y + win.scroll["vse"],
width,
40,
10,
button=do)
# ICON
UI_elements.image(layer, win, "settings/themes/"\
+win.settings["Theme"]+"/icons/vse.png",
x+5,
y+current_Y + win.scroll["vse"],
40,
40)
# NAME
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to(x+50,
y+current_Y + win.scroll["vse"]+30)
layer.show_text(blend)
current_Y = current_Y + 50
# Now let's make the 2 adding buttons and to hell with it.
if newcreate and newcreate not in blends:
if not newcreate.endswith(".blend"):
newcreate = newcreate + ".blend"
# NEW FILE
def do():
oscalls.copy_file(
win,
os.getcwd()+"/new_file/seq.blend",
"/rnd/",
newcreate)
win.text["in_vses"]["text"] = ""
UI_elements.roundrect(layer, win,
x,
y+current_Y + win.scroll["vse"],
width,
40,
10,
button=do)
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
x,
y+current_Y + win.scroll["vse"],
width,
40,
10,
fill=False)
layer.stroke()
# ICON
UI_elements.image(layer, win, "settings/themes/"\
+win.settings["Theme"]+"/icons/new_file.png",
x+5,
y+current_Y + win.scroll["vse"],
40,
40)
# NAME
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to(x+50,
y+current_Y + win.scroll["vse"]+30)
layer.show_text(newcreate)
current_Y = current_Y + 50
# COPY FILE
def do():
def after(win, var):
if var and var.endswith(".blend"):
oscalls.copy_file(
win,
var,
"/rnd/",
newcreate)
win.text["in_vses"]["text"] = ""
studio_dialogs.file_select(win, "seq_blends", after, force=True,
IMAGE=False, BLEND=True, VIDEO=False, FILE=False, CHR=False, VEH=False,
LOC=False, OBJ=False, RND=True, FOLDER=False)
UI_elements.roundrect(layer, win,
x,
y+current_Y + win.scroll["vse"],
width,
40,
10,
button=do)
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
x,
y+current_Y + win.scroll["vse"],
width,
40,
10,
fill=False)
layer.stroke()
# ICON
UI_elements.image(layer, win, "settings/themes/"\
+win.settings["Theme"]+"/icons/copy_file.png",
x+5,
y+current_Y + win.scroll["vse"],
40,
40)
# NAME
UI_color.set(layer, win, "text_normal")
layer.set_font_size(20)
layer.move_to(x+50,
y+current_Y + win.scroll["vse"]+30)
layer.show_text(newcreate)
current_Y = current_Y + 50
###########################
UI_elements.scroll_area(layer, win, "vse",
x,
y,
width,
height-60,
current_Y,
bar=True,
mmb=True,
url="vse"
)
return surface