[pitivi] proxy: Add Scaled Proxies



commit 3a2725ba7e3f8c3eb3b535aeca75ed9b59e46a71
Author: yatinmaan <yatinmaan1 gmail com>
Date:   Wed May 23 10:38:16 2018 +0530

    proxy: Add Scaled Proxies
    
    Fixes #743
    Fixes #2344

 data/pixmaps/asset-scaled.svg            | 107 +++++++++++
 data/pixmaps/question-round-symbolic.svg |  89 +++++++++
 data/pixmaps/warning-symbolic.svg        |  89 +++++++++
 data/ui/projectsettings.ui               | 181 +++++++++++++++++-
 pitivi/dialogs/prefs.py                  | 189 +++++++++++++------
 pitivi/medialibrary.py                   | 254 +++++++++++++++++++------
 pitivi/project.py                        | 152 +++++++++++++--
 pitivi/render.py                         |  93 ++++++----
 pitivi/timeline/previewers.py            |   2 +-
 pitivi/timeline/timeline.py              |  39 ++--
 pitivi/utils/proxy.py                    | 310 ++++++++++++++++++++++++++-----
 pitivi/utils/timeline.py                 |   4 +-
 pitivi/utils/ui.py                       |   3 +-
 pitivi/utils/widgets.py                  |   4 +-
 tests/test_media_library.py              | 207 ++++++++++++++++++++-
 tests/test_prefs.py                      |  14 ++
 tests/test_project.py                    |  34 ++++
 tests/test_proxy.py                      |  98 ++++++++++
 tests/test_render.py                     |  86 +++++++++
 19 files changed, 1697 insertions(+), 258 deletions(-)
---
diff --git a/data/pixmaps/asset-scaled.svg b/data/pixmaps/asset-scaled.svg
new file mode 100644
index 00000000..547bda05
--- /dev/null
+++ b/data/pixmaps/asset-scaled.svg
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="26.955959mm"
+   height="25.062054mm"
+   viewBox="0 0 95.513242 88.802552"
+   id="svg4817"
+   version="1.1"
+   inkscape:version="0.92.3 (2405546, 2018-03-11)"
+   sodipodi:docname="bolt-gradient-2.svg">
+  <defs
+     id="defs4819">
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4173"
+       id="radialGradient4181"
+       cx="33.283539"
+       cy="1002.8445"
+       fx="33.283539"
+       fy="1002.8445"
+       r="80.256622"
+       gradientTransform="matrix(1.1432904,-1.9668506e-8,1.8574145e-8,1.0796774,70.127178,-163.84935)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient4173">
+      <stop
+         style="stop-color:#000000;stop-opacity:0.502"
+         offset="0"
+         id="stop4175" />
+      <stop
+         style="stop-color:#000000;stop-opacity:0"
+         offset="1"
+         id="stop4177" />
+    </linearGradient>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="4"
+     inkscape:cx="50.999465"
+     inkscape:cy="63.919525"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1920"
+     inkscape:window-height="1018"
+     inkscape:window-x="-8"
+     inkscape:window-y="-8"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata4822">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Calque 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-107.92332,-830.2478)">
+    <rect
+       
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#radialGradient4181);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.54330707;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+       id="rect4171"
+       width="95.513245"
+       height="88.802551"
+       x="107.92332"
+       y="830.2478" />
+    <path
+       id="path32"
+       d="m 127.49428,898.70065 h -3.37127 l 1.01131,-3.79238 c 0.11861,-0.44487 -0.21717,-0.88178 
-0.67746,-0.88178 h -3.97303 c -0.35141,0 -0.64855,0.26013 -0.69496,0.60845 l -0.93471,7.01125 c 
-0.056,0.42034 0.27135,0.79378 0.69495,0.79378 h 3.46767 l -1.34554,5.68104 c -0.10506,0.44355 
0.23409,0.86279 0.68202,0.86279 0.24392,0 0.47839,-0.12778 0.60699,-0.34992 l 5.14079,-8.88082 c 
0.27005,-0.46645 -0.0669,-1.05241 -0.60676,-1.05241 z"
+       inkscape:connector-curvature="0"
+       style="fill:currentColor;stroke-width:0.02921349" />
+    <path
+       id="path32-0"
+       d="m 126.99443,898.19899 h -3.37127 l 1.01131,-3.79238 c 0.11861,-0.44487 -0.21717,-0.88178 
-0.67746,-0.88178 h -3.97303 c -0.35141,0 -0.64855,0.26013 -0.69496,0.60845 l -0.93471,7.01125 c 
-0.056,0.42034 0.27135,0.79378 0.69495,0.79378 h 3.46767 l -1.34554,5.68104 c -0.10506,0.44355 
0.23409,0.86279 0.68202,0.86279 0.24392,0 0.47839,-0.12778 0.60699,-0.34992 l 5.14079,-8.88082 c 
0.27005,-0.46645 -0.0669,-1.05241 -0.60676,-1.05241 z"
+       inkscape:connector-curvature="0"
+       style="fill:#ffcc00;stroke-width:0.02921349" />
+  </g>
+</svg>
+<!--Derived work based on
+Font Awesome Free 5.1.0 by @fontawesome - https://fontawesome.com
+License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
+-->
diff --git a/data/pixmaps/question-round-symbolic.svg b/data/pixmaps/question-round-symbolic.svg
new file mode 100644
index 00000000..b64de43d
--- /dev/null
+++ b/data/pixmaps/question-round-symbolic.svg
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:osb="http://www.openswatchbook.org/uri/2009/osb";
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   width="16"
+   viewBox="0 0 16 16"
+   version="1.1"
+   id="svg7384"
+   height="16">
+  <metadata
+     id="metadata90">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title>Gnome Symbolic Icon Theme</dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <title
+     id="title9167">Gnome Symbolic Icon Theme</title>
+  <defs
+     id="defs7386">
+    <linearGradient
+       osb:paint="solid"
+       id="linearGradient7212">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop7214" />
+    </linearGradient>
+  </defs>
+  <g
+     transform="translate(-19.9999,139.98872)"
+     style="display:inline"
+     id="layer1" />
+  <g
+     transform="translate(-261.0001,506.98872)"
+     style="display:inline"
+     id="layer9" />
+  <g
+     transform="translate(-261.0001,506.98872)"
+     style="display:inline"
+     id="g7628" />
+  <g
+     transform="translate(-19.9999,-60.01128)"
+     id="layer13" />
+  <g
+     transform="translate(-19.9999,139.98872)"
+     style="display:inline"
+     id="layer11" />
+  <g
+     transform="translate(-19.9999,-60.01128)"
+     id="layer14" />
+  <g
+     transform="translate(-19.9999,139.98872)"
+     style="display:inline"
+     id="g6387">
+    <path
+       d="m 28,-139 a 7,7 0 0 0 -7,7 7,7 0 0 0 7,7 7,7 0 0 0 7,-7 7,7 0 0 0 -7,-7 z m -0.1875,2.01172 c 
1.64243,-0.092 3.0955,1.17008 3.1875,2.8125 -10e-5,1.40136 -0.37771,1.92177 -1.59375,2.84375 -0.19093,0.14364 
-0.3256,0.2506 -0.375,0.3125 -0.0494,0.0621 -0.03125,0.0333 -0.03125,0.0312 0.007,0.52831 -0.47163,1 -1,1 
-0.52837,0 -1.007,-0.47169 -1,-1 0,-0.50239 0.22424,-0.94342 0.46875,-1.25 0.24451,-0.30663 0.4913,-0.51638 
0.71875,-0.6875 0.20405,-0.16056 0.46083,-0.38454 0.6875,-0.65625 0.0935,-0.1121 0.129,-0.30766 0.125,-0.4375 
v -0.0312 c -0.0316,-0.56324 -0.49926,-0.9691 -1.0625,-0.9375 -0.56324,0.0316 -0.9691,0.43676 -0.9375,1 h -2 
c -0.092,-1.64243 1.17007,-2.9079 2.8125,-3 z m 0.1875,8 c 0.55228,0 1,0.44772 1,1 0,0.55228 -0.44772,1 -1,1 
-0.55228,0 -1,-0.44772 -1,-1 0,-0.55228 0.44772,-1 1,-1 z"
+       id="path15586"
+       
style="opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
 />
+  </g>
+  <g
+     transform="translate(-19.9999,-60.01128)"
+     id="layer18" />
+  <g
+     transform="translate(-19.9999,-60.01128)"
+     id="layer17" />
+  <g
+     transform="translate(-19.9999,-60.01128)"
+     id="layer16" />
+  <g
+     transform="translate(-19.9999,139.98872)"
+     style="display:inline"
+     id="layer10" />
+  <g
+     transform="translate(-19.9999,-60.01128)"
+     id="layer15" />
+  <g
+     transform="translate(-19.9999,139.98872)"
+     id="layer12" />
+</svg>
diff --git a/data/pixmaps/warning-symbolic.svg b/data/pixmaps/warning-symbolic.svg
new file mode 100644
index 00000000..8caf2a36
--- /dev/null
+++ b/data/pixmaps/warning-symbolic.svg
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:osb="http://www.openswatchbook.org/uri/2009/osb";
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   width="16"
+   viewBox="0 0 16 16"
+   version="1.1"
+   id="svg7384"
+   height="16">
+  <metadata
+     id="metadata90">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title>Gnome Symbolic Icon Theme</dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <title
+     id="title9167">Gnome Symbolic Icon Theme</title>
+  <defs
+     id="defs7386">
+    <linearGradient
+       osb:paint="solid"
+       id="linearGradient7212">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop7214" />
+    </linearGradient>
+  </defs>
+  <g
+     transform="translate(-39.9997,159.98872)"
+     style="display:inline"
+     id="layer1" />
+  <g
+     transform="translate(-280.9999,526.98872)"
+     style="display:inline"
+     id="layer9" />
+  <g
+     transform="translate(-280.9999,526.98872)"
+     style="display:inline"
+     id="g7628" />
+  <g
+     transform="translate(-39.9997,-40.01128)"
+     id="layer13" />
+  <g
+     transform="translate(-39.9997,159.98872)"
+     style="display:inline"
+     id="layer11" />
+  <g
+     transform="translate(-39.9997,-40.01128)"
+     id="layer14" />
+  <g
+     transform="translate(-39.9997,159.98872)"
+     style="display:inline"
+     id="g6387" />
+  <g
+     transform="translate(-39.9997,-40.01128)"
+     id="layer18" />
+  <g
+     transform="translate(-39.9997,-40.01128)"
+     id="layer17" />
+  <g
+     transform="translate(-39.9997,-40.01128)"
+     id="layer16" />
+  <g
+     transform="translate(-39.9997,159.98872)"
+     style="display:inline"
+     id="layer10" />
+  <g
+     transform="translate(-39.9997,-40.01128)"
+     id="layer15" />
+  <g
+     transform="translate(-39.9997,159.98872)"
+     id="layer12">
+    <path
+       d="m 47.90615,-159.89497 c -0.5255,-0.0286 -1.03823,0.28305 -1.4375,0.96875 l -6.25,11.59375 c 
-0.53347,0.96339 0.04822,2.34375 1.09375,2.34375 h 13.15625 c 0.98172,0 1.90311,-1.15939 1.21875,-2.34375 l 
-6.3125,-11.53125 c -0.39872,-0.64617 -0.94325,-1.00262 -1.46875,-1.03125 z m 0.0625,3.9375 c 0.54448,-0.0172 
1.04849,0.48677 1.03125,1.03125 v 3.9375 c 0.007,0.52831 -0.47163,1 -1,1 -0.52836,0 -1.00747,-0.47169 -1,-1 v 
-3.9375 c -0.008,-0.4666 0.3541,-0.91253 0.8125,-1 0.0511,-0.0145 0.10345,-0.0249 0.15625,-0.0312 z m 
0.03125,6.96875 c 0.552285,0 1,0.44772 1,1 0,0.55228 -0.447715,1 -1,1 -0.552285,0 -1,-0.44772 -1,-1 
0,-0.55228 0.447715,-1 1,-1 z"
+       id="path18112"
+       
style="color:#bebebe;display:inline;overflow:visible;visibility:visible;fill:#2e3436;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.78124988;marker:none;enable-background:new"
 />
+  </g>
+</svg>
diff --git a/data/ui/projectsettings.ui b/data/ui/projectsettings.ui
index 5aa8a52a..5d36665b 100644
--- a/data/ui/projectsettings.ui
+++ b/data/ui/projectsettings.ui
@@ -23,6 +23,18 @@
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
+  <object class="GtkAdjustment" id="adjustment4">
+    <property name="lower">1</property>
+    <property name="upper">9999</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment5">
+    <property name="lower">1</property>
+    <property name="upper">9999</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
   <object class="GtkDialog" id="project-settings-dialog">
     <property name="can_focus">False</property>
     <property name="border_width">5</property>
@@ -218,7 +230,7 @@
                                       <object class="GtkLabel" id="label9">
                                         <property name="visible">True</property>
                                         <property name="can_focus">False</property>
-                                        <property name="label">x</property>
+                                        <property name="label" translatable="no">×</property>
                                       </object>
                                       <packing>
                                         <property name="expand">False</property>
@@ -296,7 +308,7 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
                                     <property name="halign">start</property>
-                                    <property name="label" translatable="yes">Frame Rate:</property>
+                                    <property name="label" translatable="yes">Frame rate:</property>
                                     <attributes>
                                       <attribute name="weight" value="bold"/>
                                     </attributes>
@@ -601,7 +613,7 @@
                             <property name="halign">start</property>
                             <property name="max_length">4</property>
                             <property name="invisible_char">●</property>
-                            <property name="text">1900</property>
+                            <property name="text" translatable="no">1900</property>
                             <property name="adjustment">adjustment3</property>
                             <property name="numeric">True</property>
                             <property name="value">1900</property>
@@ -629,6 +641,169 @@
                 <property name="width">2</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkFrame" id="frame4">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label_xalign">0</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment4">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkBox" id="video_tab1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="border_width">12</property>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">12</property>
+                        <child>
+                          <object class="GtkBox" id="video_details1">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="orientation">vertical</property>
+                            <property name="spacing">12</property>
+                            <child>
+                              <object class="GtkBox" id="size_box1">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="orientation">vertical</property>
+                                <property name="spacing">6</property>
+                                <child>
+                                  <object class="GtkLabel" id="label8">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="halign">start</property>
+                                    <property name="label" translatable="yes">Scaled proxies 
resolution:</property>
+                                    <attributes>
+                                      <attribute name="weight" value="bold"/>
+                                    </attributes>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkBox" id="hbox2">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="spacing">6</property>
+                                    <child>
+                                      <object class="GtkSpinButton" id="scaled_proxy_width">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="invisible_char">●</property>
+                                        <property name="text" translatable="yes">1</property>
+                                        <property name="adjustment">adjustment4</property>
+                                        <property name="numeric">True</property>
+                                        <property name="value">1</property>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">False</property>
+                                        <property name="position">0</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkLabel" id="label13">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="label" translatable="no">×</property>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">False</property>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkSpinButton" id="scaled_proxy_height">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="invisible_char">●</property>
+                                        <property name="text" translatable="yes">1</property>
+                                        <property name="adjustment">adjustment5</property>
+                                        <property name="numeric">True</property>
+                                        <property name="value">1</property>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">False</property>
+                                        <property name="position">2</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkLabel" id="label14">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="label" translatable="yes">pixels</property>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">True</property>
+                                        <property name="position">3</property>
+                                      </packing>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">False</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkCheckButton" id="proxy_res_linked">
+                                    <property name="label" translatable="yes" comments="When checked, 
changing the width or height affects also the other so that the aspect ratio value (width / height) does not 
change.">Constrain proportions</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">False</property>
+                                    <property name="halign">start</property>
+                                    <property name="draw_indicator">True</property>
+                                    <signal name="toggled" handler="_proxy_res_linked_toggle_cb" 
swapped="no"/>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">False</property>
+                                    <property name="position">2</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Proxy</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
           </object>
           <packing>
             <property name="expand">False</property>
diff --git a/pitivi/dialogs/prefs.py b/pitivi/dialogs/prefs.py
index 220f2ec7..8b31d5a5 100644
--- a/pitivi/dialogs/prefs.py
+++ b/pitivi/dialogs/prefs.py
@@ -59,8 +59,9 @@ class PreferencesDialog(Loggable):
     prefs = {}
     section_names = {
         "timeline": _("Timeline"),
-        "_plugins": _("Plugins"),
-        "_shortcuts": _("Shortcuts")
+        "__plugins": _("Plugins"),
+        "__shortcuts": _("Shortcuts"),
+        "_proxies": _("Proxies"),
     }
 
     def __init__(self, app):
@@ -91,6 +92,7 @@ class PreferencesDialog(Loggable):
             self.add_settings_page(section_id)
         self.factory_settings.set_sensitive(self._canReset())
 
+        self.__add_proxies_section()
         self.__add_shortcuts_section()
         self.__add_plugin_manager_section()
         self.__setup_css()
@@ -152,7 +154,7 @@ class PreferencesDialog(Loggable):
         """
         if section not in cls.section_names:
             raise Exception("%s is not a valid section id" % section)
-        if section.startswith("_"):
+        if section.startswith("__"):
             raise Exception("Cannot add preferences to reserved sections")
         if section not in cls.prefs:
             cls.prefs[section] = {}
@@ -225,8 +227,11 @@ class PreferencesDialog(Loggable):
 
     def add_settings_page(self, section_id):
         """Adds a page for the preferences in the specified section."""
-        options = self.prefs[section_id]
+        prefs = self._prepare_prefs_widgets(self.prefs[section_id])
+        container = self._create_props_container(prefs)
+        self._add_page(section_id, container)
 
+    def _create_props_container(self, prefs):
         container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
         container.set_border_width(SPACING)
 
@@ -235,84 +240,151 @@ class PreferencesDialog(Loggable):
         listbox.props.margin = PADDING * 2
         listbox.get_style_context().add_class('prefs_list')
 
-        container.add(listbox)
+        container.pack_start(listbox, False, False, 0)
 
         label_size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL)
         prop_size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL)
 
-        prefs = []
-        for attrname in options:
-            label, description, widget_class, args = options[attrname]
-            widget = widget_class(**args)
-            widget.setWidgetValue(getattr(self.settings, attrname))
-            widget.connectValueChanged(self._valueChangedCb, widget, attrname)
-            widget.set_tooltip_text(description)
-            self.widgets[attrname] = widget
-
-            widget.props.margin_left = PADDING * 3
-            widget.props.margin_right = PADDING * 3
-            widget.props.margin_top = PADDING * 2
-            widget.props.margin_bottom = PADDING * 2
-
-            prop_size_group.add_widget(widget)
+        for y, (label, widget, revert_widget, extra_widget) in enumerate(prefs):
+            box = Gtk.Box()
 
             label_widget = Gtk.Label(label=label)
-            label_widget.set_tooltip_text(description)
             label_widget.set_alignment(0.0, 0.5)
-
-            label_widget.props.margin_left = PADDING * 3
             label_widget.props.margin_right = PADDING * 3
             label_widget.props.margin_top = PADDING * 2
             label_widget.props.margin_bottom = PADDING * 2
-
-            label_widget.show()
             label_size_group.add_widget(label_widget)
 
-            icon = Gtk.Image()
-            icon.set_from_icon_name(
-                "edit-clear-all-symbolic", Gtk.IconSize.MENU)
-            revert = Gtk.Button()
-            revert.add(icon)
-            revert.set_tooltip_text(_("Reset to default value"))
-            revert.set_relief(Gtk.ReliefStyle.NONE)
-            revert.set_sensitive(not self.settings.isDefault(attrname))
-            revert.connect("clicked", self.__reset_option_cb, attrname)
-            revert.show_all()
+            widget.props.margin_right = PADDING * 3
+            widget.props.margin_top = PADDING * 2
+            widget.props.margin_bottom = PADDING * 2
+            prop_size_group.add_widget(widget)
 
-            self.resets[attrname] = revert
-            row_widgets = (label_widget, widget, revert)
-            # Construct the prefs list so that it can be sorted.
-            # Make sure the L{ToggleWidget}s appear at the end.
-            prefs.append((label_widget is None, label, row_widgets))
-
-        # Sort widgets: I think we only want to sort by the non-localized
-        # names, so options appear in the same place across locales ...
-        # but then I may be wrong
-        for y, (_1, _2, row_widgets) in enumerate(sorted(prefs)):
-            label, widget, revert = row_widgets
-            box = Gtk.Box()
+            box.pack_start(label_widget, True, True, 0)
+            box.pack_start(widget, False, False, 0)
+            if revert_widget:
+                box.pack_start(revert_widget, False, False, 0)
 
-            if label:
-                box.pack_start(label, True, True, 0)
+            if extra_widget:
+                box1 = box
+                box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+                box.pack_start(box1, False, False, 0)
+                box.pack_start(extra_widget, False, False, SPACING)
 
-            box.pack_start(widget, False, False, 0)
-            box.pack_start(revert, False, False, 0)
+            box.props.margin_left = PADDING * 3
 
             row = Gtk.ListBoxRow()
             row.set_activatable(False)
             row.add(box)
-            listbox.add(row)
-
             if y == 0:
                 row.get_style_context().add_class('first')
+            listbox.add(row)
 
         container.show_all()
-        self._add_page(section_id, container)
+        return container
+
+    def _create_revert_button(self):
+        revert = Gtk.Button.new_from_icon_name("edit-clear-all-symbolic", Gtk.IconSize.MENU)
+        revert.set_tooltip_text(_("Reset to default value"))
+        revert.set_relief(Gtk.ReliefStyle.NONE)
+        revert.props.valign = Gtk.Align.CENTER
+        return revert
+
+    def _prepare_prefs_widgets(self, options):
+        prefs = []
+        for attrname in options:
+            label, description, widget_class, args = options[attrname]
+            widget = widget_class(**args)
+            widget.setWidgetValue(getattr(self.settings, attrname))
+            widget.connectValueChanged(self._valueChangedCb, widget, attrname)
+            widget.set_tooltip_text(description)
+            self.widgets[attrname] = widget
+
+            revert = self._create_revert_button()
+            revert.set_sensitive(not self.settings.isDefault(attrname))
+            revert.connect("clicked", self.__reset_option_cb, attrname)
+            self.resets[attrname] = revert
+
+            # Construct the prefs list so that it can be sorted.
+            prefs.append((label, widget, revert, None))
+
+        # TODO: We need to know exactly where each preference appears,
+        #   currently they are sorted by the translated label.
+        return sorted(prefs)
 
     def __add_plugin_manager_section(self):
         page = PluginPreferencesPage(self.app, self)
         page.show_all()
-        self._add_page("_plugins", page)
+        self._add_page("__plugins", page)
+
+    def __add_proxies_section(self):
+        """Adds a section for proxy settings."""
+        prefs = self._prepare_prefs_widgets(self.prefs["_proxies"])
+
+        self.proxy_width_widget = widgets.NumericWidget(lower=1, width_chars=4)
+        self.proxy_width_widget.setWidgetValue(self.app.settings.default_scaled_proxy_width)
+        self.widgets["default_scaled_proxy_width"] = self.proxy_width_widget
+        self.proxy_height_widget = widgets.NumericWidget(lower=1, width_chars=4)
+        self.proxy_height_widget.setWidgetValue(self.app.settings.default_scaled_proxy_height)
+        self.widgets["default_scaled_proxy_height"] = self.proxy_height_widget
+        size_box = Gtk.Box(spacing=SPACING)
+        size_box.pack_start(self.proxy_width_widget, False, False, 0)
+        size_box.pack_start(Gtk.Label("×"), False, False, 0)
+        size_box.pack_start(self.proxy_height_widget, False, False, 0)
+        size_box.set_tooltip_text(_("This resolution will be used as the"
+            " default target resolution for new projects and projects missing"
+            " scaled proxy meta-data."))
+        self.scaled_proxy_size_revert_button = self._create_revert_button()
+
+        self.proxy_infobar = Gtk.InfoBar.new()
+        fix_infobar(self.proxy_infobar)
+        self.proxy_infobar.set_message_type(Gtk.MessageType.WARNING)
+        self.proxy_infobar.add_button(_("Project Settings"), Gtk.ResponseType.OK)
+        self.scaled_proxies_infobar_label = Gtk.Label.new()
+        self.proxy_infobar.get_content_area().add(self.scaled_proxies_infobar_label)
+        self.proxy_infobar.show_all()
+
+        prefs.append((_("Initial proxy size for new projects"), size_box, 
self.scaled_proxy_size_revert_button, None))
+
+        container = self._create_props_container(prefs)
+
+        container.pack_start(self.proxy_infobar, False, False, 0)
+
+        self._add_page("_proxies", container)
+
+        self.__update_scaled_proxies_infobar()
+        self.__update_proxy_size_revert_button()
+
+        self.proxy_width_widget.connectValueChanged(self.__scaled_proxy_size_change_cb)
+        self.proxy_height_widget.connectValueChanged(self.__scaled_proxy_size_change_cb)
+        self.scaled_proxy_size_revert_button.connect("clicked", self.__reset_option_cb, 
"default_scaled_proxy_width", "default_scaled_proxy_height")
+        self.proxy_infobar.connect("response", self.__proxy_infobar_cb)
+
+    def __scaled_proxy_size_change_cb(self, unused_widget):
+        self.app.settings.default_scaled_proxy_width = self.proxy_width_widget.getWidgetValue()
+        self.app.settings.default_scaled_proxy_height = self.proxy_height_widget.getWidgetValue()
+        self.__update_scaled_proxies_infobar()
+        self.__update_proxy_size_revert_button()
+
+    def __update_proxy_size_revert_button(self):
+        default = all([self.settings.isDefault(setting)
+                       for setting in ("default_scaled_proxy_width", "default_scaled_proxy_height")])
+        self.scaled_proxy_size_revert_button.set_sensitive(not default)
+
+    def __update_scaled_proxies_infobar(self):
+        project = self.app.project_manager.current_project
+        different = project and \
+            (project.scaled_proxy_width != self.app.settings.default_scaled_proxy_width or
+                project.scaled_proxy_height != self.app.settings.default_scaled_proxy_height)
+        self.proxy_infobar.set_visible(different)
+        if different:
+            self.scaled_proxies_infobar_label.set_text(
+                _("Proxy resolution for the current project is %d×%d px") %
+                (project.scaled_proxy_width, project.scaled_proxy_height))
+
+    def __proxy_infobar_cb(self, unused_infobar, unused_response_id):
+        self.app.gui.editor.showProjectSettingsDialog()
+        self.__update_scaled_proxies_infobar()
 
     def __add_shortcuts_section(self):
         """Adds a section with keyboard shortcuts."""
@@ -349,7 +421,7 @@ class PreferencesDialog(Loggable):
         outside_box.add(scrolled_window)
         outside_box.show_all()
 
-        self._add_page("_shortcuts", outside_box)
+        self._add_page("__shortcuts", outside_box)
 
     def __row_activated_cb(self, list_box, row):
         index = row.get_index()
@@ -463,8 +535,9 @@ class PreferencesDialog(Loggable):
         self.revert_button.set_sensitive(False)
         self.factory_settings.set_sensitive(self._canReset())
 
-    def __reset_option_cb(self, button, attrname):
-        self.__reset_option(button, attrname)
+    def __reset_option_cb(self, button, *attrnames):
+        for attrname in attrnames:
+            self.__reset_option(button, attrname)
 
     def __reset_option(self, button, attrname):
         """Resets a particular setting to the factory default."""
diff --git a/pitivi/medialibrary.py b/pitivi/medialibrary.py
index 5a1082d7..2024e6c8 100644
--- a/pitivi/medialibrary.py
+++ b/pitivi/medialibrary.py
@@ -50,6 +50,7 @@ from pitivi.utils.misc import disconnectAllByFunc
 from pitivi.utils.misc import path_from_uri
 from pitivi.utils.misc import PathWalker
 from pitivi.utils.misc import quote_uri
+from pitivi.utils.misc import show_user_manual
 from pitivi.utils.proxy import get_proxy_target
 from pitivi.utils.proxy import ProxyingStrategy
 from pitivi.utils.proxy import ProxyManager
@@ -115,59 +116,121 @@ for category, mime_types in SUPPORTED_FILE_FORMATS.items():
         SUPPORTED_MIMETYPES.append(category + "/" + mime)
 
 
-class FileChooserExtraWidget(Gtk.Grid, Loggable):
+class FileChooserExtraWidget(Gtk.Box, Loggable):
+
     def __init__(self, app):
         Loggable.__init__(self)
-        Gtk.Grid.__init__(self)
+        Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL)
         self.app = app
 
-        self.set_row_spacing(SPACING)
-        self.set_column_spacing(SPACING)
-
-        self.__close_after = Gtk.CheckButton(label=_("Close after importing files"))
-        self.__close_after.set_active(self.app.settings.closeImportDialog)
-        self.attach(self.__close_after, 0, 0, 1, 2)
-
-        self.__automatic_proxies = Gtk.RadioButton.new_with_label(
-            None, _("Create proxies when the media format is not supported officially"))
-        self.__automatic_proxies.set_tooltip_markup(
-            _("Let Pitivi decide when to"
-              " create proxy files and when not. The decision will be made"
-              " depending on the file format, and how well it is supported."
-              " For example H.264, FLAC files contained in QuickTime will"
-              " not be proxied, but AAC, H.264 contained in MPEG-TS will.\n\n"
-              "<i>This is the only option officially supported by the"
-              " Pitivi developers and thus is the safest."
-              "</i>"))
-
-        self.__force_proxies = Gtk.RadioButton.new_with_label_from_widget(
-            self.__automatic_proxies, _("Create proxies for all files"))
-        self.__force_proxies.set_tooltip_markup(
-            _("Use proxies for every imported file"
-              " whatever its current media format is."))
-        self.__no_proxies = Gtk.RadioButton.new_with_label_from_widget(
-            self.__automatic_proxies, _("Do not use proxy files"))
-
-        if self.app.settings.proxyingStrategy == ProxyingStrategy.ALL:
-            self.__force_proxies.set_active(True)
-        elif self.app.settings.proxyingStrategy == ProxyingStrategy.NOTHING:
-            self.__no_proxies.set_active(True)
-        else:
-            self.__automatic_proxies.set_active(True)
+        self.__keep_open_check = Gtk.CheckButton(label=_("Keep dialog open"))
+        self.__keep_open_check.props.valign = Gtk.Align.START
+        self.__keep_open_check.set_tooltip_text(_("When importing files keep the dialog open"))
+        self.__keep_open_check.set_active(not self.app.settings.closeImportDialog)
+        self.pack_start(self.__keep_open_check, expand=False, fill=False, padding=0)
+
+        self.hq_proxy_check = Gtk.CheckButton.new()
+        # Translators: Create optimized media for unsupported files.
+        self.hq_proxy_check.set_label(_("Optimize:"))
+        self.hq_proxy_check.connect("toggled", self._hq_proxy_check_cb)
+
+        self.hq_combo = Gtk.ComboBoxText.new()
+        self.hq_combo.insert_text(0, _("Unsupported assets"))
+        self.hq_combo.insert_text(1, _("All"))
+        self.hq_combo.props.active = 0
+        self.hq_combo.set_sensitive(False)
+
+        self.help_button = Gtk.Button()
+        self.__update_help_button()
+        self.help_button.props.relief = Gtk.ReliefStyle.NONE
+        self.help_button.connect("clicked", self._help_button_clicked_cb)
+
+        self.scaled_proxy_check = Gtk.CheckButton.new()
+        self.__update_scaled_proxy_check()
+        self.scaled_proxy_check.connect("toggled", self._scaled_proxy_check_cb)
+
+        self.project_settings_label = Gtk.Label()
+        self.project_settings_label.set_markup("<a href='#'>%s</a>" % _("Project Settings"))
+        self.project_settings_label.connect("activate-link", self._target_res_cb)
+
+        proxy_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+
+        hq_proxy_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
+        hq_proxy_row.pack_start(self.hq_proxy_check, expand=False, fill=False, padding=0)
+        hq_proxy_row.pack_start(self.hq_combo, expand=False, fill=False, padding=PADDING)
+        hq_proxy_row.pack_start(self.help_button, expand=False, fill=False, padding=SPACING)
+        proxy_box.pack_start(hq_proxy_row, expand=False, fill=False, padding=0)
+
+        row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
+        row.pack_start(self.scaled_proxy_check, expand=False, fill=False, padding=0)
+        row.pack_start(self.project_settings_label, expand=False, fill=False, padding=SPACING)
+        proxy_box.pack_start(row, expand=False, fill=False, padding=0)
+
+        self.pack_start(proxy_box, expand=False, fill=False, padding=SPACING * 2)
 
-        self.attach(self.__automatic_proxies, 1, 0, 1, 1)
-        self.attach(self.__force_proxies, 1, 1, 1, 1)
-        self.attach(self.__no_proxies, 1, 2, 1, 1)
         self.show_all()
 
+        size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.VERTICAL)
+        size_group.add_widget(self.__keep_open_check)
+        size_group.add_widget(hq_proxy_row)
+
+        if self.app.settings.proxyingStrategy == ProxyingStrategy.AUTOMATIC:
+            self.hq_proxy_check.set_active(True)
+            self.hq_combo.set_sensitive(True)
+            self.hq_combo.props.active = 0
+        elif self.app.settings.proxyingStrategy == ProxyingStrategy.ALL:
+            self.hq_proxy_check.set_active(True)
+            self.hq_combo.set_sensitive(True)
+            self.hq_combo.props.active = 1
+
+        if self.app.settings.auto_scaling_enabled:
+            self.scaled_proxy_check.set_active(True)
+
+    def _hq_proxy_check_cb(self, check_button):
+        active = check_button.get_active()
+        self.hq_combo.set_sensitive(active)
+        self.__update_help_button()
+
+        if not active:
+            self.scaled_proxy_check.set_active(False)
+
+    def __update_help_button(self):
+        if self.hq_proxy_check.get_active():
+            icon = "question-round-symbolic"
+        else:
+            icon = "warning-symbolic"
+        image = Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.BUTTON)
+        self.help_button.set_image(image)
+
+    def _help_button_clicked_cb(self, unused_button):
+        show_user_manual("importing")
+
+    def _scaled_proxy_check_cb(self, unused_button):
+        if self.scaled_proxy_check.get_active():
+            self.hq_proxy_check.set_active(True)
+
+    def _target_res_cb(self, label_widget, unused_uri):
+        self.app.gui.editor.showProjectSettingsDialog()
+        self.__update_scaled_proxy_check()
+
+    def __update_scaled_proxy_check(self):
+        target_width = self.app.project_manager.current_project.scaled_proxy_width
+        target_height = self.app.project_manager.current_project.scaled_proxy_height
+        self.scaled_proxy_check.set_label(_("Scale assets larger than %s×%s px.") % (target_width, 
target_height))
+
     def saveValues(self):
-        self.app.settings.closeImportDialog = self.__close_after.get_active()
-        if self.__force_proxies.get_active():
-            self.app.settings.proxyingStrategy = ProxyingStrategy.ALL
-        elif self.__no_proxies.get_active():
-            self.app.settings.proxyingStrategy = ProxyingStrategy.NOTHING
+        self.app.settings.closeImportDialog = not self.__keep_open_check.get_active()
+
+        if self.hq_proxy_check.get_active():
+            if self.hq_combo.props.active == 0:
+                self.app.settings.proxyingStrategy = ProxyingStrategy.AUTOMATIC
+            else:
+                self.app.settings.proxyingStrategy = ProxyingStrategy.ALL
         else:
-            self.app.settings.proxyingStrategy = ProxyingStrategy.AUTOMATIC
+            assert not self.scaled_proxy_check.get_active()
+            self.app.settings.proxyingStrategy = ProxyingStrategy.NOTHING
+
+        self.app.settings.auto_scaling_enabled = self.scaled_proxy_check.get_active()
 
 
 class AssetThumbnail(GObject.Object, Loggable):
@@ -184,6 +247,7 @@ class AssetThumbnail(GObject.Object, Loggable):
 
     EMBLEMS = {}
     PROXIED = "asset-proxied"
+    SCALED = "asset-scaled"
     NO_PROXY = "no-proxy"
     IN_PROGRESS = "asset-proxy-in-progress"
     ASSET_PROXYING_ERROR = "asset-proxying-error"
@@ -193,7 +257,7 @@ class AssetThumbnail(GObject.Object, Loggable):
 
     icons_by_name = {}
 
-    for status in [PROXIED, IN_PROGRESS, ASSET_PROXYING_ERROR, UNSUPPORTED]:
+    for status in [PROXIED, SCALED, IN_PROGRESS, ASSET_PROXYING_ERROR, UNSUPPORTED]:
         EMBLEMS[status] = GdkPixbuf.Pixbuf.new_from_file_at_size(
             os.path.join(get_pixmap_dir(), "%s.svg" % status), 64, 64)
 
@@ -347,14 +411,17 @@ class AssetThumbnail(GObject.Object, Loggable):
             icon = icon_theme.load_icon("dialog-question", size, 0)
         return icon
 
-    def __setState(self):
+    def _set_state(self):
         asset = self.__asset
         target = asset.get_proxy_target()
-        asset_is_proxy = self.proxy_manager.is_proxy_asset(asset)
-        if asset_is_proxy and target and not target.get_error():
-            # The asset is a proxy.
+        target_is_valid = target and not target.get_error()
+        if self.proxy_manager.is_scaled_proxy(asset) and target_is_valid:
+            # The asset is a scaled proxy.
+            self.state = self.SCALED
+        elif self.proxy_manager.is_hq_proxy(asset) and target_is_valid:
+            # The asset is a HQ proxy.
             self.state = self.PROXIED
-        elif not asset_is_proxy and asset.proxying_error:
+        elif not self.proxy_manager.is_proxy_asset(asset) and asset.proxying_error:
             self.state = self.ASSET_PROXYING_ERROR
         elif self.proxy_manager.is_asset_queued(asset):
             self.state = self.IN_PROGRESS
@@ -364,7 +431,7 @@ class AssetThumbnail(GObject.Object, Loggable):
             self.state = self.NO_PROXY
 
     def decorate(self):
-        self.__setState()
+        self._set_state()
         if self.state == self.NO_PROXY:
             self.small_thumb = self.src_small
             self.large_thumb = self.src_large
@@ -776,7 +843,7 @@ class MediaLibraryWidget(Gtk.Box, Loggable):
             self.treeview_scrollwin.hide()
             self.iconview_scrollwin.show_all()
 
-    def __filter_unsupported(self, filter_info):
+    def _filter_unsupported(self, filter_info):
         """Returns whether the specified item should be displayed."""
         if filter_info.mime_type not in SUPPORTED_MIMETYPES:
             return False
@@ -811,7 +878,7 @@ class MediaLibraryWidget(Gtk.Box, Loggable):
         filter.set_name(_("Supported file formats"))
         filter.add_custom(Gtk.FileFilterFlags.URI |
                           Gtk.FileFilterFlags.MIME_TYPE,
-                          self.__filter_unsupported)
+                          self._filter_unsupported)
         for formatter in GES.list_assets(GES.Formatter):
             for extension in formatter.get_meta("extension").split(","):
                 if not extension:
@@ -956,7 +1023,7 @@ class MediaLibraryWidget(Gtk.Box, Loggable):
             self._addAsset(asset)
 
         if self._project.loaded:
-            self.app.gui.editor.timeline_ui.switchProxies(asset)
+            self.app.gui.editor.timeline_ui.update_clips_asset(asset, proxy)
 
     def _assetAddedCb(self, unused_project, asset):
         """Checks whether the asset added to the project should be shown."""
@@ -1236,14 +1303,22 @@ class MediaLibraryWidget(Gtk.Box, Loggable):
             self.iconview.unselect_all()
 
     def __stopUsingProxyCb(self, unused_action, unused_parameter):
-        self._project.disable_proxies_for_assets(self.getSelectedAssets())
+        prefer_original = self.app.settings.proxyingStrategy == ProxyingStrategy.NOTHING
+        self._project.disable_proxies_for_assets(self.getSelectedAssets(),
+                                                 hq_proxy=not prefer_original)
 
     def __useProxiesCb(self, unused_action, unused_parameter):
         self._project.use_proxies_for_assets(self.getSelectedAssets())
 
+    def __use_scaled_proxies_cb(self, unused_action, unused_parameter):
+        self._project.use_proxies_for_assets(self.getSelectedAssets(),
+            scaled=True)
+
     def __deleteProxiesCb(self, unused_action, unused_parameter):
+        prefer_original = self.app.settings.proxyingStrategy == ProxyingStrategy.NOTHING
         self._project.disable_proxies_for_assets(self.getSelectedAssets(),
-                                                 delete_proxy_file=True)
+                                                 delete_proxy_file=True,
+                                                 hq_proxy=not prefer_original)
 
     def __open_containing_folder_cb(self, unused_action, unused_parameter):
         assets = self.getSelectedAssets()
@@ -1293,15 +1368,63 @@ class MediaLibraryWidget(Gtk.Box, Loggable):
 
         proxies = [asset.get_proxy_target() for asset in assets
                    if self.app.proxy_manager.is_proxy_asset(asset)]
+        hq_proxies = [asset.get_proxy_target() for asset in assets
+                   if self.app.proxy_manager.is_hq_proxy(asset)]
+        scaled_proxies = [asset.get_proxy_target() for asset in assets
+                   if self.app.proxy_manager.is_scaled_proxy(asset)]
         in_progress = [asset.creation_progress for asset in assets
                        if asset.creation_progress < 100]
 
-        if proxies or in_progress:
+        if hq_proxies:
+            action = Gio.SimpleAction.new("unproxy-asset", None)
+            action.connect("activate", self.__stopUsingProxyCb)
+            action_group.insert(action)
+            text = ngettext("Do not use Optimised Proxy for selected asset",
+                            "Do not use Optimised Proxies for selected assets",
+                            len(proxies) + len(in_progress))
+
+            menu_model.append(text, "assets.%s" %
+                              action.get_name().replace(" ", "."))
+
+            action = Gio.SimpleAction.new("delete-proxies", None)
+            action.connect("activate", self.__deleteProxiesCb)
+            action_group.insert(action)
+
+            text = ngettext("Delete corresponding proxy file",
+                            "Delete corresponding proxy files",
+                            len(proxies) + len(in_progress))
+
+            menu_model.append(text, "assets.%s" %
+                              action.get_name().replace(" ", "."))
+
+        if in_progress:
             action = Gio.SimpleAction.new("unproxy-asset", None)
             action.connect("activate", self.__stopUsingProxyCb)
             action_group.insert(action)
-            text = ngettext("Do not use proxy for selected asset",
-                            "Do not use proxies for selected assets",
+            text = ngettext("Do not use Proxy for selected asset",
+                            "Do not use Proxies for selected assets",
+                            len(proxies) + len(in_progress))
+
+            menu_model.append(text, "assets.%s" %
+                              action.get_name().replace(" ", "."))
+
+            action = Gio.SimpleAction.new("delete-proxies", None)
+            action.connect("activate", self.__deleteProxiesCb)
+            action_group.insert(action)
+
+            text = ngettext("Delete corresponding proxy file",
+                            "Delete corresponding proxy files",
+                            len(proxies) + len(in_progress))
+
+            menu_model.append(text, "assets.%s" %
+                              action.get_name().replace(" ", "."))
+
+        if scaled_proxies:
+            action = Gio.SimpleAction.new("unproxy-asset", None)
+            action.connect("activate", self.__stopUsingProxyCb)
+            action_group.insert(action)
+            text = ngettext("Do not use Scaled Proxy for selected asset",
+                            "Do not use Scaled Proxies for selected assets",
                             len(proxies) + len(in_progress))
 
             menu_model.append(text, "assets.%s" %
@@ -1322,8 +1445,17 @@ class MediaLibraryWidget(Gtk.Box, Loggable):
             action = Gio.SimpleAction.new("use-proxies", None)
             action.connect("activate", self.__useProxiesCb)
             action_group.insert(action)
-            text = ngettext("Use proxy for selected asset",
-                            "Use proxies for selected assets", len(assets))
+            text = ngettext("Use Optimised Proxy for selected asset",
+                            "Use Optimised Proxies for selected assets", len(assets))
+
+            menu_model.append(text, "assets.%s" %
+                              action.get_name().replace(" ", "."))
+
+            action = Gio.SimpleAction.new("use-scaled-proxies", None)
+            action.connect("activate", self.__use_scaled_proxies_cb)
+            action_group.insert(action)
+            text = ngettext("Use Scaled Proxy for selected asset",
+                            "Use Scaled Proxies for selected assets", len(assets))
 
             menu_model.append(text, "assets.%s" %
                               action.get_name().replace(" ", "."))
diff --git a/pitivi/project.py b/pitivi/project.py
index b7da243a..2b63f24f 100644
--- a/pitivi/project.py
+++ b/pitivi/project.py
@@ -708,6 +708,9 @@ class Project(Loggable, GES.Project):
         # Project property default values
         self.register_meta(GES.MetaFlag.READWRITE, "author", "")
 
+        self.register_meta(GES.MetaFlag.READWRITE, "scaled_proxy_width", 0)
+        self.register_meta(GES.MetaFlag.READWRITE, "scaled_proxy_height", 0)
+
         # The rendering settings.
         self.set_meta("render-scale", 100.0)
 
@@ -805,6 +808,32 @@ class Project(Loggable, GES.Project):
             return
         self.set_meta("author", author)
 
+    @property
+    def scaled_proxy_height(self):
+        return self.get_meta("scaled_proxy_height") or self.app.settings.default_scaled_proxy_height
+
+    @scaled_proxy_height.setter
+    def scaled_proxy_height(self, scaled_proxy_height):
+        if scaled_proxy_height == self.get_meta("scaled_proxy_height"):
+            return
+        self.set_meta("scaled_proxy_height", scaled_proxy_height)
+        self.setModificationState(True)
+
+    @property
+    def scaled_proxy_width(self):
+        return self.get_meta("scaled_proxy_width") or self.app.settings.default_scaled_proxy_width
+
+    @scaled_proxy_width.setter
+    def scaled_proxy_width(self, scaled_proxy_width):
+        if scaled_proxy_width == self.get_meta("scaled_proxy_width"):
+            return
+        self.set_meta("scaled_proxy_width", scaled_proxy_width)
+        self.setModificationState(True)
+
+    def has_scaled_proxy_size(self):
+        """Returns whether the proxy size has been set."""
+        return bool(self.get_meta("scaled_proxy_width") and self.get_meta("scaled_proxy_height"))
+
     @staticmethod
     def get_thumb_path(uri, resolution):
         """Returns path of thumbnail of specified resolution in the cache."""
@@ -1123,9 +1152,22 @@ class Project(Loggable, GES.Project):
             else:
                 # Check that we are not recreating deleted proxy
                 proxy_uri = self.app.proxy_manager.getProxyUri(asset)
+                scaled_proxy_uri = self.app.proxy_manager.getProxyUri(asset, scaled=True)
+
+                no_hq_proxy = False
+                no_scaled_proxy = False
+
                 if proxy_uri and proxy_uri not in self.__deleted_proxy_files and \
                         asset.props.id not in self.__awaited_deleted_proxy_targets:
+                    no_hq_proxy = True
+
+                if scaled_proxy_uri and scaled_proxy_uri not in self.__deleted_proxy_files and \
+                        asset.props.id not in self.__awaited_deleted_proxy_targets:
+                    no_scaled_proxy = True
+
+                if no_hq_proxy and no_scaled_proxy:
                     asset.ready = True
+
                 num_loaded += 1
 
         if all_ready:
@@ -1224,9 +1266,7 @@ class Project(Loggable, GES.Project):
         asset.creation_progress = 100
         asset.ready = True
         if proxy:
-            proxy.ready = False
-            proxy.error = None
-            proxy.creation_progress = 100
+            self.finalize_proxy(proxy)
 
         asset.set_proxy(proxy)
         try:
@@ -1240,6 +1280,11 @@ class Project(Loggable, GES.Project):
 
         self.__updateAssetLoadingProgress()
 
+    def finalize_proxy(self, proxy):
+        proxy.ready = False
+        proxy.error = None
+        proxy.creation_progress = 100
+
     # ------------------------------------------ #
     # GES.Project virtual methods implementation #
     # ------------------------------------------ #
@@ -1250,12 +1295,12 @@ class Project(Loggable, GES.Project):
 
         self._prepare_asset_processing(asset)
 
-    def __regenerate_missing_proxy(self, asset):
+    def __regenerate_missing_proxy(self, asset, scaled=False):
         self.info("Re generating deleted proxy file %s.", asset.props.id)
         GES.Asset.needs_reload(GES.UriClip, asset.props.id)
         self._prepare_asset_processing(asset)
         asset.force_proxying = True
-        self.app.proxy_manager.add_job(asset)
+        self.app.proxy_manager.add_job(asset, scaled=scaled)
         self.__updateAssetLoadingProgress()
 
     def do_missing_uri(self, error, asset):
@@ -1271,7 +1316,8 @@ class Project(Loggable, GES.Project):
             target = [asset for asset in self.list_assets(GES.UriClip) if
                       asset.props.id == target_uri]
             if target:
-                self.__regenerate_missing_proxy(target[0])
+                scaled = self.app.proxy_manager.is_scaled_proxy(asset)
+                self.__regenerate_missing_proxy(target[0], scaled=scaled)
             else:
                 self.__awaited_deleted_proxy_targets.add(target_uri)
 
@@ -1515,12 +1561,19 @@ class Project(Loggable, GES.Project):
 
         return GES.Project.save(self, ges_timeline, uri, formatter_asset, overwrite)
 
-    def use_proxies_for_assets(self, assets):
+    def use_proxies_for_assets(self, assets, scaled=False):
+        proxy_manager = self.app.proxy_manager
         originals = []
         for asset in assets:
-            if not self.app.proxy_manager.is_proxy_asset(asset):
+            if scaled:
+                is_proxied = proxy_manager.is_scaled_proxy(asset) and \
+                    not proxy_manager.asset_matches_target_res(asset)
+            else:
+                is_proxied = proxy_manager.is_hq_proxy(asset)
+            if not is_proxied:
                 target = asset.get_proxy_target()
-                if target and target.props.id == self.app.proxy_manager.getProxyUri(asset):
+                uri = proxy_manager.getProxyUri(asset, scaled=scaled)
+                if target and target.props.id == uri:
                     self.info("Missing proxy needs to be recreated after cancelling"
                               " its recreation")
                     target.unproxy(asset)
@@ -1536,22 +1589,43 @@ class Project(Loggable, GES.Project):
                     self.app.action_log.push(action)
                     self._prepare_asset_processing(asset)
                     asset.force_proxying = True
-                    self.app.proxy_manager.add_job(asset)
+                    proxy_manager.add_job(asset, scaled)
 
-    def disable_proxies_for_assets(self, assets, delete_proxy_file=False):
+    def disable_proxies_for_assets(self, assets, delete_proxy_file=False, hq_proxy=True):
+        proxy_manager = self.app.proxy_manager
         for asset in assets:
-            if self.app.proxy_manager.is_proxy_asset(asset):
+            if proxy_manager.is_proxy_asset(asset):
                 proxy_target = asset.get_proxy_target()
                 # The asset is a proxy for the proxy_target original asset.
                 self.debug("Stop proxying %s", proxy_target.props.id)
                 proxy_target.set_proxy(None)
+                if proxy_manager.is_scaled_proxy(asset) \
+                        and not proxy_manager.isAssetFormatWellSupported(proxy_target) \
+                        and hq_proxy:
+                    # The original asset is unsupported, and the user prefers
+                    # to edit with HQ proxies instead of scaled proxies.
+                    self.use_proxies_for_assets([proxy_target])
                 self.remove_asset(asset)
                 proxy_target.force_proxying = False
                 if delete_proxy_file:
                     os.remove(Gst.uri_get_location(asset.props.id))
             else:
                 # The asset is an original which is not being proxied.
-                self.app.proxy_manager.cancel_job(asset)
+                proxy_manager.cancel_job(asset)
+
+    def regenerate_scaled_proxies(self):
+        assets = self.list_assets(GES.Extractable)
+        scaled_proxies = []
+        scaled_proxy_targets = []
+
+        for asset in assets:
+            if self.app.proxy_manager.is_scaled_proxy(asset):
+                scaled_proxies.append(asset)
+                scaled_proxy_targets.append(asset.get_proxy_target())
+
+        self.disable_proxies_for_assets(scaled_proxies, delete_proxy_file=True,
+                                        hq_proxy=False)
+        self.use_proxies_for_assets(scaled_proxy_targets, scaled=True)
 
     def _commit(self):
         """Logs the operation and commits.
@@ -1980,7 +2054,6 @@ class ProjectSettingsDialog(object):
         self.video_presets_combo = getObj("video_presets_combo")
         self.constrain_sar_button = getObj("constrain_sar_button")
         self.select_dar_radiobutton = getObj("select_dar_radiobutton")
-        self.author_entry = getObj("author_entry")
         self.year_spinbutton = getObj("year_spinbutton")
 
         self.video_preset_menubutton = getObj("video_preset_menubutton")
@@ -1991,6 +2064,10 @@ class ProjectSettingsDialog(object):
         self.audio_presets.setupUi(self.audio_presets_combo,
                                    self.audio_preset_menubutton)
 
+        self.scaled_proxy_width_spin = getObj("scaled_proxy_width")
+        self.scaled_proxy_height_spin = getObj("scaled_proxy_height")
+        self.proxy_res_linked_check = getObj("proxy_res_linked")
+
     def _setupUiConstraints(self):
         """Creates the dynamic widgets and connects other widgets."""
 
@@ -2002,7 +2079,6 @@ class ProjectSettingsDialog(object):
 
         # Populate comboboxes.
         self.frame_rate_combo.set_model(frame_rates)
-
         self.channels_combo.set_model(audio_channels)
         self.sample_rate_combo.set_model(audio_rates)
 
@@ -2026,14 +2102,26 @@ class ProjectSettingsDialog(object):
                           update_func_args=(self.video_presets,))
         self.wg.addVertex(self.channels_combo, signal="changed")
         self.wg.addVertex(self.sample_rate_combo, signal="changed")
+        self.wg.addVertex(self.scaled_proxy_width_spin, signal="value-changed")
+        self.wg.addVertex(self.scaled_proxy_height_spin, signal="value-changed")
 
         # Constrain width and height IFF the Link checkbox is checked.
+        # Video
         self.wg.addEdge(self.width_spinbutton, self.height_spinbutton,
                         predicate=self.widthHeightLinked,
                         edge_func=self.updateHeight)
         self.wg.addEdge(self.height_spinbutton, self.width_spinbutton,
                         predicate=self.widthHeightLinked,
                         edge_func=self.updateWidth)
+        # Proxy
+        self.wg.addEdge(self.scaled_proxy_width_spin,
+                        self.scaled_proxy_height_spin,
+                        predicate=self.proxy_res_linked,
+                        edge_func=self.update_scaled_proxy_height)
+        self.wg.addEdge(self.scaled_proxy_height_spin,
+                        self.scaled_proxy_width_spin,
+                        predicate=self.proxy_res_linked,
+                        edge_func=self.update_scaled_proxy_width)
 
         # Keep the framerate combo and fraction widgets in sync.
         self.wg.addBiEdge(
@@ -2079,6 +2167,9 @@ class ProjectSettingsDialog(object):
     def widthHeightLinked(self):
         return self.constrain_sar_button.props.active and not self.video_presets.ignore_update_requests
 
+    def proxy_res_linked(self):
+        return self.proxy_res_linked_check.props.active
+
     def _updateFraction(self, unused, fraction, combo):
         fraction.setWidgetValue(get_combo_value(combo))
 
@@ -2114,6 +2205,23 @@ class ProjectSettingsDialog(object):
         height = int(fraction.num / fraction.denom)
         self.height_spinbutton.set_value(height)
 
+    def _proxy_res_linked_toggle_cb(self, unused_button):
+        width = int(self.scaled_proxy_width_spin.get_value())
+        height = int(self.scaled_proxy_height_spin.get_value())
+        self.proxy_aspect_ratio = Gst.Fraction(width, height)
+
+    def update_scaled_proxy_width(self):
+        height = int(self.scaled_proxy_height_spin.get_value())
+        fraction = height * self.proxy_aspect_ratio
+        width = int(fraction.num / fraction.denom)
+        self.scaled_proxy_width_spin.set_value(width)
+
+    def update_scaled_proxy_height(self):
+        width = int(self.scaled_proxy_width_spin.get_value())
+        fraction = width / self.proxy_aspect_ratio
+        height = int(fraction.num / fraction.denom)
+        self.scaled_proxy_height_spin.set_value(height)
+
     def updateUI(self):
         # Video
         self.width_spinbutton.set_value(self.project.videowidth)
@@ -2140,6 +2248,9 @@ class ProjectSettingsDialog(object):
             year = datetime.datetime.now().year
         self.year_spinbutton.get_adjustment().set_value(year)
 
+        self.scaled_proxy_width_spin.set_value(self.project.scaled_proxy_width)
+        self.scaled_proxy_height_spin.set_value(self.project.scaled_proxy_height)
+
     def updateProject(self):
         with self.app.action_log.started("change project settings",
                                          toplevel=True):
@@ -2153,6 +2264,17 @@ class ProjectSettingsDialog(object):
             self.project.audiochannels = get_combo_value(self.channels_combo)
             self.project.audiorate = get_combo_value(self.sample_rate_combo)
 
+            proxy_width = int(self.scaled_proxy_width_spin.get_value())
+            proxy_height = int(self.scaled_proxy_height_spin.get_value())
+            # Update scaled proxy meta-data and trigger proxy regen
+            if not self.project.has_scaled_proxy_size() or \
+                    self.project.scaled_proxy_width != proxy_width or \
+                    self.project.scaled_proxy_height != proxy_height:
+                self.project.scaled_proxy_width = proxy_width
+                self.project.scaled_proxy_height = proxy_height
+
+                self.project.regenerate_scaled_proxies()
+
     def _responseCb(self, unused_widget, response):
         """Handles the dialog being closed."""
         if response == Gtk.ResponseType.OK:
diff --git a/pitivi/render.py b/pitivi/render.py
index e3216473..fdb7184d 100644
--- a/pitivi/render.py
+++ b/pitivi/render.py
@@ -444,7 +444,7 @@ class RenderDialog(Loggable):
         # the current container format.
         self.preferred_vencoder = self.project.vencoder
         self.preferred_aencoder = self.project.aencoder
-        self.__unproxiedClips = {}
+        self.__replaced_assets = {}
 
         self.frame_rate_combo.set_model(frame_rates)
         self.channels_combo.set_model(audio_channels)
@@ -935,7 +935,7 @@ class RenderDialog(Loggable):
         self._time_spent_paused = 0
         self._pipeline.set_state(Gst.State.NULL)
         self.project.set_rendering(False)
-        self.__useProxyAssets()
+        self._use_proxy_assets()
         self._disconnectFromGst()
         self._pipeline.set_mode(GES.PipelineFlags.FULL_PREVIEW)
         self._pipeline.set_state(Gst.State.PAUSED)
@@ -983,50 +983,61 @@ class RenderDialog(Loggable):
         except GLib.Error as e:
             self.warning("GSound failed to play: %s", e)
 
-    def __maybeUseSourceAsset(self):
-        if self.__always_use_proxies.get_active():
-            self.debug("Rendering from proxies, not replacing assets")
-            return
-
-        for layer in self.app.project_manager.current_project.ges_timeline.get_layers():
-            for clip in layer.get_clips():
-                if not isinstance(clip, GES.UriClip):
-                    continue
-
-                asset = clip.get_asset()
-                asset_target = asset.get_proxy_target()
-                if not asset_target:
-                    # The asset is not a proxy.
-                    continue
-
-                if self.__automatically_use_proxies.get_active():
-                    if not self.app.proxy_manager.isAssetFormatWellSupported(
-                            asset_target):
-                        self.info("Original asset %s format not well supported, "
-                                  "rendering from proxy.",
-                                  asset_target.props.id)
-                        continue
-
-                    self.info("Original asset %s format well supported, "
-                              "rendering from real asset.",
-                              asset_target.props.id)
+    def _asset_replacement(self, clip):
+        if not isinstance(clip, GES.UriClip):
+            return None
 
-                if asset_target.get_error():
-                    # The original asset cannot be used.
-                    continue
+        asset = clip.get_asset()
+        asset_target = asset.get_proxy_target()
+        if not asset_target:
+            # The asset is not a proxy.
+            return None
 
-                clip.set_asset(asset_target)
-                self.info("Using original asset %s (instead of proxy %s)",
-                          asset_target.get_id(),
-                          asset.get_id())
-                self.__unproxiedClips[clip] = asset
+        # Replace all proxies
+        if self.__never_use_proxies.get_active():
+            return asset_target
 
-    def __useProxyAssets(self):
-        for clip, asset in self.__unproxiedClips.items():
+        # Use HQ Proxy (or equivalent) only for unsupported assets
+        if self.__automatically_use_proxies.get_active():
+            if self.app.proxy_manager.isAssetFormatWellSupported(
+                    asset_target):
+                return asset_target
+            else:
+                proxy_unsupported = True
+
+        # Use HQ Proxy (or equivalent) whenever available
+        if self.__always_use_proxies.get_active() or proxy_unsupported:
+            if self.app.proxy_manager.is_hq_proxy(asset):
+                return None
+
+            if self.app.proxy_manager.is_scaled_proxy(asset):
+                width, height = self.project.getVideoWidthAndHeight(render=True)
+                stream = asset.get_info().get_video_streams()[0]
+                asset_res = [stream.get_width(), stream.get_height()]
+
+                if asset_res[0] == width and asset_res[1] == height:
+                    # Check whether the scaled proxy size matches the render size
+                    # exactly. If the size is same, render from the scaled proxy
+                    # to avoid double scaling.
+                    return None
+
+                hq_proxy = GES.Asset.request(GES.UriClip,
+                    self.app.proxy_manager.getProxyUri(asset_target))
+                return hq_proxy or None
+
+    def __replace_proxies(self):
+        for clip in self.project.ges_timeline.ui.clips():
+            asset = self._asset_replacement(clip)
+            if asset:
+                self.__replaced_assets[clip] = clip.get_asset()
+                clip.set_asset(asset)
+
+    def _use_proxy_assets(self):
+        for clip, asset in self.__replaced_assets.items():
             self.info("Reverting to using proxy asset %s", asset)
             clip.set_asset(asset)
 
-        self.__unproxiedClips = {}
+        self.__replaced_assets = {}
 
     # ------------------- Callbacks ------------------------------------------ #
 
@@ -1042,7 +1053,7 @@ class RenderDialog(Loggable):
 
     def _renderButtonClickedCb(self, unused_button):
         """Starts the rendering process."""
-        self.__maybeUseSourceAsset()
+        self.__replace_proxies()
         self.outfile = os.path.join(self.filebutton.get_uri(),
                                     self.fileentry.get_text())
         self.progress = RenderingProgressDialog(self.app, self)
diff --git a/pitivi/timeline/previewers.py b/pitivi/timeline/previewers.py
index 74f35366..4094bed8 100644
--- a/pitivi/timeline/previewers.py
+++ b/pitivi/timeline/previewers.py
@@ -554,7 +554,7 @@ class AssetPreviewer(Previewer, Loggable):
         self.thumb_height = THUMB_HEIGHT
         self.thumb_width = 0
 
-        self.thumb_cache = ThumbnailCache.get(self.uri)
+        self.thumb_cache = ThumbnailCache.get(self.asset)
 
         self.thumb_width, unused_height = self.thumb_cache.image_size
         self.pipeline = None
diff --git a/pitivi/timeline/timeline.py b/pitivi/timeline/timeline.py
index 01e0d47b..d285655c 100644
--- a/pitivi/timeline/timeline.py
+++ b/pitivi/timeline/timeline.py
@@ -43,6 +43,7 @@ from pitivi.timeline.previewers import Previewer
 from pitivi.timeline.ruler import ScaleRuler
 from pitivi.undo.timeline import CommitTimelineFinalizingAction
 from pitivi.utils.loggable import Loggable
+from pitivi.utils.proxy import get_proxy_target
 from pitivi.utils.timeline import EditingContext
 from pitivi.utils.timeline import SELECT
 from pitivi.utils.timeline import Selection
@@ -831,6 +832,11 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable):
 
         return clips.union(grouped_clips)
 
+    def clips(self):
+        for layer in self.ges_timeline.get_layers():
+            for clip in layer.get_clips():
+                yield clip
+
     def _motion_notify_event_cb(self, unused_widget, event):
         if self.draggingElement:
             if type(self.draggingElement) == TransitionClip and \
@@ -1421,28 +1427,19 @@ class TimelineContainer(Gtk.Grid, Zoomable, Loggable):
 
     # Public API
 
-    def switchProxies(self, asset):
-        proxy = asset.props.proxy
-        unproxy = False
-
-        if not proxy:
-            unproxy = True
-            proxy_uri = self.app.proxy_manager.getProxyUri(asset)
-            proxy = GES.Asset.request(GES.UriClip,
-                                      proxy_uri)
-            if not proxy:
-                self.debug("proxy_uri: %s does not have an asset associated",
-                           proxy_uri)
-                return
+    def update_clips_asset(self, asset, proxy):
+        """Updates the relevant clips to use the asset or the proxy.
 
-        layers = self.ges_timeline.get_layers()
-        for layer in layers:
-            for clip in layer.get_clips():
-                if unproxy:
-                    if clip.get_asset() == proxy:
-                        clip.set_asset(asset)
-                elif clip.get_asset() == proxy.get_proxy_target():
-                    clip.set_asset(proxy)
+        Args:
+            asset (GES.Asset): Only the clips who's current asset's target is
+                is this will be updated.
+            proxy (Ges.Asset): The proxy to use, or None to use the asset itself.
+        """
+        original_asset = get_proxy_target(asset)
+        replacement_asset = proxy or asset
+        for clip in self.timeline.clips():
+            if get_proxy_target(clip) == original_asset:
+                clip.set_asset(replacement_asset)
         self._project.pipeline.commit_timeline()
 
     def insertAssets(self, assets, position=None):
diff --git a/pitivi/utils/proxy.py b/pitivi/utils/proxy.py
index 0eae303e..976bbeb8 100644
--- a/pitivi/utils/proxy.py
+++ b/pitivi/utils/proxy.py
@@ -18,6 +18,8 @@
 # Boston, MA 02110-1301, USA.
 import os
 import time
+from fractions import Fraction
+from gettext import gettext as _
 
 from gi.repository import GES
 from gi.repository import Gio
@@ -28,6 +30,7 @@ from gi.repository import GstPbutils
 from gi.repository import GstTranscoder
 
 from pitivi.configure import get_gstpresets_dir
+from pitivi.dialogs.prefs import PreferencesDialog
 from pitivi.settings import GlobalSettings
 from pitivi.utils.loggable import Loggable
 
@@ -46,16 +49,47 @@ GlobalSettings.addConfigOption('proxyingStrategy',
                                section='proxy',
                                key='proxying-strategy',
                                default=ProxyingStrategy.AUTOMATIC)
+
 GlobalSettings.addConfigOption('numTranscodingJobs',
                                section='proxy',
                                key='num-proxying-jobs',
-                               default=4)
+                               default=4,
+                               notify=True)
+PreferencesDialog.addNumericPreference('numTranscodingJobs',
+                                       description="",
+                                       section="_proxies",
+                                       label=_("Max number of parallel transcoding jobs"),
+                                       lower=1)
+
 GlobalSettings.addConfigOption("max_cpu_usage",
                                section="proxy",
                                key="max-cpu-usage",
-                               default=10)
+                               default=10,
+                               notify=True)
+PreferencesDialog.addNumericPreference('max_cpu_usage',
+                                       description="",
+                                       section="_proxies",
+                                       label=_("Max CPU usage dedicated to transcoding"),
+                                       lower=1,
+                                       upper=100)
 
 
+GlobalSettings.addConfigOption("auto_scaling_enabled",
+                               section="proxy",
+                               key="s-proxy-enabled",
+                               default=False,
+                               notify=True)
+GlobalSettings.addConfigOption("default_scaled_proxy_width",
+                               section="proxy",
+                               key="s-proxy-width",
+                               default=1920,
+                               notify=True)
+GlobalSettings.addConfigOption("default_scaled_proxy_height",
+                               section="proxy",
+                               key="s-proxy-height",
+                               default=1080,
+                               notify=True)
+
 ENCODING_FORMAT_PRORES = "prores-raw-in-matroska.gep"
 ENCODING_FORMAT_JPEG = "jpeg-raw-in-matroska.gep"
 
@@ -86,7 +120,7 @@ class ProxyManager(GObject.Object, Loggable):
     }
 
     WHITELIST_CONTAINER_CAPS = ["video/quicktime", "application/ogg", "application/xges",
-                                "video/x-matroska", "video/webm"]
+                                "video/x-matroska", "video/webm", "image/jpeg"]
     WHITELIST_AUDIO_CAPS = ["audio/mpeg", "audio/x-vorbis",
                             "audio/x-raw", "audio/x-flac",
                             "audio/x-wav"]
@@ -105,7 +139,10 @@ class ProxyManager(GObject.Object, Loggable):
         a = GstPbutils.EncodingAudioProfile.new(Gst.Caps(audio), None, None, 0)
         WHITELIST_FORMATS.append(a)
 
-    proxy_extension = "proxy.mkv"
+    hq_proxy_extension = "proxy.mkv"
+    scaled_proxy_extension = "scaledproxy.mkv"
+    # Suffix for filenames of proxies being created.
+    part_suffix = ".part"
 
     def __init__(self, app):
         GObject.Object.__init__(self)
@@ -119,6 +156,9 @@ class ProxyManager(GObject.Object, Loggable):
         self._start_proxying_time = 0
         self.__running_transcoders = []
         self.__pending_transcoders = []
+        # The scaled proxy transcoders waiting for their corresponding shadow
+        # HQ proxy transcoder to finish.
+        self.__waiting_transcoders = []
 
         self.__encoding_target_file = None
         self.proxyingUnsupported = False
@@ -135,6 +175,29 @@ class ProxyManager(GObject.Object, Loggable):
             self.error("Not supporting any proxy formats!")
             return
 
+    def _scale_asset_resolution(self, asset, max_width, max_height):
+        stream = asset.get_info().get_video_streams()[0]
+        width = stream.get_width()
+        height = stream.get_height()
+        aspect_ratio = Fraction(width, height)
+
+        if aspect_ratio.numerator >= width or aspect_ratio.denominator >= height:
+            self.log("Unscalable aspect ratio.")
+            return width, height
+        if aspect_ratio.numerator >= max_width or aspect_ratio.denominator >= max_height:
+            self.log("Cannot scale to target resolution.")
+            return width, height
+
+        if width > max_width or height > max_height:
+            width_factor = max_width // aspect_ratio.numerator
+            height_factor = max_height // aspect_ratio.denominator
+            scaling_factor = min(height_factor, width_factor)
+
+            width = aspect_ratio.numerator * scaling_factor
+            height = aspect_ratio.denominator * scaling_factor
+
+        return width, height
+
     def _assetMatchesEncodingFormat(self, asset, encoding_profile):
         def capsMatch(info, profile):
             return not info.get_caps().intersect(profile.get_format()).is_empty()
@@ -168,7 +231,8 @@ class ProxyManager(GObject.Object, Loggable):
                         return False
         return True
 
-    def __getEncodingProfile(self, encoding_target_file, asset=None):
+    def __getEncodingProfile(self, encoding_target_file, asset=None, width=None,
+            height=None):
         encoding_target = GstPbutils.EncodingTarget.load_from_file(
             os.path.join(get_gstpresets_dir(), encoding_target_file))
         encoding_profile = encoding_target.get_profile("default")
@@ -188,6 +252,10 @@ class ProxyManager(GObject.Object, Loggable):
                     Gst.ELEMENT_FACTORY_TYPE_ENCODER, Gst.Rank.MARGINAL),
                     profile_format, Gst.PadDirection.SRC, False):
                 return None
+            if height and width and profile.get_type_nick() == "video":
+                profile.set_restriction(Gst.Caps.from_string(
+                    "video/x-raw, width=%d, height=%d" % (width, height)))
+
             if not Gst.ElementFactory.list_filter(
                 Gst.ElementFactory.list_get_elements(
                     Gst.ELEMENT_FACTORY_TYPE_DECODER, Gst.Rank.MARGINAL),
@@ -216,12 +284,25 @@ class ProxyManager(GObject.Object, Loggable):
 
     @classmethod
     def is_proxy_asset(cls, obj):
+        return cls.is_scaled_proxy(obj) or cls.is_hq_proxy(obj)
+
+    @classmethod
+    def is_scaled_proxy(cls, obj):
         if isinstance(obj, GES.Asset):
             uri = obj.props.id
         else:
             uri = obj
 
-        return uri.endswith("." + cls.proxy_extension)
+        return uri.endswith("." + cls.scaled_proxy_extension)
+
+    @classmethod
+    def is_hq_proxy(cls, obj):
+        if isinstance(obj, GES.Asset):
+            uri = obj.props.id
+        else:
+            uri = obj
+
+        return uri.endswith("." + cls.hq_proxy_extension)
 
     def checkProxyLoadingSucceeded(self, proxy):
         if self.is_proxy_asset(proxy):
@@ -237,9 +318,12 @@ class ProxyManager(GObject.Object, Loggable):
         else:
             uri = obj
 
+        if cls.is_scaled_proxy(uri):
+            return ".".join(uri.split(".")[:-4])
+
         return ".".join(uri.split(".")[:-3])
 
-    def getProxyUri(self, asset):
+    def getProxyUri(self, asset, scaled=False):
         """Returns the URI of a possible proxy file.
 
         The name looks like:
@@ -255,8 +339,16 @@ class ProxyManager(GObject.Object, Loggable):
                 return None
             else:
                 raise
-
-        return "%s.%s.%s" % (asset.get_id(), file_size, self.proxy_extension)
+        if scaled:
+            max_w = self.app.project_manager.current_project.scaled_proxy_width
+            max_h = self.app.project_manager.current_project.scaled_proxy_height
+            t_width, t_height = self._scale_asset_resolution(asset, max_w, max_h)
+            proxy_res = "%sx%s" % (t_width, t_height)
+            return "%s.%s.%s.%s" % (asset.get_id(), file_size, proxy_res,
+                self.scaled_proxy_extension)
+        else:
+            return "%s.%s.%s" % (asset.get_id(), file_size,
+                self.hq_proxy_extension)
 
     def isAssetFormatWellSupported(self, asset):
         for encoding_format in self.WHITELIST_FORMATS:
@@ -266,7 +358,17 @@ class ProxyManager(GObject.Object, Loggable):
 
         return False
 
-    def __assetNeedsTranscoding(self, asset):
+    def asset_matches_target_res(self, asset):
+        stream = asset.get_info().get_video_streams()[0]
+
+        asset_res = (stream.get_width(), stream.get_height())
+        target_res = self._scale_asset_resolution(asset,
+            self.app.project_manager.current_project.scaled_proxy_width,
+            self.app.project_manager.current_project.scaled_proxy_height)
+
+        return asset_res == target_res
+
+    def __assetNeedsTranscoding(self, asset, scaled=False):
         if self.proxyingUnsupported:
             self.info("No proxying supported")
             return False
@@ -280,7 +382,11 @@ class ProxyManager(GObject.Object, Loggable):
             return False
 
         if self.app.settings.proxyingStrategy == ProxyingStrategy.AUTOMATIC \
-                and not self.is_proxy_asset(asset) and \
+                and scaled and not self.asset_matches_target_res(asset):
+            return True
+
+        if self.app.settings.proxyingStrategy == ProxyingStrategy.AUTOMATIC \
+                and not scaled and not self.is_hq_proxy(asset) and \
                 self.isAssetFormatWellSupported(asset):
             return False
 
@@ -319,9 +425,12 @@ class ProxyManager(GObject.Object, Loggable):
 
             return
 
+        shadow = transcoder and self._is_shadow_transcoder(transcoder)
+
         if not transcoder:
             if not self.__assetsMatch(asset, proxy):
-                return self.__createTranscoder(asset)
+                self.__createTranscoder(asset)
+                return
         else:
             transcoder.props.pipeline.props.video_filter.finalize()
             transcoder.props.pipeline.props.audio_filter.finalize()
@@ -338,30 +447,53 @@ class ProxyManager(GObject.Object, Loggable):
                 )
             )
 
-        self.emit("proxy-ready", asset, proxy)
-        self.__emitProgress(proxy, 100)
+        if shadow:
+            self.app.project_manager.current_project.finalize_proxy(proxy)
+        else:
+            self.emit("proxy-ready", asset, proxy)
+            self.__emitProgress(proxy, 100)
 
     def __transcoderErrorCb(self, transcoder, error, unused_details, asset):
         self.emit("error-preparing-asset", asset, None, error)
 
     def __transcoderDoneCb(self, transcoder, asset):
+        transcoder.disconnect_by_func(self.__proxyingPositionChangedCb)
         transcoder.disconnect_by_func(self.__transcoderDoneCb)
         transcoder.disconnect_by_func(self.__transcoderErrorCb)
-        transcoder.disconnect_by_func(self.__proxyingPositionChangedCb)
 
         self.debug("Transcoder done with %s", asset.get_id())
 
         self.__running_transcoders.remove(transcoder)
 
-        proxy_uri = self.getProxyUri(asset)
+        proxy_uri = transcoder.props.dest_uri.rstrip(ProxyManager.part_suffix)
         os.rename(Gst.uri_get_location(transcoder.props.dest_uri),
                   Gst.uri_get_location(proxy_uri))
 
-        # Make sure that if it first failed loading, the proxy is forced to be
-        # reloaded in the GES cache.
-        GES.Asset.needs_reload(GES.UriClip, proxy_uri)
-        GES.Asset.request_async(GES.UriClip, proxy_uri, None,
-                                self.__assetLoadedCb, asset, transcoder)
+        shadow = self._is_shadow_transcoder(transcoder)
+        second_transcoder = self._get_second_transcoder(transcoder)
+        if second_transcoder and not shadow:
+            # second_transcoder is the shadow for transcoder.
+            # Defer loading until the shadow transcoder finishes.
+            self.__waiting_transcoders.append([transcoder, asset])
+        else:
+            # Make sure that if it first failed loading, the proxy is forced to
+            # be reloaded in the GES cache.
+            GES.Asset.needs_reload(GES.UriClip, proxy_uri)
+            GES.Asset.request_async(GES.UriClip, proxy_uri, None,
+                                    self.__assetLoadedCb, asset, transcoder)
+
+        if shadow:
+            # Finish deferred loading for waiting scaled proxy transcoder.
+            for pair in self.__waiting_transcoders:
+                waiting_transcoder, waiting_asset = pair
+                if waiting_transcoder.props.src_uri == transcoder.props.src_uri:
+                    proxy_uri = waiting_transcoder.props.dest_uri.rstrip(ProxyManager.part_suffix)
+                    GES.Asset.needs_reload(GES.UriClip, proxy_uri)
+                    GES.Asset.request_async(GES.UriClip, proxy_uri, None,
+                        self.__assetLoadedCb, waiting_asset, waiting_transcoder)
+
+                    self.__waiting_transcoders.remove(pair)
+                    break
 
         try:
             self.__startTranscoder(self.__pending_transcoders.pop())
@@ -389,6 +521,10 @@ class ProxyManager(GObject.Object, Loggable):
             self.info("Position changed after job cancelled!")
             return
 
+        second_transcoder = self._get_second_transcoder(transcoder)
+        if second_transcoder is not None:
+            position = (position + second_transcoder.props.position) // 2
+
         self._transcoded_durations[asset] = position / Gst.SECOND
 
         duration = transcoder.props.duration
@@ -397,37 +533,80 @@ class ProxyManager(GObject.Object, Loggable):
         if duration > 0 and duration != Gst.CLOCK_TIME_NONE:
             creation_progress = 100 * position / duration
             # Do not set to >= 100 as we need to notify about the proxy first.
+
             asset.creation_progress = max(0, min(creation_progress, 99))
 
         self.__emitProgress(asset, asset.creation_progress)
 
-    def is_asset_queued(self, asset):
+    def _get_second_transcoder(self, transcoder):
+        """Gets the shadow of a scaled proxy or the other way around."""
+        all_transcoders = self.__running_transcoders + self.__pending_transcoders
+        for t in all_transcoders:
+            if t.props.position_update_interval != transcoder.props.position_update_interval \
+                    and t.props.src_uri == transcoder.props.src_uri:
+                return t
+        return None
+
+    def _is_shadow_transcoder(self, transcoder):
+        if transcoder.props.position_update_interval == 1001:
+            return True
+        return False
+
+    def is_asset_queued(self, asset, optimisation=True, scaling=True):
         """Returns whether the specified asset is queued for transcoding.
 
         Args:
             asset (GES.Asset): The asset to check.
+            optimisation(bool): Whether to check optimisation queue
+            scaling(bool): Whether to check scaling queue
 
         Returns:
-            bool: True iff the asset is being transcoded or pending.
+            bool: True if the asset is being transcoded or pending.
         """
         all_transcoders = self.__running_transcoders + self.__pending_transcoders
+        is_queued = False
         for transcoder in all_transcoders:
-            if asset.props.id == transcoder.props.src_uri:
-                return True
+            transcoder_uri = transcoder.props.dest_uri
+            scaling_ext = "." + self.scaled_proxy_extension + ProxyManager.part_suffix
+            optimisation_ext = "." + self.hq_proxy_extension + ProxyManager.part_suffix
 
-        return False
+            scaling_transcoder = transcoder_uri.endswith(scaling_ext)
+            optimisation_transcoder = transcoder_uri.endswith(optimisation_ext)
+
+            if transcoder.props.src_uri == asset.props.id:
+                if optimisation and optimisation_transcoder:
+                    is_queued = True
+                    break
+
+                if scaling and scaling_transcoder:
+                    is_queued = True
+                    break
+
+        return is_queued
 
-    def __createTranscoder(self, asset):
+    def __createTranscoder(self, asset, width=None, height=None, shadow=False):
         self._total_time_to_transcode += asset.get_duration() / Gst.SECOND
         asset_uri = asset.get_id()
-        proxy_uri = self.getProxyUri(asset)
+
+        if width and height:
+            proxy_uri = self.getProxyUri(asset, scaled=True)
+        else:
+            proxy_uri = self.getProxyUri(asset)
 
         dispatcher = GstTranscoder.TranscoderGMainContextSignalDispatcher.new()
-        encoding_profile = self.__getEncodingProfile(self.__encoding_target_file, asset)
+
+        enc_profile = self.__getEncodingProfile(self.__encoding_target_file,
+            asset, width, height)
+
         transcoder = GstTranscoder.Transcoder.new_full(
-            asset_uri, proxy_uri + ".part", encoding_profile,
+            asset_uri, proxy_uri + ProxyManager.part_suffix, enc_profile,
             dispatcher)
-        transcoder.props.position_update_interval = 1000
+
+        if shadow:
+            # Used to identify shadow transcoder
+            transcoder.props.position_update_interval = 1001
+        else:
+            transcoder.props.position_update_interval = 1000
 
         thumbnailbin = Gst.ElementFactory.make("teedthumbnailbin")
         thumbnailbin.props.uri = asset.get_id()
@@ -446,6 +625,7 @@ class ProxyManager(GObject.Object, Loggable):
 
         transcoder.connect("done", self.__transcoderDoneCb, asset)
         transcoder.connect("error", self.__transcoderErrorCb, asset)
+
         if len(self.__running_transcoders) < self.app.settings.numTranscodingJobs:
             self.__startTranscoder(transcoder)
         else:
@@ -467,7 +647,6 @@ class ProxyManager(GObject.Object, Loggable):
                           transcoder.__grefcount__)
                 self.__running_transcoders.remove(transcoder)
                 self.emit("asset-preparing-cancelled", asset)
-                return
 
         for transcoder in self.__pending_transcoders:
             if asset.props.id == transcoder.props.src_uri:
@@ -478,39 +657,70 @@ class ProxyManager(GObject.Object, Loggable):
                 # here, which means it will be stopped.
                 self.__pending_transcoders.remove(transcoder)
                 self.emit("asset-preparing-cancelled", asset)
-                return
 
-    def add_job(self, asset):
+        return
+
+    def add_job(self, asset, scaled=False, shadow=False):
         """Adds a transcoding job for the specified asset if needed.
 
         Args:
             asset (GES.Asset): The asset to be transcoded.
         """
-        if self.is_asset_queued(asset):
-            self.log("Asset already queued for proxying: %s", asset)
-            return
-
         force_proxying = asset.force_proxying
-        if not force_proxying and not self.__assetNeedsTranscoding(asset):
-            self.debug("Not proxying asset (proxying disabled: %s)",
+        # Handle Automatic scaling
+        if self.app.settings.auto_scaling_enabled and not force_proxying \
+                and not shadow and not self.asset_matches_target_res(asset):
+            scaled = True
+
+        # Create shadow proxies for unsupported assets
+        if not self.isAssetFormatWellSupported(asset) and not \
+                self.app.settings.proxyingStrategy == ProxyingStrategy.NOTHING \
+                and not shadow:
+            hq_uri = self.app.proxy_manager.getProxyUri(asset)
+            if not Gio.File.new_for_uri(hq_uri).query_exists(None):
+                self.add_job(asset, shadow=True)
+
+        if scaled:
+            if self.is_asset_queued(asset, optimisation=False):
+                self.log("Asset already queued for scaling: %s", asset)
+                return
+
+        else:
+            if self.is_asset_queued(asset, scaling=False):
+                self.log("Asset already queued for optimization: %s", asset)
+                return
+
+        if not force_proxying:
+            if not self.__assetNeedsTranscoding(asset, scaled):
+                self.debug("Not proxying asset (proxying disabled: %s)",
                        self.proxyingUnsupported)
-            # Make sure to notify we do not need a proxy for that asset.
-            self.emit("proxy-ready", asset, None)
-            return
+                # Make sure to notify we do not need a proxy for that asset.
+                self.emit("proxy-ready", asset, None)
+                return
 
-        proxy_uri = self.getProxyUri(asset)
+        proxy_uri = self.getProxyUri(asset, scaled)
         if Gio.File.new_for_uri(proxy_uri).query_exists(None):
             self.debug("Using proxy already generated: %s", proxy_uri)
             GES.Asset.request_async(GES.UriClip,
-                                    proxy_uri, None,
-                                    self.__assetLoadedCb, asset,
-                                    None)
+                                proxy_uri, None,
+                                self.__assetLoadedCb, asset,
+                                None)
             return
 
-        self.debug("Creating a proxy for %s (strategy: %s, force: %s)",
+        self.debug("Creating a proxy for %s (strategy: %s, force: %s, scaled: %s)",
                    asset.get_id(), self.app.settings.proxyingStrategy,
-                   force_proxying)
-        self.__createTranscoder(asset)
+                   force_proxying, scaled)
+        if scaled:
+            project = self.app.project_manager.current_project
+            w = project.scaled_proxy_width
+            h = project.scaled_proxy_height
+            if not project.has_scaled_proxy_size():
+                project.scaled_proxy_width = w
+                project.scaled_proxy_height = h
+            t_width, t_height = self._scale_asset_resolution(asset, w, h)
+            self.__createTranscoder(asset, width=t_width, height=t_height, shadow=shadow)
+        else:
+            self.__createTranscoder(asset, shadow=shadow)
         return
 
 
diff --git a/pitivi/utils/timeline.py b/pitivi/utils/timeline.py
index ac66b679..5e1048dd 100644
--- a/pitivi/utils/timeline.py
+++ b/pitivi/utils/timeline.py
@@ -23,8 +23,6 @@ from gi.repository import Gst
 from gi.repository import Gtk
 
 from pitivi.utils.loggable import Loggable
-from pitivi.utils.ui import set_children_state_recurse
-from pitivi.utils.ui import unset_children_state_recurse
 
 
 # Selection modes
@@ -129,8 +127,10 @@ class Selection(GObject.Object, Loggable):
             obj.selected.selected = selected
             if obj.ui:
                 if selected:
+                    from pitivi.utils.ui import set_children_state_recurse
                     set_children_state_recurse(obj.ui, Gtk.StateFlags.SELECTED)
                 else:
+                    from pitivi.utils.ui import unset_children_state_recurse
                     unset_children_state_recurse(obj.ui, Gtk.StateFlags.SELECTED)
             for element in obj.get_children(False):
                 if isinstance(obj, GES.BaseEffect) or\
diff --git a/pitivi/utils/ui.py b/pitivi/utils/ui.py
index d6d8a756..f3566214 100644
--- a/pitivi/utils/ui.py
+++ b/pitivi/utils/ui.py
@@ -46,7 +46,6 @@ from pitivi.utils.loggable import doLog
 from pitivi.utils.loggable import ERROR
 from pitivi.utils.loggable import INFO
 from pitivi.utils.misc import path_from_uri
-from pitivi.utils.proxy import get_proxy_target
 
 # Dimensions in pixels
 EXPANDED_SIZE = 65
@@ -427,6 +426,7 @@ def beautify_asset(asset):
     Args:
         asset (GES.Asset): The asset to display.
     """
+    from pitivi.utils.proxy import get_proxy_target
     uri = get_proxy_target(asset).props.id
     path = path_from_uri(uri)
     res = ["<b>" + GLib.markup_escape_text(path) + "</b>"]
@@ -494,6 +494,7 @@ def info_name(info):
         info (GES.Asset or DiscovererInfo): The info to display.
     """
     if isinstance(info, GES.Asset):
+        from pitivi.utils.proxy import get_proxy_target
         filename = urllib.parse.unquote(os.path.basename(get_proxy_target(info).get_id()))
     elif isinstance(info, DiscovererInfo):
         filename = urllib.parse.unquote(os.path.basename(info.get_uri()))
diff --git a/pitivi/utils/widgets.py b/pitivi/utils/widgets.py
index d42b2030..fc440f90 100644
--- a/pitivi/utils/widgets.py
+++ b/pitivi/utils/widgets.py
@@ -214,7 +214,7 @@ class NumericWidget(Gtk.Box, DynamicWidget):
         lower (Optional[int]): The lower limit for this widget.
     """
 
-    def __init__(self, upper=None, lower=None, default=None, adjustment=None):
+    def __init__(self, upper=None, lower=None, default=None, adjustment=None, width_chars=None):
         Gtk.Box.__init__(self)
         DynamicWidget.__init__(self, default)
 
@@ -239,6 +239,8 @@ class NumericWidget(Gtk.Box, DynamicWidget):
         self.adjustment.props.upper = upper
 
         self.spinner = Gtk.SpinButton(adjustment=self.adjustment)
+        if width_chars:
+            self.spinner.props.width_chars = width_chars
         self.pack_start(self.spinner, expand=False, fill=False, padding=0)
         self.spinner.show()
 
diff --git a/tests/test_media_library.py b/tests/test_media_library.py
index dc6c51fc..be4c3fbf 100644
--- a/tests/test_media_library.py
+++ b/tests/test_media_library.py
@@ -18,6 +18,7 @@
 # Boston, MA 02110-1301, USA.
 import os
 import tempfile
+from unittest import mock
 
 from gi.repository import GES
 from gi.repository import Gst
@@ -83,13 +84,13 @@ class BaseTestMediaLibrary(common.TestCase):
                              len(self.samples))
             self.mainloop.quit()
 
-    def _createAssets(self, samples):
+    def _create_assets(self, samples):
         self.samples = samples
         for sample_name in samples:
             self.app.project_manager.current_project.create_asset(
                 common.get_sample_uri(sample_name), GES.UriClip)
 
-    def check_import(self, assets, proxying_strategy=ProxyingStrategy.ALL,
+    def check_import(self, samples, proxying_strategy=ProxyingStrategy.ALL,
                      check_no_transcoding=False):
         self._customSetUp(proxyingStrategy=proxying_strategy,
                           numTranscodingJobs=4,
@@ -99,13 +100,103 @@ class BaseTestMediaLibrary(common.TestCase):
         self.medialibrary._progressbar.connect(
             "notify::fraction", self._progressBarCb)
 
-        self._createAssets(assets)
+        self._create_assets(samples)
         self.mainloop.run()
         self.assertFalse(self.medialibrary._progressbar.props.visible)
 
+    def check_add_proxy(self, asset, scaled=False, w=160, h=120,
+            check_progress=True):
+        self.assertFalse(self.app.proxy_manager.is_proxy_asset(asset))
+
+        # Check the inital state of the asset, nothing should be going on.
+        self.assertNotIn("Proxy creation progress:",
+            self.medialibrary.storemodel[0][medialibrary.COL_INFOTEXT])
+        self.assertIn(
+            self.medialibrary.storemodel[0][medialibrary.COL_THUMB_DECORATOR].state,
+            [medialibrary.AssetThumbnail.NO_PROXY,
+             medialibrary.AssetThumbnail.UNSUPPORTED])
+
+        # Check proxy creation.
+        was_in_progress = False
+
+        project = self.app.project_manager.current_project
+        project.scaled_proxy_width = w
+        project.scaled_proxy_height = h
+
+        def check_set_state(self):
+            old_set_state(self)
+            if self.state == self.IN_PROGRESS:
+                nonlocal was_in_progress
+                was_in_progress = True
+
+        old_set_state = medialibrary.AssetThumbnail._set_state
+        medialibrary.AssetThumbnail._set_state = check_set_state
+        try:
+            project.use_proxies_for_assets([asset], scaled)
+
+            self.assertIn("Proxy creation progress:",
+                self.medialibrary.storemodel[0][medialibrary.COL_INFOTEXT])
+
+            self.mainloop.run(timeout_seconds=10)
+        finally:
+            medialibrary.AssetThumbnail._set_state = old_set_state
+
+        if check_progress:
+            self.assertTrue(was_in_progress)
+
+        # Finally, check the final staus of the asset after proxying.
+        self.assertNotIn("Proxy creation progress:",
+            self.medialibrary.storemodel[0][medialibrary.COL_INFOTEXT])
+        if scaled:
+            self.assertEqual(
+                self.medialibrary.storemodel[0][medialibrary.COL_THUMB_DECORATOR].state,
+                medialibrary.AssetThumbnail.SCALED)
+        else:
+            self.assertEqual(
+                self.medialibrary.storemodel[0][medialibrary.COL_THUMB_DECORATOR].state,
+                medialibrary.AssetThumbnail.PROXIED)
+
+        proxy = self.medialibrary.storemodel[0][medialibrary.COL_ASSET]
+        stream = proxy.get_info().get_video_streams()[0]
+        resolution = [stream.get_width(), stream.get_height()]
+        self.assertEqual(proxy.props.proxy_target.props.id, asset.props.id)
+        if scaled:
+            self.assertEqual(resolution, [w, h])
+
+        return proxy
+
+    def check_disable_proxy(self, proxy, asset, delete=False):
+        self.assertFalse(self.app.proxy_manager.is_proxy_asset(asset))
+        self.assertTrue(self.app.proxy_manager.is_proxy_asset(proxy))
+
+        self.app.project_manager.current_project.disable_proxies_for_assets(
+            [proxy], delete_proxy_file=delete)
+
+        self.assertIsNone(asset.get_proxy())
+        self.assertEqual(self.medialibrary.storemodel[0][medialibrary.COL_URI],
+                         asset.props.id)
+
+        self.assertEqual(os.path.exists(Gst.uri_get_location(proxy.props.id)),
+                         not delete)
+
 
 class TestMediaLibrary(BaseTestMediaLibrary):
 
+    def test_import_dialog_proxy_filter(self):
+        mock_filter = mock.Mock()
+        mock_filter.mime_type = "video/mp4"
+
+        self._customSetUp()
+        mlib = self.medialibrary
+
+        # Test HQ Proxies are filtered
+        mock_filter.uri = "file:///home/user/Videos/video.mp4.2360382.proxy.mkv"
+        self.assertFalse(mlib._filter_unsupported(mock_filter))
+
+        # Test Scaled Proxies are filtered
+        mock_filter.uri = "file:///home/user/Videos/video.mp4.2360382.300x300.scaledproxy.mkv"
+        self.assertFalse(mlib._filter_unsupported(mock_filter))
+
     def stop_using_proxies(self, delete_proxies=False):
         sample_name = "30fps_numeroted_frames_red.mkv"
         self.check_import([sample_name])
@@ -204,7 +295,7 @@ class TestMediaLibrary(BaseTestMediaLibrary):
 
             # Check that the info column notifies the user about progress
             self.assertTrue("Proxy creation progress:" in
-                            self.medialibrary.storemodel[0][medialibrary.COL_INFOTEXT])
+                self.medialibrary.storemodel[0][medialibrary.COL_INFOTEXT])
 
             # Run the mainloop and let _progressBarCb stop it when the proxy is
             # ready
@@ -213,6 +304,114 @@ class TestMediaLibrary(BaseTestMediaLibrary):
             self.assertEqual(asset.creation_progress, 100)
             self.assertEqual(asset.get_proxy(), proxy)
 
+    def test_create_and_delete_scaled_proxy(self):
+        sample_name = "30fps_numeroted_frames_red.mkv"
+        with common.cloned_sample(sample_name):
+            self.check_import([sample_name], proxying_strategy=ProxyingStrategy.NOTHING)
+            asset = self.medialibrary.storemodel[0][medialibrary.COL_ASSET]
+
+            # Create scaled proxy
+            proxy = self.check_add_proxy(asset, scaled=True)
+
+            # Delete scaled proxy
+            self.check_disable_proxy(proxy, asset, delete=True)
+
+    def test_mixed_proxies(self):
+        sample_name = "30fps_numeroted_frames_red.mkv"
+        with common.cloned_sample(sample_name):
+            self.check_import([sample_name], proxying_strategy=ProxyingStrategy.NOTHING)
+            asset = self.medialibrary.storemodel[0][medialibrary.COL_ASSET]
+
+            # Create and disable scaled proxy
+            proxy = self.check_add_proxy(asset, scaled=True)
+            scaled_uri = self.app.proxy_manager.getProxyUri(asset, scaled=True)
+            self.check_disable_proxy(proxy, asset)
+
+            # Create and disable HQ proxy
+            proxy = self.check_add_proxy(asset)
+            hq_uri = self.app.proxy_manager.getProxyUri(asset)
+            self.check_disable_proxy(proxy, asset)
+
+            # Check both files exist
+            self.assertTrue(os.path.exists(Gst.uri_get_location(hq_uri)))
+            self.assertTrue(os.path.exists(Gst.uri_get_location(scaled_uri)))
+
+            # Enable and delete scaled proxy
+            proxy = self.check_add_proxy(asset, scaled=True,
+                check_progress=False)
+            self.check_disable_proxy(proxy, asset, delete=True)
+
+            # Check that only HQ Proxy exists
+            self.assertFalse(os.path.exists(Gst.uri_get_location(scaled_uri)))
+            self.assertTrue(os.path.exists(Gst.uri_get_location(hq_uri)))
+
+            # Enable and delete HQ proxy
+            proxy = self.check_add_proxy(asset, check_progress=False)
+            self.check_disable_proxy(proxy, asset, delete=True)
+
+    def test_regenerate_scaled_proxy(self):
+        sample_name = "30fps_numeroted_frames_red.mkv"
+        with common.cloned_sample(sample_name):
+            self.check_import([sample_name], proxying_strategy=ProxyingStrategy.NOTHING)
+            asset = self.medialibrary.storemodel[0][medialibrary.COL_ASSET]
+            asset_uri = common.get_sample_uri(sample_name)
+
+            # Create scaled proxy
+            proxy = self.check_add_proxy(asset, scaled=True)
+            proxy_uri = self.app.proxy_manager.getProxyUri(asset, scaled=True)
+
+            # Change target resolution and trigger regeneration (1/4 Asset width)
+            self.app.project_manager.current_project.scaled_proxy_width = 80
+            self.app.project_manager.current_project.scaled_proxy_height = 60
+
+            self.app.project_manager.current_project.regenerate_scaled_proxies()
+            self.assertTrue("Proxy creation progress:" in
+                self.medialibrary.storemodel[0][medialibrary.COL_INFOTEXT])
+            self.mainloop.run()
+
+            proxy = self.medialibrary.storemodel[0][medialibrary.COL_ASSET]
+            self.assertNotEqual(proxy.props.id, proxy_uri)
+
+            stream = proxy.get_info().get_video_streams()[0]
+            resolution = [stream.get_width(), stream.get_height()]
+            self.assertEqual(resolution, [80, 60])
+            self.assertEqual(proxy.props.proxy_target.props.id, asset_uri)
+
+            # Delete proxy
+            self.check_disable_proxy(proxy, asset, delete=True)
+            self.assertFalse(os.path.exists(Gst.uri_get_location(proxy_uri)))
+
+    def test_scaled_proxy_for_unsupported_asset(self):
+        sample_name = "1sec_simpsons_trailer.mp4"
+        with common.cloned_sample(sample_name):
+            self.check_import([sample_name], proxying_strategy=ProxyingStrategy.AUTOMATIC)
+            asset = self.medialibrary.storemodel[0][medialibrary.COL_ASSET]
+
+            # Mark all formats as unsupported
+            with mock.patch.object(self.app.proxy_manager,
+                                   "isAssetFormatWellSupported",
+                                   return_value=False):
+                # Create scaled proxy
+                proxy = self.check_add_proxy(asset, scaled=True, w=80, h=34)
+                proxy_uri = self.app.proxy_manager.getProxyUri(asset, scaled=True)
+                self.mainloop.run(until_empty=True)
+
+                # Check that HQ proxy was created
+                hq_uri = self.app.proxy_manager.getProxyUri(asset)
+                self.assertTrue(os.path.exists(Gst.uri_get_location(hq_uri)))
+
+                # Delete scaled proxy
+                self.check_disable_proxy(proxy, asset, delete=True)
+                self.mainloop.run()
+
+                # Check that we revert to HQ proxy
+                proxy = self.medialibrary.storemodel[0][medialibrary.COL_ASSET]
+                proxy_uri = self.app.proxy_manager.getProxyUri(asset, scaled=False)
+                self.assertEqual(proxy.props.id, proxy_uri)
+
+                # Delete HQ Proxy
+                self.check_disable_proxy(proxy, asset, delete=True)
+
     def test_supported_out_of_container_audio(self):
         sample = "mp3_sample.mp3"
         with common.cloned_sample(sample):
diff --git a/tests/test_prefs.py b/tests/test_prefs.py
index 17d568cc..913a3706 100644
--- a/tests/test_prefs.py
+++ b/tests/test_prefs.py
@@ -16,12 +16,26 @@
 # License along with this program; if not, write to the
 # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 # Boston, MA 02110-1301, USA.
+from unittest import mock
+
+from gi.repository import Gtk
+
 from pitivi.dialogs.prefs import PreferencesDialog
 from tests import common
 
 
 class PreferencesDialogTest(common.TestCase):
 
+    def test_dialog_creation(self):
+        """Exercises the dialog creation."""
+        app = common.create_pitivi()
+        with mock.patch.object(Gtk.Dialog, "set_transient_for"):
+            PreferencesDialog(app)
+
+        app.project_manager.new_blank_project()
+        with mock.patch.object(Gtk.Dialog, "set_transient_for"):
+            PreferencesDialog(app)
+
     def testNumeric(self):
         PreferencesDialog.addNumericPreference('numericPreference1',
                                                label="Open Range",
diff --git a/tests/test_project.py b/tests/test_project.py
index 5bc58475..f45236fd 100644
--- a/tests/test_project.py
+++ b/tests/test_project.py
@@ -633,6 +633,40 @@ class TestProjectSettings(common.TestCase):
         project.uri = "file:///tmp/%40%23%24%5E%26%60.xges"
         self.assertEqual(project.name, "@#$^&`")
 
+    def test_scaled_proxy_size(self):
+        app = common.create_pitivi_mock(default_scaled_proxy_width=123,
+                                        default_scaled_proxy_height=456)
+        manager = ProjectManager(app)
+        project = manager.new_blank_project()
+        self.assertFalse(project.has_scaled_proxy_size())
+        self.assertEqual(project.scaled_proxy_width, 123)
+        self.assertEqual(project.scaled_proxy_height, 456)
+
+        with tempfile.NamedTemporaryFile() as f:
+            uri = Gst.filename_to_uri(f.name)
+            manager.saveProject(uri=uri, backup=False)
+            app2 = common.create_pitivi_mock(default_scaled_proxy_width=12,
+                                             default_scaled_proxy_height=45)
+            project2 = ProjectManager(app2).load_project(uri)
+            self.assertFalse(project2.has_scaled_proxy_size())
+            self.assertEqual(project2.scaled_proxy_width, 12)
+            self.assertEqual(project2.scaled_proxy_height, 45)
+
+        project.scaled_proxy_width = 123
+        project.scaled_proxy_height = 456
+        self.assertTrue(project.has_scaled_proxy_size())
+        self.assertEqual(project.scaled_proxy_width, 123)
+        self.assertEqual(project.scaled_proxy_height, 456)
+
+        with tempfile.NamedTemporaryFile() as f:
+            manager.saveProject(uri=uri, backup=False)
+            app2 = common.create_pitivi_mock(default_scaled_proxy_width=1,
+                                             default_scaled_proxy_height=4)
+            project2 = ProjectManager(app2).load_project(uri)
+            self.assertTrue(project2.has_scaled_proxy_size())
+            self.assertEqual(project2.scaled_proxy_width, 123)
+            self.assertEqual(project2.scaled_proxy_height, 456)
+
 
 class TestExportSettings(common.TestCase):
 
diff --git a/tests/test_proxy.py b/tests/test_proxy.py
new file mode 100644
index 00000000..be8f5c61
--- /dev/null
+++ b/tests/test_proxy.py
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+# Pitivi video editor
+# Copyright (c) 2019, Yatin Maan <yatinmaan1 gmail com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+"""Tests for the utils.proxy module."""
+# pylint: disable=protected-access,too-many-arguments
+from unittest import mock
+
+from gi.repository import GES
+
+from tests import common
+
+
+class TestProxyManager(common.TestCase):
+    """Tests for the ProxyManager class."""
+
+    def _check_scale_asset_resolution(self, asset_res, max_res, expected_res):
+        app = common.create_pitivi_mock()
+        manager = app.proxy_manager
+
+        stream = mock.Mock()
+        stream.get_width.return_value = asset_res[0]
+        stream.get_height.return_value = asset_res[1]
+
+        asset = mock.Mock()
+        asset.get_info().get_video_streams.return_value = [stream]
+
+        result = manager._scale_asset_resolution(asset, max_res[0], max_res[1])
+        self.assertEqual(result, expected_res)
+
+    def test_scale_asset_resolution(self):
+        """Checks the _scale_asset_resolution method."""
+        self._check_scale_asset_resolution((1920, 1080), (100, 100), (96, 54))
+        self._check_scale_asset_resolution((1080, 1920), (100, 100), (54, 96))
+        self._check_scale_asset_resolution((1000, 1000), (100, 100), (100, 100))
+
+        # Unscalable resolutions.
+        self._check_scale_asset_resolution((1000, 10), (100, 100), (1000, 10))
+        self._check_scale_asset_resolution((10, 1000), (100, 100), (10, 1000))
+        self._check_scale_asset_resolution((100, 100), (200, 200), (100, 100))
+
+    def _check_getTargetUri(self, proxy_uri, expected_uri):
+        app = common.create_pitivi_mock()
+        manager = app.proxy_manager
+
+        asset = mock.Mock(spec=GES.Asset)
+        asset.props.id = proxy_uri
+
+        result = manager.getTargetUri(asset)
+        self.assertEqual(result, expected_uri)
+
+    def test_getTargetUri(self):
+        """Checks the getTargetUri method."""
+        self._check_getTargetUri("file:///home/filename.ext.size.scaled_res.scaledproxy.mkv",
+                                 "file:///home/filename.ext")
+        self._check_getTargetUri("file:///home/filename.ext.size.proxy.mkv",
+                                 "file:///home/filename.ext")
+        self._check_getTargetUri("file:///home/file.name.mp4.1927006.1280x720.scaledproxy.mkv",
+                                 "file:///home/file.name.mp4")
+        self._check_getTargetUri("file:///home/file.name.mp4.1927006.proxy.mkv",
+                                 "file:///home/file.name.mp4")
+
+    def _check_getProxyUri(self, asset_uri, expected_uri, size=10, scaled=False, scaled_res=(1280, 720)):
+        app = common.create_pitivi_mock()
+        manager = app.proxy_manager
+
+        asset = mock.Mock()
+        asset.get_id.return_value = asset_uri
+        with mock.patch.object(manager, "_scale_asset_resolution") as s_res:
+            s_res.return_value = scaled_res
+            with mock.patch("pitivi.utils.proxy.Gio.File") as gio:
+                gio.new_for_uri.return_value = gio
+                gio.query_info().get_size.return_value = size
+
+                result = manager.getProxyUri(asset, scaled=scaled)
+                self.assertEqual(result, expected_uri)
+
+    def test_getProxyUri(self):
+        """Checks the getProxyUri method."""
+        self._check_getProxyUri("file:///home/file.name.mp4",
+                                "file:///home/file.name.mp4.10.proxy.mkv")
+        self._check_getProxyUri("file:///home/file.name.mp4",
+                                "file:///home/file.name.mp4.10.1280x720.scaledproxy.mkv",
+                                scaled=True)
diff --git a/tests/test_render.py b/tests/test_render.py
index f7c9de70..ac3d93cd 100644
--- a/tests/test_render.py
+++ b/tests/test_render.py
@@ -33,6 +33,7 @@ from pitivi.preset import EncodingTargetManager
 from pitivi.render import Encoders
 from pitivi.render import extension_for_muxer
 from pitivi.timeline.timeline import TimelineContainer
+from pitivi.utils.proxy import ProxyingStrategy
 from pitivi.utils.ui import get_combo_value
 from pitivi.utils.ui import set_combo_value
 from tests import common
@@ -340,6 +341,91 @@ class TestRender(BaseTestMediaLibrary):
             self.assertEqual(video_source.get_child_property("width")[1], 320)
             self.assertEqual(video_source.get_child_property("height")[1], 240)
 
+    # pylint: disable=invalid-name
+    def test_rendering_with_scaled_proxies(self):
+        """Tests rendering with scaled proxies."""
+        sample_name = "30fps_numeroted_frames_red.mkv"
+        with common.cloned_sample(sample_name):
+            self.check_import([sample_name], proxying_strategy=ProxyingStrategy.NOTHING)
+
+            project = self.app.project_manager.current_project
+            proxy_manager = self.app.proxy_manager
+            timeline_container = TimelineContainer(self.app)
+            timeline_container.setProject(project)
+            rendering_asset = None
+
+            asset, = project.list_assets(GES.UriClip)
+            proxy = self.check_add_proxy(asset, scaled=True)
+
+            layer, = project.ges_timeline.get_layers()
+            clip = proxy.extract()
+            layer.add_clip(clip)
+
+            # Patch the function that reverts assets to proxies after rendering.
+            from pitivi.render import RenderDialog
+            old_use_proxy_assets = RenderDialog._use_proxy_assets
+
+            def check_use_proxy_assets(self):
+                nonlocal layer, asset, rendering_asset
+                clip, = layer.get_clips()
+                rendering_asset = clip.get_asset()
+                old_use_proxy_assets(self)
+
+            RenderDialog._use_proxy_assets = check_use_proxy_assets
+            dialog = self.create_rendering_dialog(project)
+            self.render(dialog)
+            self.mainloop.run(until_empty=True)
+            RenderDialog._use_proxy_assets = old_use_proxy_assets
+
+            # Check rendering did not use scaled proxy
+            self.assertFalse(proxy_manager.is_scaled_proxy(rendering_asset))
+            # Check asset was replaced with scaled proxy after rendering
+            self.assertTrue(proxy_manager.is_scaled_proxy(clip.get_asset()))
+
+    # pylint: disable=invalid-name
+    def test_rendering_with_unsupported_asset_scaled_proxies(self):
+        """Tests rendering with scaled proxies."""
+        sample_name = "30fps_numeroted_frames_red.mkv"
+        with common.cloned_sample(sample_name):
+            self.check_import([sample_name], proxying_strategy=ProxyingStrategy.AUTOMATIC)
+
+            project = self.app.project_manager.current_project
+            proxy_manager = self.app.proxy_manager
+            timeline_container = TimelineContainer(self.app)
+            timeline_container.setProject(project)
+            rendering_asset = None
+
+            asset, = project.list_assets(GES.UriClip)
+            with mock.patch.object(proxy_manager,
+                                   "isAssetFormatWellSupported",
+                                   return_value=False):
+                proxy = self.check_add_proxy(asset, scaled=True)
+
+                # Check that HQ proxy was created
+                hq_uri = self.app.proxy_manager.getProxyUri(asset)
+                self.assertTrue(os.path.exists(Gst.uri_get_location(hq_uri)), hq_uri)
+
+                layer, = project.ges_timeline.get_layers()
+                clip = proxy.extract()
+                layer.add_clip(clip)
+
+                def _use_proxy_assets():
+                    nonlocal layer, asset, rendering_asset
+                    clip, = layer.get_clips()
+                    rendering_asset = clip.get_asset()
+                    old_use_proxy_assets()
+
+                dialog = self.create_rendering_dialog(project)
+                old_use_proxy_assets = dialog._use_proxy_assets
+                dialog._use_proxy_assets = _use_proxy_assets
+                self.render(dialog)
+                self.mainloop.run(until_empty=True)
+
+                # Check rendering used HQ proxy
+                self.assertTrue(proxy_manager.is_hq_proxy(rendering_asset))
+                # Check asset was replaced with scaled proxy after rendering
+                self.assertTrue(proxy_manager.is_scaled_proxy(clip.get_asset()))
+
     @skipUnless(*encoding_target_exists("youtube"))
     # pylint: disable=invalid-name
     def test_rendering_with_youtube_profile(self):


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]