🎨 Format codes

This commit is contained in:
OpenWaygate committed 2026-04-06 23:55:05 +08:00
1 parent 5c4ff9d8a3
commit 6cba52bb9b
49 files changed
+1297 -884

No files matched your search

+1 -1
View File
@@ -58,7 +58,7 @@ builds:
mod_timestamp: "{{ .CommitTimestamp }}"
archives:
- formats: ["binary"]
- formats: [ "binary" ]
ids:
- yutu
name_template: "{{ .Binary }}"
+55 -1
View File
@@ -1,3 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" style="background: transparent; background-color: transparent; color-scheme: light dark;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="262px" height="262px" viewBox="0 0 262 262"><defs/><g><g data-cell-id="0"><g data-cell-id="1"><g data-cell-id="rtcpYZbgnlMAwqHNECys-1"><g transform="translate(0.5,0.5)"/><g data-cell-id="WKj6DpxLUBhHQ0AxX3lQ-251"><g transform="translate(0.5,0.5)"><ellipse cx="116.02" cy="61.1" rx="51.015" ry="26.095" fill="none" stroke="none" transform="rotate(75,116.02,61.1)" pointer-events="all"/><path d="M 86.35 37.24 C 93.1 34.39 106.67 35.2 116.15 35.83 C 125.62 36.45 135.4 38.93 143.19 40.99 C 150.98 43.05 159.13 44.56 162.88 48.2 C 166.63 51.83 167.37 57.76 165.68 62.79 C 164 67.83 158.36 74.58 152.76 78.41 C 147.16 82.24 140.39 84.28 132.06 85.77 C 123.74 87.25 112.03 88.71 102.81 87.31 C 93.6 85.91 83.07 81.34 76.78 77.35 C 70.48 73.35 65.96 68.16 65.05 63.35 C 64.13 58.55 66.84 52.63 71.27 48.5 C 75.7 44.37 87.24 40.03 91.6 38.56 C 95.96 37.1 97.25 39.47 97.44 39.7 M 136.92 38.41 C 145.05 39.96 156.04 43.88 161.34 47.4 C 166.64 50.92 169.01 54.76 168.71 59.53 C 168.41 64.3 164.96 71.74 159.54 76 C 154.11 80.25 144.95 83.73 136.14 85.07 C 127.32 86.4 116.21 84.92 106.67 84.01 C 97.12 83.1 85.29 82.47 78.86 79.59 C 72.42 76.71 70.09 71.43 68.07 66.73 C 66.05 62.02 64.11 55.86 66.71 51.37 C 69.32 46.88 76.4 42.25 83.7 39.77 C 91.01 37.29 101.17 36.49 110.54 36.5 C 119.92 36.51 135.55 39.72 139.96 39.81 C 144.38 39.89 137.69 37.11 137.04 36.99" fill="#ff6666" stroke="none" transform="rotate(75,116.02,61.1)" pointer-events="all" style="fill: light-dark(rgb(255, 102, 102), rgb(224, 93, 93));"/></g></g><g data-cell-id="WKj6DpxLUBhHQ0AxX3lQ-252"><g transform="translate(0.5,0.5)"><ellipse cx="154.52" cy="56.86" rx="48.74" ry="26.86" fill="none" stroke="none" transform="rotate(105,154.52,56.86)" pointer-events="all"/><path d="M 173.15 30.29 C 181.69 31.52 190.81 35.99 195.53 39.91 C 200.25 43.83 201.23 48.59 201.46 53.79 C 201.7 59 200.95 66.59 196.94 71.12 C 192.93 75.66 185.43 78.8 177.41 81.01 C 169.39 83.22 157.85 84.64 148.83 84.37 C 139.81 84.1 129.77 82.42 123.29 79.37 C 116.82 76.33 112.83 70.94 109.98 66.12 C 107.12 61.31 104.29 54.97 106.16 50.49 C 108.03 46.01 114.57 42.51 121.2 39.27 C 127.84 36.02 133.79 30.77 145.99 31.01 C 158.2 31.25 184.83 36.76 194.42 40.72 C 204 44.68 204.03 54.47 203.49 54.76 M 140.68 28.92 C 149.33 26.98 161.44 28.51 170.37 29.79 C 179.3 31.07 188.8 32.49 194.28 36.6 C 199.77 40.71 202.73 49.45 203.26 54.45 C 203.79 59.45 200.99 62.31 197.45 66.59 C 193.92 70.87 189.06 77.56 182.06 80.16 C 175.07 82.75 164.82 82.34 155.48 82.17 C 146.15 81.99 133.64 81.17 126.04 79.1 C 118.44 77.04 113.41 74.07 109.9 69.76 C 106.39 65.44 103.35 58.82 104.98 53.24 C 106.62 47.66 113.34 40.46 119.71 36.27 C 126.09 32.08 139.19 29.14 143.21 28.1 C 147.22 27.05 144.2 28.87 143.78 30.01" fill="#ff6666" stroke="none" transform="rotate(105,154.52,56.86)" pointer-events="all" style="fill: light-dark(rgb(255, 102, 102), rgb(224, 93, 93));"/></g></g><g data-cell-id="WKj6DpxLUBhHQ0AxX3lQ-253"><g transform="translate(0.5,0.5)"><ellipse cx="131" cy="158.9" rx="128" ry="99.905" fill="none" stroke="none" pointer-events="all"/><path d="M 69.96 71.02 C 81.27 64.57 96.11 62.75 110.3 61.31 C 124.48 59.86 141.05 60.64 155.08 62.33 C 169.12 64.02 182.39 66.03 194.51 71.47 C 206.63 76.9 217.99 85.62 227.81 94.94 C 237.63 104.26 248.56 116.28 253.43 127.42 C 258.31 138.56 257.71 150.85 257.07 161.79 C 256.43 172.72 254.66 182.63 249.58 193.03 C 244.51 203.43 235.87 215.31 226.63 224.17 C 217.38 233.03 206.65 240.9 194.12 246.19 C 181.6 251.48 165.73 254.31 151.48 255.9 C 137.24 257.48 123.06 257.82 108.68 255.71 C 94.29 253.6 78.14 248.73 65.18 243.24 C 52.21 237.74 40.23 231.55 30.88 222.74 C 21.53 213.92 13.97 201.26 9.06 190.34 C 4.16 179.42 1.06 168.52 1.46 157.2 C 1.87 145.89 6.11 133.3 11.49 122.47 C 16.87 111.64 19.7 102.02 33.74 92.23 C 47.79 82.44 81.11 68.98 95.76 63.76 C 110.4 58.53 121.07 59.53 121.61 60.89 M 40.36 89.69 C 48.42 80.58 59.94 75.33 72.06 70.16 C 84.19 64.99 98.38 59.72 113.1 58.69 C 127.81 57.66 146.15 61.55 160.37 63.97 C 174.59 66.39 185.95 67.28 198.43 73.2 C 210.9 79.12 226.48 89.97 235.22 99.49 C 243.97 109.01 247.2 119.69 250.89 130.34 C 254.58 141 258.04 152.26 257.36 163.41 C 256.68 174.57 251.89 186.96 246.82 197.26 C 241.75 207.55 236.92 216.38 226.92 225.19 C 216.92 234.01 200.65 244.28 186.82 250.14 C 172.99 256 157.8 259.36 143.94 260.36 C 130.08 261.37 118.02 259 103.66 256.16 C 89.3 253.31 70.37 249.76 57.78 243.29 C 45.19 236.82 35.91 226.75 28.11 217.35 C 20.31 207.95 15.18 197.88 10.98 186.88 C 6.79 175.89 2.83 162.83 2.94 151.38 C 3.05 139.93 5.48 128.02 11.63 118.18 C 17.78 108.33 35.27 96.76 39.85 92.3 C 44.43 87.84 37.74 90.33 39.11 91.42" fill="#ff6666" stroke="none" pointer-events="all" style="fill: light-dark(rgb(255, 102, 102), rgb(224, 93, 93));"/></g></g><g data-cell-id="WKj6DpxLULine truncated
<svg xmlns="http://www.w3.org/2000/svg"
style="background: transparent; background-color: transparent; color-scheme: light dark;"
version="1.1" width="262px" height="262px" viewBox="0 0 262 262">
<defs/>
<g>
<g data-cell-id="0">
<g data-cell-id="1">
<g data-cell-id="rtcpYZbgnlMAwqHNECys-1">
<g transform="translate(0.5,0.5)"/>
<g data-cell-id="WKj6DpxLUBhHQ0AxX3lQ-251">
<g transform="translate(0.5,0.5)">
<ellipse cx="116.02" cy="61.1" rx="51.015" ry="26.095" fill="none" stroke="none"
transform="rotate(75,116.02,61.1)" pointer-events="all"/>
<path
d="M 86.35 37.24 C 93.1 34.39 106.67 35.2 116.15 35.83 C 125.62 36.45 135.4 38.93 143.19 40.99 C 150.98 43.05 159.13 44.56 162.88 48.2 C 166.63 51.83 167.37 57.76 165.68 62.79 C 164 67.83 158.36 74.58 152.76 78.41 C 147.16 82.24 140.39 84.28 132.06 85.77 C 123.74 87.25 112.03 88.71 102.81 87.31 C 93.6 85.91 83.07 81.34 76.78 77.35 C 70.48 73.35 65.96 68.16 65.05 63.35 C 64.13 58.55 66.84 52.63 71.27 48.5 C 75.7 44.37 87.24 40.03 91.6 38.56 C 95.96 37.1 97.25 39.47 97.44 39.7 M 136.92 38.41 C 145.05 39.96 156.04 43.88 161.34 47.4 C 166.64 50.92 169.01 54.76 168.71 59.53 C 168.41 64.3 164.96 71.74 159.54 76 C 154.11 80.25 144.95 83.73 136.14 85.07 C 127.32 86.4 116.21 84.92 106.67 84.01 C 97.12 83.1 85.29 82.47 78.86 79.59 C 72.42 76.71 70.09 71.43 68.07 66.73 C 66.05 62.02 64.11 55.86 66.71 51.37 C 69.32 46.88 76.4 42.25 83.7 39.77 C 91.01 37.29 101.17 36.49 110.54 36.5 C 119.92 36.51 135.55 39.72 139.96 39.81 C 144.38 39.89 137.69 37.11 137.04 36.99"
fill="#ff6666" stroke="none" transform="rotate(75,116.02,61.1)" pointer-events="all"
style="fill: light-dark(rgb(255, 102, 102), rgb(224, 93, 93));"/>
</g>
</g>
<g data-cell-id="WKj6DpxLUBhHQ0AxX3lQ-252">
<g transform="translate(0.5,0.5)">
<ellipse cx="154.52" cy="56.86" rx="48.74" ry="26.86" fill="none" stroke="none"
transform="rotate(105,154.52,56.86)" pointer-events="all"/>
<path
d="M 173.15 30.29 C 181.69 31.52 190.81 35.99 195.53 39.91 C 200.25 43.83 201.23 48.59 201.46 53.79 C 201.7 59 200.95 66.59 196.94 71.12 C 192.93 75.66 185.43 78.8 177.41 81.01 C 169.39 83.22 157.85 84.64 148.83 84.37 C 139.81 84.1 129.77 82.42 123.29 79.37 C 116.82 76.33 112.83 70.94 109.98 66.12 C 107.12 61.31 104.29 54.97 106.16 50.49 C 108.03 46.01 114.57 42.51 121.2 39.27 C 127.84 36.02 133.79 30.77 145.99 31.01 C 158.2 31.25 184.83 36.76 194.42 40.72 C 204 44.68 204.03 54.47 203.49 54.76 M 140.68 28.92 C 149.33 26.98 161.44 28.51 170.37 29.79 C 179.3 31.07 188.8 32.49 194.28 36.6 C 199.77 40.71 202.73 49.45 203.26 54.45 C 203.79 59.45 200.99 62.31 197.45 66.59 C 193.92 70.87 189.06 77.56 182.06 80.16 C 175.07 82.75 164.82 82.34 155.48 82.17 C 146.15 81.99 133.64 81.17 126.04 79.1 C 118.44 77.04 113.41 74.07 109.9 69.76 C 106.39 65.44 103.35 58.82 104.98 53.24 C 106.62 47.66 113.34 40.46 119.71 36.27 C 126.09 32.08 139.19 29.14 143.21 28.1 C 147.22 27.05 144.2 28.87 143.78 30.01"
fill="#ff6666" stroke="none" transform="rotate(105,154.52,56.86)" pointer-events="all"
style="fill: light-dark(rgb(255, 102, 102), rgb(224, 93, 93));"/>
</g>
</g>
<g data-cell-id="WKj6DpxLUBhHQ0AxX3lQ-253">
<g transform="translate(0.5,0.5)">
<ellipse cx="131" cy="158.9" rx="128" ry="99.905" fill="none" stroke="none" pointer-events="all"/>
<path
d="M 69.96 71.02 C 81.27 64.57 96.11 62.75 110.3 61.31 C 124.48 59.86 141.05 60.64 155.08 62.33 C 169.12 64.02 182.39 66.03 194.51 71.47 C 206.63 76.9 217.99 85.62 227.81 94.94 C 237.63 104.26 248.56 116.28 253.43 127.42 C 258.31 138.56 257.71 150.85 257.07 161.79 C 256.43 172.72 254.66 182.63 249.58 193.03 C 244.51 203.43 235.87 215.31 226.63 224.17 C 217.38 233.03 206.65 240.9 194.12 246.19 C 181.6 251.48 165.73 254.31 151.48 255.9 C 137.24 257.48 123.06 257.82 108.68 255.71 C 94.29 253.6 78.14 248.73 65.18 243.24 C 52.21 237.74 40.23 231.55 30.88 222.74 C 21.53 213.92 13.97 201.26 9.06 190.34 C 4.16 179.42 1.06 168.52 1.46 157.2 C 1.87 145.89 6.11 133.3 11.49 122.47 C 16.87 111.64 19.7 102.02 33.74 92.23 C 47.79 82.44 81.11 68.98 95.76 63.76 C 110.4 58.53 121.07 59.53 121.61 60.89 M 40.36 89.69 C 48.42 80.58 59.94 75.33 72.06 70.16 C 84.19 64.99 98.38 59.72 113.1 58.69 C 127.81 57.66 146.15 61.55 160.37 63.97 C 174.59 66.39 185.95 67.28 198.43 73.2 C 210.9 79.12 226.48 89.97 235.22 99.49 C 243.97 109.01 247.2 119.69 250.89 130.34 C 254.58 141 258.04 152.26 257.36 163.41 C 256.68 174.57 251.89 186.96 246.82 197.26 C 241.75 207.55 236.92 216.38 226.92 225.19 C 216.92 234.01 200.65 244.28 186.82 250.14 C 172.99 256 157.8 259.36 143.94 260.36 C 130.08 261.37 118.02 259 103.66 256.16 C 89.3 253.31 70.37 249.76 57.78 243.29 C 45.19 236.82 35.91 226.75 28.11 217.35 C 20.31 207.95 15.18 197.88 10.98 186.88 C 6.79 175.89 2.83 162.83 2.94 151.38 C 3.05 139.93 5.48 128.02 11.63 118.18 C 17.78 108.33 35.27 96.76 39.85 92.3 C 44.43 87.84 37.74 90.33 39.11 91.42"
fill="#ff6666" stroke="none" pointer-events="all"
style="fill: light-dark(rgb(255, 102, 102), rgb(224, 93, 93));"/>
</g>
</g>
<g data-cell-id="WKj6DpxLUBhHQ0AxX3lQ-254">
<g transform="translate(0.5,0.5)"
style="filter: drop-shadow(light-dark(rgba(0, 0, 0, 0.25), rgba(237, 237, 237, 0.25)) 2px 3px 2px);">
<path
d="M 91 158.9 L 91 106.17 Q 91 96.17 99.47 101.48 L 182.53 153.59 Q 191 158.9 182.53 164.21 L 99.47 216.32 Q 91 221.63 91 211.63 Z"
fill="none" stroke="none" transform="rotate(5,141,158.9)" pointer-events="all"/>
<path
d="M 91 158.9 C 91.33 139.59 96.98 125.12 91 106.17 C 88.63 100.36 93.18 96.85 99.47 101.48 C 133.23 120.26 158.06 141.62 182.53 153.59 C 186.69 158.39 188.57 161.88 182.53 164.21 C 170.08 175.32 153.15 184.27 99.47 216.32 C 89.99 221.34 94.26 215.61 91 211.63 C 84.43 194.57 92.07 179.32 91 158.9"
fill="#ffb570" stroke="none" transform="rotate(5,141,158.9)" pointer-events="all"
style="fill: light-dark(rgb(255, 181, 112), rgb(126, 63, 3));"/>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

+88 -1
View File
@@ -1,4 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Do not edit this file with editors other than draw.io -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="428" height="207" viewBox="-0.5 -0.5 428 207" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2024-03-13T16:01:24.589Z&quot; agent=&quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36&quot; version=&quot;24.0.4&quot; etag=&quot;Npa-3qv13WmSzfzboc-e&quot; type=&quot;device&quot; scale=&quot;1&quot; border=&quot;1&quot;&gt;&#10; &lt;diagram name=&quot;Page-1&quot; id=&quot;tx2GcPUS3px8lowsv_hs&quot;&gt;&#10; &lt;mxGraphModel dx=&quot;835&quot; dy=&quot;565&quot; grid=&quot;1&quot; gridSize=&quot;10&quot; guides=&quot;1&quot; tooltips=&quot;1&quot; connect=&quot;1&quot; arrows=&quot;1&quot; fold=&quot;1&quot; page=&quot;1&quot; pageScale=&quot;1&quot; pageWidth=&quot;1100&quot; pageHeight=&quot;850&quot; math=&quot;0&quot; shadow=&quot;0&quot;&gt;&#10; &lt;root&gt;&#10; &lt;mxCell id=&quot;0&quot; /&gt;&#10; &lt;mxCell id=&quot;1&quot; parent=&quot;0&quot; /&gt;&#10; &lt;mxCell id=&quot;2&quot; value=&quot;&quot; style=&quot;group&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;360&quot; y=&quot;199.9962463550923&quot; width=&quot;140&quot; height=&quot;198.7737536449077&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;3&quot; value=&quot;&quot; style=&quot;group&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;2&quot;&gt;&#10; &lt;mxGeometry y=&quot;68.77375364490771&quot; width=&quot;140&quot; height=&quot;130&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;4&quot; value=&quot;&quot; style=&quot;group&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;3&quot;&gt;&#10; &lt;mxGeometry width=&quot;139.99999999999997&quot; height=&quot;129.99999999999994&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;5&quot; value=&quot;&quot; style=&quot;ellipse;whiteSpace=wrap;html=1;fillColor=#FF6666;strokeColor=none;sketch=1;curveFitting=1;jiggle=2;shadow=0;fillStyle=solid;&quot; vertex=&quot;1&quot; parent=&quot;4&quot;&gt;&#10; &lt;mxGeometry width=&quot;139.99999999999997&quot; height=&quot;129.99999999999994&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;6&quot; value=&quot;&quot; style=&quot;triangle;whiteSpace=wrap;html=1;fillColor=#FFB570;strokeColor=none;sketch=1;curveFitting=1;jiggle=2;fillStyle=solid;rounded=1;shadow=1;rotation=5;&quot; vertex=&quot;1&quot; parent=&quot;4&quot;&gt;&#10; &lt;mxGeometry x=&quot;52.02&quot; y=&quot;28.96857142857143&quot; width=&quot;61.12&quot; height=&quot;65&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;7&quot; value=&quot;&quot; style=&quot;group&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;2&quot;&gt;&#10; &lt;mxGeometry x=&quot;19.450183927289345&quot; width=&quot;114.73981607271065&quot; height=&quot;96.74375364490771&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;8&quot; value=&quot;&quot; style=&quot;group&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;7&quot;&gt;&#10; &lt;mxGeometry x=&quot;16.10981607271065&quot; y=&quot;19.383753644907713&quot; width=&quot;55&quot; height=&quot;60&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;9&quot; value=&quot;&quot; style=&quot;group&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;8&quot;&gt;&#10; &lt;mxGeometry y=&quot;0.10259520280253653&quot; width=&quot;56.715265723924134&quot; height=&quot;57.54059926327666&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;10&quot; value=&quot;&quot; style=&quot;group;rotation=-30;aspect=fixed;&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;9&quot;&gt;&#10; &lt;mxGeometry x=&quot;15.800297587748455&quot; y=&quot;2.4003365383475392&quot; width=&quot;23.732966783961707&quot; height=&quot;52.73992618658157&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;11&quot; value=&quot;&quot; style=&quot;curved=1;endArrow=none;html=1;rounded=0;endFill=0;fillColor=#FF6666;strokeColor=#FF6666;comic=0;orthogonalLoop=0;ignoreEdge=0;deletable=1;startFill=1;&quot; edge=&quot;1&quot; parent=&quot;10&quot;&gt;&#10; &lt;mxGeometry width=&quot;50&quot; height=&quot;50&quot; relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10; &lt;mxPoint x=&quot;36.390549068741294&quot; y=&quot;45.488186335926606&quot; as=&quot;sourcePoint&quot; /&gt;&#10; &lt;mxPoint x=&quot;36.390549068741294&quot; y=&quot;45.4Line truncated
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1" width="428"
height="207" viewBox="-0.5 -0.5 428 207"
content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2024-03-13T16:01:24.589Z&quot; agent=&quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36&quot; version=&quot;24.0.4&quot; etag=&quot;Npa-3qv13WmSzfzboc-e&quot; type=&quot;device&quot; scale=&quot;1&quot; border=&quot;1&quot;&gt;&#10; &lt;diagram name=&quot;Page-1&quot; id=&quot;tx2GcPUS3px8lowsv_hs&quot;&gt;&#10; &lt;mxGraphModel dx=&quot;835&quot; dy=&quot;565&quot; grid=&quot;1&quot; gridSize=&quot;10&quot; guides=&quot;1&quot; tooltips=&quot;1&quot; connect=&quot;1&quot; arrows=&quot;1&quot; fold=&quot;1&quot; page=&quot;1&quot; pageScale=&quot;1&quot; pageWidth=&quot;1100&quot; pageHeight=&quot;850&quot; math=&quot;0&quot; shadow=&quot;0&quot;&gt;&#10; &lt;root&gt;&#10; &lt;mxCell id=&quot;0&quot; /&gt;&#10; &lt;mxCell id=&quot;1&quot; parent=&quot;0&quot; /&gt;&#10; &lt;mxCell id=&quot;2&quot; value=&quot;&quot; style=&quot;group&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;360&quot; y=&quot;199.9962463550923&quot; width=&quot;140&quot; height=&quot;198.7737536449077&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;3&quot; value=&quot;&quot; style=&quot;group&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;2&quot;&gt;&#10; &lt;mxGeometry y=&quot;68.77375364490771&quot; width=&quot;140&quot; height=&quot;130&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;4&quot; value=&quot;&quot; style=&quot;group&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;3&quot;&gt;&#10; &lt;mxGeometry width=&quot;139.99999999999997&quot; height=&quot;129.99999999999994&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;5&quot; value=&quot;&quot; style=&quot;ellipse;whiteSpace=wrap;html=1;fillColor=#FF6666;strokeColor=none;sketch=1;curveFitting=1;jiggle=2;shadow=0;fillStyle=solid;&quot; vertex=&quot;1&quot; parent=&quot;4&quot;&gt;&#10; &lt;mxGeometry width=&quot;139.99999999999997&quot; height=&quot;129.99999999999994&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;6&quot; value=&quot;&quot; style=&quot;triangle;whiteSpace=wrap;html=1;fillColor=#FFB570;strokeColor=none;sketch=1;curveFitting=1;jiggle=2;fillStyle=solid;rounded=1;shadow=1;rotation=5;&quot; vertex=&quot;1&quot; parent=&quot;4&quot;&gt;&#10; &lt;mxGeometry x=&quot;52.02&quot; y=&quot;28.96857142857143&quot; width=&quot;61.12&quot; height=&quot;65&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;7&quot; value=&quot;&quot; style=&quot;group&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;2&quot;&gt;&#10; &lt;mxGeometry x=&quot;19.450183927289345&quot; width=&quot;114.73981607271065&quot; height=&quot;96.74375364490771&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;8&quot; value=&quot;&quot; style=&quot;group&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;7&quot;&gt;&#10; &lt;mxGeometry x=&quot;16.10981607271065&quot; y=&quot;19.383753644907713&quot; width=&quot;55&quot; height=&quot;60&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;9&quot; value=&quot;&quot; style=&quot;group&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;8&quot;&gt;&#10; &lt;mxGeometry y=&quot;0.10259520280253653&quot; width=&quot;56.715265723924134&quot; height=&quot;57.54059926327666&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;10&quot; value=&quot;&quot; style=&quot;group;rotation=-30;aspect=fixed;&quot; vertex=&quot;1&quot; connectable=&quot;0&quot; parent=&quot;9&quot;&gt;&#10; &lt;mxGeometry x=&quot;15.800297587748455&quot; y=&quot;2.4003365383475392&quot; width=&quot;23.732966783961707&quot; height=&quot;52.73992618658157&quot; as=&quot;geometry&quot; /&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;11&quot; value=&quot;&quot; style=&quot;curved=1;endArrow=none;html=1;rounded=0;endFill=0;fillColor=#FF6666;strokeColor=#FF6666;comic=0;orthogonalLoop=0;ignoreEdge=0;deletable=1;startFill=1;&quot; edge=&quot;1&quot; parent=&quot;10&quot;&gt;&#10; &lt;mxGeometry width=&quot;50&quot; height=&quot;50&quot; relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10; &lt;mxPoint x=&quot;36.390549068741294&quot; y=&quot;45.488186335926606&quot; as=&quot;sourcePoint&quot; /&gt;&#10; &lt;mxPoint x=&quot;36.390549068741294&quot; y=&quot;45.488186335926606&quot; as=&quot;targetPoint&quot; /&gt;&#10; &lt;Array as=&quot;points&quot;&gt;&#10; &lt;mxPoint x=&quot;1Line truncated
<defs>
<filter id="dropShadow">
<feGaussianBlur in="SourceAlpha" stdDeviation="1.7" result="blur"/>
<feOffset in="blur" dx="3" dy="3" result="offsetBlur"/>
<feFlood flood-color="#3D4574" flood-opacity="0.4" result="offsetColor"/>
<feComposite in="offsetColor" in2="offsetBlur" operator="in" result="offsetBlur"/>
<feBlend in="SourceGraphic" in2="offsetBlur"/>
</filter>
</defs>
<g filter="url(#dropShadow)">
<g/>
<g/>
<g/>
<g>
<ellipse cx="71" cy="134.77" rx="69.99999999999999" ry="64.99999999999997" fill="none" stroke="none"
pointer-events="all"/>
<path
d="M 102.19 74.26 C 112.15 77.38 122.28 88.63 128.26 96.82 C 134.23 105.01 136.12 113.74 138.03 123.42 C 139.95 133.11 142.44 145.61 139.76 154.91 C 137.08 164.22 129.87 172.06 121.96 179.26 C 114.06 186.47 102.69 195.14 92.34 198.14 C 82 201.14 70.54 198.92 59.9 197.26 C 49.27 195.6 37.47 194.16 28.53 188.19 C 19.59 182.23 10.56 171 6.29 161.47 C 2.01 151.94 2 140.94 2.89 131 C 3.78 121.05 6.77 110.05 11.62 101.8 C 16.47 93.56 23.06 86.51 31.99 81.53 C 40.92 76.54 54.15 73.39 65.2 71.9 C 76.24 70.42 89.82 70.29 98.28 72.63 C 106.75 74.96 110.99 81.61 115.98 85.92 C 120.97 90.23 128.99 97.96 128.22 98.49 M 59.87 72.84 C 70.08 69.8 80.68 70.54 90.57 72.63 C 100.45 74.71 111.28 78.74 119.17 85.33 C 127.06 91.93 134.77 102.62 137.92 112.17 C 141.07 121.72 139.33 132.61 138.07 142.64 C 136.81 152.66 135.94 164.27 130.35 172.34 C 124.77 180.42 114.21 186.34 104.57 191.08 C 94.92 195.82 83.56 200.79 72.46 200.77 C 61.35 200.75 47.53 195.48 37.93 190.96 C 28.34 186.45 20.7 182.25 14.89 173.67 C 9.09 165.09 4.51 150.27 3.11 139.49 C 1.71 128.71 3.35 117.61 6.5 109.01 C 9.65 100.41 14.2 94.43 22.01 87.88 C 29.81 81.33 47.04 72.9 53.35 69.72 C 59.65 66.54 58.64 68.53 59.85 68.79 C 61.05 69.04 60.73 69.48 60.57 71.23"
fill="#ff6666" stroke="none" pointer-events="all"/>
</g>
<g style="filter: drop-shadow(rgba(0, 0, 0, 0.25) 2px 3px 2px);">
<path
d="M 53.02 131.24 L 53.02 108.74 Q 53.02 98.74 61.85 103.43 L 105.31 126.54 Q 114.14 131.24 105.31 135.93 L 61.85 159.04 Q 53.02 163.74 53.02 153.74 Z"
fill="none" stroke="none" transform="rotate(5,83.58,131.24)" pointer-events="all"/>
<path
d="M 50.78 130.33 L 52.63 107.13 L 53.55 98.54 L 104.71 126.71 L 109.37 129.74 L 101.52 137.41 L 58.08 159.47 L 52.48 157.93 L 53.7 130.74"
fill="#ffb570" stroke="none" transform="rotate(5,83.58,131.24)" pointer-events="all"/>
</g>
<g/>
<g/>
<g/>
<g/>
<g>
<path
d="M 88.75 68.37 Q 67.39 47.93 60.67 43.32 Q 53.94 38.7 48.4 37.39 Q 42.87 36.07 57.11 52.22 Q 71.35 68.37 88.75 68.37"
fill="none" stroke="#ff6666" stroke-miterlimit="10" pointer-events="stroke"/>
</g>
<g>
<path
d="M 93.28 70.77 Q 75.18 74.55 57.63 54.62 Q 40.07 34.68 38.32 31.59 Q 36.56 28.5 54.12 32.97 Q 71.67 37.43 93.28 70.77"
fill="none" stroke="#b0e3e6" stroke-miterlimit="10" pointer-events="stroke"/>
</g>
<g>
<ellipse cx="52.97" cy="47.35" rx="41.975" ry="22.425" fill="none" stroke="none"
transform="rotate(75,52.97,47.35)" pointer-events="all"/>
<path
d="M 56.85 26.81 C 65.69 27.19 76.77 29.03 82.72 31.89 C 88.67 34.76 91.14 39.82 92.55 44.02 C 93.96 48.22 95.25 53.33 91.17 57.09 C 87.1 60.84 76.72 64.67 68.12 66.55 C 59.51 68.44 48.06 69.64 39.54 68.38 C 31.02 67.12 21.44 63.15 17 59.01 C 12.56 54.87 12.23 48.04 12.9 43.53 C 13.58 39.03 15.49 35.15 21.03 31.97 C 26.58 28.79 38.6 25.72 46.18 24.47 C 53.76 23.23 61.34 23.58 66.53 24.49 C 71.72 25.39 77.79 29.18 77.34 29.89 M 75.12 26.3 C 83.03 28.34 88.55 35.69 91.42 39.98 C 94.28 44.27 94.5 48.1 92.3 52.05 C 90.09 56 84.71 60.84 78.19 63.68 C 71.67 66.52 62.54 68.81 53.17 69.11 C 43.79 69.4 28.85 68.31 21.92 65.44 C 14.98 62.58 12.11 56.8 11.56 51.91 C 11.01 47.02 13.96 40.79 18.63 36.12 C 23.31 31.46 31.72 25.96 39.6 23.92 C 47.47 21.87 60.89 22.8 65.89 23.85 C 70.88 24.89 68.83 29.45 69.56 30.17 C 70.29 30.88 70.23 28.26 70.26 28.15"
fill="#ff6666" stroke="none" transform="rotate(75,52.97,47.35)" pointer-events="all"/>
</g>
<g>
<ellipse cx="99.02" cy="47.35" rx="41.975" ry="22.425" fill="none" stroke="none"
transform="rotate(105,99.02,47.35)" pointer-events="all"/>
<path
d="M 99.79 24.65 C 107.81 24.09 117.16 25.64 124.1 28.73 C 131.03 31.81 139.4 38.26 141.38 43.15 C 143.36 48.04 140.34 54.12 135.96 58.06 C 131.58 62.01 123.22 65.16 115.12 66.81 C 107.01 68.47 95.65 68.69 87.33 67.98 C 79.01 67.28 69.94 66.34 65.18 62.56 C 60.43 58.79 58.64 50.04 58.78 45.35 C 58.93 40.66 60.85 38.07 66.04 34.44 C 71.23 30.81 79.66 24.37 89.9 23.56 C 100.13 22.75 119.31 25.96 127.43 29.59 C 135.55 33.22 139.33 45.03 138.6 45.31 M 120.12 31.01 C 126.84 33.25 136.09 36.27 139.13 40.2 C 142.17 44.14 141.25 49.95 138.35 54.63 C 135.46 59.31 129.02 66.08 121.77 68.29 C 114.52 70.49 104.21 68.7 94.85 67.88 C 85.49 67.05 72.11 66.21 65.63 63.33 C 59.15 60.46 56 55.74 55.97 50.61 C 55.94 45.48 60.44 36.57 65.43 32.56 C 70.43 28.54 77.83 26.98 85.93 26.52 C 94.03 26.07 107.97 29.91 114.04 29.83 C 120.11 29.75 121.17 26.54 122.35 26.03 C 123.53 25.52 121.75 25.77 121.12 26.77"
fill="#ff6666" stroke="none" transform="rotate(105,99.02,47.35)" pointer-events="all"/>
</g>
<g>
<rect x="161" y="70.38" width="260" height="90" fill="none" stroke="none" pointer-events="all"/>
</g>
<g>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%"
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"
style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml"
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 115px; margin-left: 291px;">
<div data-drawio-colors="color: rgb(0, 0, 0); "
style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div
style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">
<p style="line-height: 100%;">
<span style="font-family: &quot;Comic Sans MS&quot;; font-size: 118px;">yutu</span>
</p>
</div>
</div>
</div>
</foreignObject>
<image x="171" y="75.5" width="240" height="83"
xlink:href="data:image/png;base64,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 truncated
</switch>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 39 KiB

+7 -7
View File
@@ -2,7 +2,7 @@
"use strict";
const { execFileSync } = require("child_process");
const {execFileSync} = require("child_process");
const os = require("os");
const PLATFORMS = {
@@ -21,7 +21,7 @@ function getBinaryPath() {
if (!pkg) {
throw new Error(
`Unsupported platform: ${platformKey}. ` +
`yutu supports: ${Object.keys(PLATFORMS).join(", ")}`
`yutu supports: ${Object.keys(PLATFORMS).join(", ")}`
);
}
@@ -40,16 +40,16 @@ function getBinaryPath() {
}
throw new Error(
`You installed yutu for ${key} but this platform is ${platformKey}.\n` +
`This can happen when copying node_modules between platforms.\n` +
`Run "npm install" on this platform to fix.`
`This can happen when copying node_modules between platforms.\n` +
`Run "npm install" on this platform to fix.`
);
}
throw new Error(
`The package "${pkg}" could not be found, and is needed by yutu.\n\n` +
`If you installed with npm, make sure you don't use the "--no-optional"\n` +
`or "--omit=optional" flags. The "optionalDependencies" feature is used\n` +
`by yutu to install the correct binary for your platform.`
`If you installed with npm, make sure you don't use the "--no-optional"\n` +
`or "--omit=optional" flags. The "optionalDependencies" feature is used\n` +
`by yutu to install the correct binary for your platform.`
);
}
}
+12 -6
View File
@@ -69,9 +69,12 @@ func (a *Activity) Get() ([]*youtube.Activity, error) {
call = call.RegionCode(a.RegionCode)
}
return common.Paginate(a.Fields, call, func(r *youtube.ActivityListResponse) ([]*youtube.Activity, string) {
return r.Items, r.NextPageToken
}, errGetActivity)
return common.Paginate(
a.Fields, call,
func(r *youtube.ActivityListResponse) ([]*youtube.Activity, string) {
return r.Items, r.NextPageToken
}, errGetActivity,
)
}
func (a *Activity) List(writer io.Writer) error {
@@ -80,9 +83,12 @@ func (a *Activity) List(writer io.Writer) error {
return err
}
common.PrintList(a.Output, activities, writer, table.Row{"ID", "Title", "Type", "Time"}, func(a *youtube.Activity) table.Row {
return table.Row{a.Id, a.Snippet.Title, a.Snippet.Type, a.Snippet.PublishedAt}
})
common.PrintList(
a.Output, activities, writer, table.Row{"ID", "Title", "Type", "Time"},
func(a *youtube.Activity) table.Row {
return table.Row{a.Id, a.Snippet.Title, a.Snippet.Type, a.Snippet.PublishedAt}
},
)
return err
}
+21 -17
View File
@@ -287,23 +287,25 @@ func TestActivity_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "1", "snippet": {"title": "A1"}},
{"id": "2", "snippet": {"title": "A2"}}
],
"nextPageToken": ""
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
a := NewActivity(opts...)
@@ -378,12 +380,14 @@ func TestActivity_List(t *testing.T) {
"nextPageToken": ""
}`
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(mockResponse))
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(mockResponse))
},
),
)
tests := []struct {
name string
+9 -3
View File
@@ -155,7 +155,9 @@ func TestGetCodeFromPrompt_Success(t *testing.T) {
s := NewY2BService(WithIO(in, &out)).(*svc)
code, err := s.getCodeFromPrompt("http://example.com/auth", "http://localhost:8216")
code, err := s.getCodeFromPrompt(
"http://example.com/auth", "http://localhost:8216",
)
if err != nil {
t.Fatalf("getCodeFromPrompt returned error: %v", err)
}
@@ -164,7 +166,9 @@ func TestGetCodeFromPrompt_Success(t *testing.T) {
}
outStr := out.String()
if !strings.Contains(outStr, openBrowserHint[:20]) { // basic sanity check that hint was written
if !strings.Contains(
outStr, openBrowserHint[:20],
) { // basic sanity check that hint was written
t.Fatalf("expected openBrowserHint to be written to out, got %q", outStr)
}
if !strings.Contains(outStr, "After completing the authorization flow") {
@@ -181,7 +185,9 @@ func TestGetCodeFromPrompt_URLDecode(t *testing.T) {
s := NewY2BService(WithIO(in, &out)).(*svc)
code, err := s.getCodeFromPrompt("http://example.com/auth", "http://localhost:8216")
code, err := s.getCodeFromPrompt(
"http://example.com/auth", "http://localhost:8216",
)
if err != nil {
t.Fatalf("getCodeFromPrompt returned error: %v", err)
}
+6 -3
View File
@@ -93,9 +93,12 @@ func (c *Caption) List(writer io.Writer) error {
return err
}
common.PrintList(c.Output, captions, writer, table.Row{"ID", "Video ID", "Name", "Language"}, func(cap *youtube.Caption) table.Row {
return table.Row{cap.Id, cap.Snippet.VideoId, cap.Snippet.Name, cap.Snippet.Language}
})
common.PrintList(
c.Output, captions, writer, table.Row{"ID", "Video ID", "Name", "Language"},
func(cap *youtube.Caption) table.Row {
return table.Row{cap.Id, cap.Snippet.VideoId, cap.Snippet.Name, cap.Snippet.Language}
},
)
return nil
}
+71 -59
View File
@@ -271,22 +271,24 @@ func TestCaption_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "1", "snippet": {"videoId": "video-id"}},
{"id": "2", "snippet": {"videoId": "video-id"}}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
c := NewCaption(opts...)
@@ -306,11 +308,12 @@ func TestCaption_Get(t *testing.T) {
}
func TestCaption_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "caption-1",
@@ -322,9 +325,10 @@ func TestCaption_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
@@ -451,15 +455,17 @@ func TestCaption_Insert(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-caption-id", "snippet": {"videoId": "video-id"}}`))
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-caption-id", "snippet": {"videoId": "video-id"}}`))
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
c := NewCaption(opts...)
@@ -546,25 +552,27 @@ func TestCaption_Update(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "caption-id", "snippet": {"videoId": "video-id", "language": "en"}}
]
}`),
)
} else {
_, _ = w.Write([]byte(`{"id": "caption-id", "snippet": {"videoId": "video-id", "language": "es"}}`))
}
},
))
)
} else {
_, _ = w.Write([]byte(`{"id": "caption-id", "snippet": {"videoId": "video-id", "language": "es"}}`))
}
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
c := NewCaption(opts...)
@@ -633,14 +641,16 @@ func TestCaption_Delete(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
c := NewCaption(opts...)
@@ -740,14 +750,16 @@ func TestCaption_Download(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
_, _ = w.Write([]byte("1\n00:00:01,000 --> 00:00:04,000\nDownloaded Caption"))
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
_, _ = w.Write([]byte("1\n00:00:01,000 --> 00:00:04,000\nDownloaded Caption"))
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
c := NewCaption(opts...)
+18 -12
View File
@@ -82,9 +82,12 @@ func (c *Channel) Get() ([]*youtube.Channel, error) {
call = call.OnBehalfOfContentOwner(c.OnBehalfOfContentOwner)
}
return common.Paginate(c.Fields, call, func(r *youtube.ChannelListResponse) ([]*youtube.Channel, string) {
return r.Items, r.NextPageToken
}, errGetChannel)
return common.Paginate(
c.Fields, call,
func(r *youtube.ChannelListResponse) ([]*youtube.Channel, string) {
return r.Items, r.NextPageToken
}, errGetChannel,
)
}
func (c *Channel) List(writer io.Writer) error {
@@ -93,15 +96,18 @@ func (c *Channel) List(writer io.Writer) error {
return err
}
common.PrintList(c.Output, channels, writer, table.Row{"ID", "Title", "Country"}, func(ch *youtube.Channel) table.Row {
title := ""
country := ""
if ch.Snippet != nil {
title = ch.Snippet.Title
country = ch.Snippet.Country
}
return table.Row{ch.Id, title, country}
})
common.PrintList(
c.Output, channels, writer, table.Row{"ID", "Title", "Country"},
func(ch *youtube.Channel) table.Row {
title := ""
country := ""
if ch.Snippet != nil {
title = ch.Snippet.Title
country = ch.Snippet.Country
}
return table.Row{ch.Id, title, country}
},
)
return err
}
+40 -34
View File
@@ -376,21 +376,23 @@ func TestChannel_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "channel-1", "snippet": {"title": "Channel 1"}}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
c := NewChannel(opts...)
@@ -452,11 +454,12 @@ func TestChannel_Get_Pagination(t *testing.T) {
}
func TestChannel_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "channel-1",
@@ -467,9 +470,10 @@ func TestChannel_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
@@ -578,25 +582,27 @@ func TestChannel_Update(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "channel-id", "snippet": {"title": "Old Title"}}
]
}`),
)
} else {
_, _ = w.Write([]byte(`{"id": "channel-id", "snippet": {"title": "New Title"}}`))
}
},
))
)
} else {
_, _ = w.Write([]byte(`{"id": "channel-id", "snippet": {"title": "New Title"}}`))
}
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
c := NewChannel(opts...)
+3 -1
View File
@@ -64,7 +64,9 @@ func (cb *ChannelBanner) Insert(writer io.Writer) error {
return errors.Join(errInsertChannelBanner, err)
}
common.PrintResult(cb.Output, res, writer, "ChannelBanner inserted: %s\n", res.Url)
common.PrintResult(
cb.Output, res, writer, "ChannelBanner inserted: %s\n", res.Url,
)
return nil
}
+11 -9
View File
@@ -299,15 +299,17 @@ func TestChannelBanner_Insert(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"url": "https://example.com/banner.jpg"}`))
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"url": "https://example.com/banner.jpg"}`))
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
cb := NewChannelBanner(opts...)
+6 -3
View File
@@ -81,9 +81,12 @@ func (cs *ChannelSection) List(writer io.Writer) error {
return err
}
common.PrintList(cs.Output, channelSections, writer, table.Row{"ID", "Channel ID", "Title"}, func(s *youtube.ChannelSection) table.Row {
return table.Row{s.Id, s.Snippet.ChannelId, s.Snippet.Title}
})
common.PrintList(
cs.Output, channelSections, writer, table.Row{"ID", "Channel ID", "Title"},
func(s *youtube.ChannelSection) table.Row {
return table.Row{s.Id, s.Snippet.ChannelId, s.Snippet.Title}
},
)
return nil
}
+33 -27
View File
@@ -221,21 +221,23 @@ func TestChannelSection_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "section-1", "snippet": {"title": "Section 1"}}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
cs := NewChannelSection(opts...)
@@ -258,11 +260,12 @@ func TestChannelSection_Get(t *testing.T) {
}
func TestChannelSection_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "section-1",
@@ -273,9 +276,10 @@ func TestChannelSection_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
@@ -373,14 +377,16 @@ func TestChannelSection_Delete(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
cs := NewChannelSection(opts...)
+34 -24
View File
@@ -24,15 +24,15 @@ var (
type Comment struct {
*common.Fields
AuthorChannelId string `yaml:"author_channel_id" json:"author_channel_id,omitempty"`
CanRate *bool `yaml:"can_rate" json:"can_rate,omitempty"`
ParentId string `yaml:"parent_id" json:"parent_id,omitempty"`
TextFormat string `yaml:"text_format" json:"text_format,omitempty"`
TextOriginal string `yaml:"text_original" json:"text_original,omitempty"`
ModerationStatus string `yaml:"moderation_status" json:"moderation_status,omitempty"`
BanAuthor *bool `yaml:"ban_author" json:"ban_author,omitempty"`
VideoId string `yaml:"video_id" json:"video_id,omitempty"`
ViewerRating string `yaml:"viewer_rating" json:"viewer_rating,omitempty"`
AuthorChannelId string `yaml:"author_channel_id" json:"author_channel_id,omitempty"`
CanRate *bool `yaml:"can_rate" json:"can_rate,omitempty"`
ParentId string `yaml:"parent_id" json:"parent_id,omitempty"`
TextFormat string `yaml:"text_format" json:"text_format,omitempty"`
TextOriginal string `yaml:"text_original" json:"text_original,omitempty"`
ModerationStatus string `yaml:"moderation_status" json:"moderation_status,omitempty"`
BanAuthor *bool `yaml:"ban_author" json:"ban_author,omitempty"`
VideoId string `yaml:"video_id" json:"video_id,omitempty"`
ViewerRating string `yaml:"viewer_rating" json:"viewer_rating,omitempty"`
}
type IComment[T any] interface {
@@ -70,9 +70,12 @@ func (c *Comment) Get() ([]*youtube.Comment, error) {
call = call.TextFormat(c.TextFormat)
}
return common.Paginate(c.Fields, call, func(r *youtube.CommentListResponse) ([]*youtube.Comment, string) {
return r.Items, r.NextPageToken
}, errGetComment)
return common.Paginate(
c.Fields, call,
func(r *youtube.CommentListResponse) ([]*youtube.Comment, string) {
return r.Items, r.NextPageToken
}, errGetComment,
)
}
func (c *Comment) List(writer io.Writer) error {
@@ -81,17 +84,21 @@ func (c *Comment) List(writer io.Writer) error {
return err
}
common.PrintList(c.Output, comments, writer, table.Row{"ID", "Author", "Video ID", "Text Display"}, func(cm *youtube.Comment) table.Row {
author := ""
videoId := ""
textDisplay := ""
if cm.Snippet != nil {
author = cm.Snippet.AuthorDisplayName
videoId = cm.Snippet.VideoId
textDisplay = cm.Snippet.TextDisplay
}
return table.Row{cm.Id, author, videoId, textDisplay}
})
common.PrintList(
c.Output, comments, writer,
table.Row{"ID", "Author", "Video ID", "Text Display"},
func(cm *youtube.Comment) table.Row {
author := ""
videoId := ""
textDisplay := ""
if cm.Snippet != nil {
author = cm.Snippet.AuthorDisplayName
videoId = cm.Snippet.VideoId
textDisplay = cm.Snippet.TextDisplay
}
return table.Row{cm.Id, author, videoId, textDisplay}
},
)
return err
}
@@ -191,7 +198,10 @@ func (c *Comment) SetModerationStatus(writer io.Writer) error {
return errors.Join(errSetModerationStatus, err)
}
common.PrintResult(c.Output, c, writer, "Comment moderation status set to %s: %s\n", c.ModerationStatus, c.Ids)
common.PrintResult(
c.Output, c, writer, "Comment moderation status set to %s: %s\n",
c.ModerationStatus, c.Ids,
)
return nil
}
+86 -72
View File
@@ -55,12 +55,12 @@ func TestNewComment(t *testing.T) {
},
want: &Comment{
Fields: &common.Fields{
Service: svc,
Parts: []string{"id", "snippet"},
Output: "json",
Ids: []string{"comment1", "comment2"},
Service: svc,
Parts: []string{"id", "snippet"},
Output: "json",
Ids: []string{"comment1", "comment2"},
MaxResults: 50,
ChannelId: "channel123",
ChannelId: "channel123",
},
AuthorChannelId: "author123",
CanRate: &canRateTrue,
@@ -242,21 +242,23 @@ func TestComment_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "comment-1", "snippet": {"textOriginal": "Comment 1"}}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
c := NewComment(opts...)
@@ -318,11 +320,12 @@ func TestComment_Get_Pagination(t *testing.T) {
}
func TestComment_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "comment-1",
@@ -334,9 +337,10 @@ func TestComment_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
@@ -440,15 +444,17 @@ func TestComment_Insert(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-comment-id"}`))
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-comment-id"}`))
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
c := NewComment(opts...)
@@ -509,21 +515,23 @@ func TestComment_Update(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
_, _ = w.Write(
[]byte(`{"items": [{"id": "comment-id", "snippet": {"textOriginal": "Old Text"}}]}`),
)
} else {
_, _ = w.Write([]byte(`{"id": "comment-id", "snippet": {"textOriginal": "Updated Text"}}`))
}
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
_, _ = w.Write(
[]byte(`{"items": [{"id": "comment-id", "snippet": {"textOriginal": "Old Text"}}]}`),
)
} else {
_, _ = w.Write([]byte(`{"id": "comment-id", "snippet": {"textOriginal": "Updated Text"}}`))
}
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
c := NewComment(opts...)
@@ -560,14 +568,16 @@ func TestComment_Delete(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
c := NewComment(opts...)
@@ -607,14 +617,16 @@ func TestComment_MarkAsSpam(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
c := NewComment(opts...)
@@ -680,14 +692,16 @@ func TestComment_SetModerationStatus(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
c := NewComment(opts...)
+17 -8
View File
@@ -76,9 +76,12 @@ func (c *CommentThread) Get() ([]*youtube.CommentThread, error) {
call = call.VideoId(c.VideoId)
}
return common.Paginate(c.Fields, call, func(r *youtube.CommentThreadListResponse) ([]*youtube.CommentThread, string) {
return r.Items, r.NextPageToken
}, errGetCommentThread)
return common.Paginate(
c.Fields, call,
func(r *youtube.CommentThreadListResponse) ([]*youtube.CommentThread, string) {
return r.Items, r.NextPageToken
}, errGetCommentThread,
)
}
func (c *CommentThread) List(writer io.Writer) error {
@@ -87,10 +90,14 @@ func (c *CommentThread) List(writer io.Writer) error {
return err
}
common.PrintList(c.Output, commentThreads, writer, table.Row{"ID", "Author", "Video ID", "Text Display"}, func(cot *youtube.CommentThread) table.Row {
snippet := cot.Snippet.TopLevelComment.Snippet
return table.Row{cot.Id, snippet.AuthorDisplayName, snippet.VideoId, snippet.TextDisplay}
})
common.PrintList(
c.Output, commentThreads, writer,
table.Row{"ID", "Author", "Video ID", "Text Display"},
func(cot *youtube.CommentThread) table.Row {
snippet := cot.Snippet.TopLevelComment.Snippet
return table.Row{cot.Id, snippet.AuthorDisplayName, snippet.VideoId, snippet.TextDisplay}
},
)
return err
}
@@ -119,7 +126,9 @@ func (c *CommentThread) Insert(writer io.Writer) error {
return errors.Join(errInsertCommentThread, err)
}
common.PrintResult(c.Output, res, writer, "CommentThread inserted: %s\n", res.Id)
common.PrintResult(
c.Output, res, writer, "CommentThread inserted: %s\n", res.Id,
)
return nil
}
+34 -28
View File
@@ -272,21 +272,23 @@ func TestCommentThread_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "thread-1", "snippet": {"topLevelComment": {"snippet": {"textDisplay": "Comment 1"}}}}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
c := NewCommentThread(opts...)
@@ -348,11 +350,12 @@ func TestCommentThread_Get_Pagination(t *testing.T) {
}
func TestCommentThread_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "thread-1",
@@ -368,9 +371,10 @@ func TestCommentThread_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
@@ -472,15 +476,17 @@ func TestCommentThread_Insert(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-thread-id"}`))
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-thread-id"}`))
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
c := NewCommentThread(opts...)
+7 -4
View File
@@ -60,14 +60,17 @@ func (i *I18nLanguage) List(writer io.Writer) error {
return err
}
common.PrintList(i.Output, languages, writer, table.Row{"ID", "Hl", "Name"}, func(l *youtube.I18nLanguage) table.Row {
return table.Row{l.Id, l.Snippet.Hl, l.Snippet.Name}
})
common.PrintList(
i.Output, languages, writer, table.Row{"ID", "Hl", "Name"},
func(l *youtube.I18nLanguage) table.Row {
return table.Row{l.Id, l.Snippet.Hl, l.Snippet.Name}
},
)
return nil
}
var (
WithHl = common.WithHl[*I18nLanguage]
WithHl = common.WithHl[*I18nLanguage]
WithParts = common.WithParts[*I18nLanguage]
WithOutput = common.WithOutput[*I18nLanguage]
WithService = common.WithService[*I18nLanguage]
+23 -19
View File
@@ -103,17 +103,19 @@ func TestI18nLanguage_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{"items": [{"id": "en", "snippet": {"hl": "en", "name": "English"}}]}`),
)
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{"items": [{"id": "en", "snippet": {"hl": "en", "name": "English"}}]}`),
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
i := NewI18nLanguage(opts...)
@@ -133,11 +135,12 @@ func TestI18nLanguage_Get(t *testing.T) {
}
func TestI18nLanguage_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "en",
@@ -148,9 +151,10 @@ func TestI18nLanguage_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
+7 -4
View File
@@ -58,14 +58,17 @@ func (i *I18nRegion) List(writer io.Writer) error {
return err
}
common.PrintList(i.Output, regions, writer, table.Row{"ID", "Gl", "Name"}, func(r *youtube.I18nRegion) table.Row {
return table.Row{r.Id, r.Snippet.Gl, r.Snippet.Name}
})
common.PrintList(
i.Output, regions, writer, table.Row{"ID", "Gl", "Name"},
func(r *youtube.I18nRegion) table.Row {
return table.Row{r.Id, r.Snippet.Gl, r.Snippet.Name}
},
)
return nil
}
var (
WithHl = common.WithHl[*I18nRegion]
WithHl = common.WithHl[*I18nRegion]
WithParts = common.WithParts[*I18nRegion]
WithOutput = common.WithOutput[*I18nRegion]
WithService = common.WithService[*I18nRegion]
+23 -19
View File
@@ -103,17 +103,19 @@ func TestI18nRegion_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{"items": [{"id": "US", "snippet": {"gl": "US", "name": "United States"}}]}`),
)
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{"items": [{"id": "US", "snippet": {"gl": "US", "name": "United States"}}]}`),
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
i := NewI18nRegion(opts...)
@@ -133,11 +135,12 @@ func TestI18nRegion_Get(t *testing.T) {
}
func TestI18nRegion_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "US",
@@ -148,9 +151,10 @@ func TestI18nRegion_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
+12 -6
View File
@@ -53,9 +53,12 @@ func (m *Member) Get() ([]*youtube.Member, error) {
call = call.Mode(m.Mode)
}
return common.Paginate(m.Fields, call, func(r *youtube.MemberListResponse) ([]*youtube.Member, string) {
return r.Items, r.NextPageToken
}, errGetMember)
return common.Paginate(
m.Fields, call,
func(r *youtube.MemberListResponse) ([]*youtube.Member, string) {
return r.Items, r.NextPageToken
}, errGetMember,
)
}
func (m *Member) List(writer io.Writer) error {
@@ -64,9 +67,12 @@ func (m *Member) List(writer io.Writer) error {
return err
}
common.PrintList(m.Output, members, writer, table.Row{"Channel ID", "Display Name"}, func(m *youtube.Member) table.Row {
return table.Row{m.Snippet.MemberDetails.ChannelId, m.Snippet.MemberDetails.DisplayName}
})
common.PrintList(
m.Output, members, writer, table.Row{"Channel ID", "Display Name"},
func(m *youtube.Member) table.Row {
return table.Row{m.Snippet.MemberDetails.ChannelId, m.Snippet.MemberDetails.DisplayName}
},
)
return err
}
+23 -19
View File
@@ -192,14 +192,15 @@ func TestMember_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"snippet": {
@@ -211,9 +212,10 @@ func TestMember_Get(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
m := NewMember(opts...)
@@ -273,11 +275,12 @@ func TestMember_Get_Pagination(t *testing.T) {
}
func TestMember_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"snippet": {
@@ -289,9 +292,10 @@ func TestMember_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
+6 -3
View File
@@ -58,9 +58,12 @@ func (m *MembershipsLevel) List(writer io.Writer) error {
return err
}
common.PrintList(m.Output, levels, writer, table.Row{"ID", "Display Name"}, func(ml *youtube.MembershipsLevel) table.Row {
return table.Row{ml.Id, ml.Snippet.LevelDetails.DisplayName}
})
common.PrintList(
m.Output, levels, writer, table.Row{"ID", "Display Name"},
func(ml *youtube.MembershipsLevel) table.Row {
return table.Row{ml.Id, ml.Snippet.LevelDetails.DisplayName}
},
)
return nil
}
+20 -16
View File
@@ -67,11 +67,12 @@ func TestNewMembershipsLevel(t *testing.T) {
}
func TestMembershipsLevel_Get(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "level-1",
@@ -83,9 +84,10 @@ func TestMembershipsLevel_Get(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
m := NewMembershipsLevel(WithService(svc))
got, err := m.Get()
@@ -98,11 +100,12 @@ func TestMembershipsLevel_Get(t *testing.T) {
}
func TestMembershipsLevel_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "level-1",
@@ -114,9 +117,10 @@ func TestMembershipsLevel_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
+18 -12
View File
@@ -75,9 +75,12 @@ func (p *Playlist) Get() ([]*youtube.Playlist, error) {
call = call.OnBehalfOfContentOwnerChannel(p.OnBehalfOfContentOwnerChannel)
}
return common.Paginate(p.Fields, call, func(r *youtube.PlaylistListResponse) ([]*youtube.Playlist, string) {
return r.Items, r.NextPageToken
}, errGetPlaylist)
return common.Paginate(
p.Fields, call,
func(r *youtube.PlaylistListResponse) ([]*youtube.Playlist, string) {
return r.Items, r.NextPageToken
}, errGetPlaylist,
)
}
func (p *Playlist) List(writer io.Writer) error {
@@ -86,15 +89,18 @@ func (p *Playlist) List(writer io.Writer) error {
return err
}
common.PrintList(p.Output, playlists, writer, table.Row{"ID", "Channel ID", "Title"}, func(pl *youtube.Playlist) table.Row {
channelId := ""
title := ""
if pl.Snippet != nil {
channelId = pl.Snippet.ChannelId
title = pl.Snippet.Title
}
return table.Row{pl.Id, channelId, title}
})
common.PrintList(
p.Output, playlists, writer, table.Row{"ID", "Channel ID", "Title"},
func(pl *youtube.Playlist) table.Row {
channelId := ""
title := ""
if pl.Snippet != nil {
channelId = pl.Snippet.ChannelId
title = pl.Snippet.Title
}
return table.Row{pl.Id, channelId, title}
},
)
return err
}
+61 -51
View File
@@ -286,21 +286,23 @@ func TestPlaylist_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "playlist-1", "snippet": {"title": "Playlist 1"}}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
p := NewPlaylist(opts...)
@@ -362,11 +364,12 @@ func TestPlaylist_Get_Pagination(t *testing.T) {
}
func TestPlaylist_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "playlist-1",
@@ -377,9 +380,10 @@ func TestPlaylist_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
@@ -461,15 +465,17 @@ func TestPlaylist_Insert(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-playlist-id"}`))
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-playlist-id"}`))
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
p := NewPlaylist(opts...)
@@ -575,25 +581,27 @@ func TestPlaylist_Update(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "playlist-id", "snippet": {"title": "Old Title"}, "status": {"privacyStatus": "public"}}
]
}`),
)
} else {
_, _ = w.Write([]byte(`{"id": "playlist-id", "snippet": {"title": "Updated Title"}}`))
}
},
))
)
} else {
_, _ = w.Write([]byte(`{"id": "playlist-id", "snippet": {"title": "Updated Title"}}`))
}
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
p := NewPlaylist(opts...)
@@ -649,14 +657,16 @@ func TestPlaylist_Delete(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
p := NewPlaylist(opts...)
+19 -8
View File
@@ -68,9 +68,12 @@ func (pi *PlaylistImage) Get() ([]*youtube.PlaylistImage, error) {
call = call.OnBehalfOfContentOwnerChannel(pi.OnBehalfOfContentOwnerChannel)
}
return common.Paginate(pi.Fields, call, func(r *youtube.PlaylistImageListResponse) ([]*youtube.PlaylistImage, string) {
return r.Items, r.NextPageToken
}, errGetPlaylistImage)
return common.Paginate(
pi.Fields, call,
func(r *youtube.PlaylistImageListResponse) ([]*youtube.PlaylistImage, string) {
return r.Items, r.NextPageToken
}, errGetPlaylistImage,
)
}
func (pi *PlaylistImage) List(writer io.Writer) error {
@@ -79,9 +82,13 @@ func (pi *PlaylistImage) List(writer io.Writer) error {
return err
}
common.PrintList(pi.Output, playlistImages, writer, table.Row{"ID", "Kind", "Playlist ID", "Type"}, func(img *youtube.PlaylistImage) table.Row {
return table.Row{img.Id, img.Kind, img.Snippet.PlaylistId, img.Snippet.Type}
})
common.PrintList(
pi.Output, playlistImages, writer,
table.Row{"ID", "Kind", "Playlist ID", "Type"},
func(img *youtube.PlaylistImage) table.Row {
return table.Row{img.Id, img.Kind, img.Snippet.PlaylistId, img.Snippet.Type}
},
)
return err
}
@@ -121,7 +128,9 @@ func (pi *PlaylistImage) Insert(writer io.Writer) error {
return errors.Join(errInsertPlaylistImage, err)
}
common.PrintResult(pi.Output, res, writer, "PlaylistImage inserted: %s\n", res.Id)
common.PrintResult(
pi.Output, res, writer, "PlaylistImage inserted: %s\n", res.Id,
)
return nil
}
@@ -173,7 +182,9 @@ func (pi *PlaylistImage) Update(writer io.Writer) error {
return errors.Join(errUpdatePlaylistImage, err)
}
common.PrintResult(pi.Output, res, writer, "PlaylistImage updated: %s\n", res.Id)
common.PrintResult(
pi.Output, res, writer, "PlaylistImage updated: %s\n", res.Id,
)
return nil
}
+61 -51
View File
@@ -214,21 +214,23 @@ func TestPlaylistImage_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "image-1", "snippet": {"type": "default"}}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
pi := NewPlaylistImage(opts...)
@@ -290,11 +292,12 @@ func TestPlaylistImage_Get_Pagination(t *testing.T) {
}
func TestPlaylistImage_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "image-1",
@@ -306,9 +309,10 @@ func TestPlaylistImage_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
@@ -470,15 +474,17 @@ func TestPlaylistImage_Insert(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-image-id"}`))
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-image-id"}`))
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
pi := NewPlaylistImage(opts...)
@@ -567,25 +573,27 @@ func TestPlaylistImage_Update(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "image-id", "snippet": {"type": "default"}}
]
}`),
)
} else {
_, _ = w.Write([]byte(`{"id": "image-id", "snippet": {"type": "hero"}}`))
}
},
))
)
} else {
_, _ = w.Write([]byte(`{"id": "image-id", "snippet": {"type": "hero"}}`))
}
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
pi := NewPlaylistImage(opts...)
@@ -646,14 +654,16 @@ func TestPlaylistImage_Delete(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
pi := NewPlaylistImage(opts...)
+34 -23
View File
@@ -69,9 +69,12 @@ func (pi *PlaylistItem) Get() ([]*youtube.PlaylistItem, error) {
call = call.VideoId(pi.VideoId)
}
return common.Paginate(pi.Fields, call, func(r *youtube.PlaylistItemListResponse) ([]*youtube.PlaylistItem, string) {
return r.Items, r.NextPageToken
}, errGetPlaylistItem)
return common.Paginate(
pi.Fields, call,
func(r *youtube.PlaylistItemListResponse) ([]*youtube.PlaylistItem, string) {
return r.Items, r.NextPageToken
}, errGetPlaylistItem,
)
}
func (pi *PlaylistItem) List(writer io.Writer) error {
@@ -80,26 +83,30 @@ func (pi *PlaylistItem) List(writer io.Writer) error {
return err
}
common.PrintList(pi.Output, playlistItems, writer, table.Row{"ID", "Title", "Kind", "Resource ID"}, func(item *youtube.PlaylistItem) table.Row {
title := ""
kind := ""
resourceId := ""
if item.Snippet != nil {
title = item.Snippet.Title
if item.Snippet.ResourceId != nil {
kind = item.Snippet.ResourceId.Kind
switch kind {
case "youtube#video":
resourceId = item.Snippet.ResourceId.VideoId
case "youtube#channel":
resourceId = item.Snippet.ResourceId.ChannelId
case "youtube#playlist":
resourceId = item.Snippet.ResourceId.PlaylistId
common.PrintList(
pi.Output, playlistItems, writer,
table.Row{"ID", "Title", "Kind", "Resource ID"},
func(item *youtube.PlaylistItem) table.Row {
title := ""
kind := ""
resourceId := ""
if item.Snippet != nil {
title = item.Snippet.Title
if item.Snippet.ResourceId != nil {
kind = item.Snippet.ResourceId.Kind
switch kind {
case "youtube#video":
resourceId = item.Snippet.ResourceId.VideoId
case "youtube#channel":
resourceId = item.Snippet.ResourceId.ChannelId
case "youtube#playlist":
resourceId = item.Snippet.ResourceId.PlaylistId
}
}
}
}
return table.Row{item.Id, title, kind, resourceId}
})
return table.Row{item.Id, title, kind, resourceId}
},
)
return err
}
@@ -151,7 +158,9 @@ func (pi *PlaylistItem) Insert(writer io.Writer) error {
return errors.Join(errInsertPlaylistItem, err)
}
common.PrintResult(pi.Output, res, writer, "Playlist Item inserted: %s\n", res.Id)
common.PrintResult(
pi.Output, res, writer, "Playlist Item inserted: %s\n", res.Id,
)
return nil
}
@@ -192,7 +201,9 @@ func (pi *PlaylistItem) Update(writer io.Writer) error {
return errors.Join(errUpdatePlaylistItem, err)
}
common.PrintResult(pi.Output, res, writer, "Playlist Item updated: %s\n", res.Id)
common.PrintResult(
pi.Output, res, writer, "Playlist Item updated: %s\n", res.Id,
)
return nil
}
+61 -51
View File
@@ -250,21 +250,23 @@ func TestPlaylistItem_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "item-1", "snippet": {"title": "Item 1", "resourceId": {"kind": "youtube#video", "videoId": "video-1"}}}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
pi := NewPlaylistItem(opts...)
@@ -326,11 +328,12 @@ func TestPlaylistItem_Get_Pagination(t *testing.T) {
}
func TestPlaylistItem_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "item-1",
@@ -344,9 +347,10 @@ func TestPlaylistItem_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
@@ -520,15 +524,17 @@ func TestPlaylistItem_Insert(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-item-id"}`))
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-item-id"}`))
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
pi := NewPlaylistItem(opts...)
@@ -614,25 +620,27 @@ func TestPlaylistItem_Update(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "item-id", "snippet": {"title": "Old Title"}, "status": {"privacyStatus": "public"}}
]
}`),
)
} else {
_, _ = w.Write([]byte(`{"id": "item-id", "snippet": {"title": "Updated Title"}}`))
}
},
))
)
} else {
_, _ = w.Write([]byte(`{"id": "item-id", "snippet": {"title": "Updated Title"}}`))
}
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
pi := NewPlaylistItem(opts...)
@@ -690,14 +698,16 @@ func TestPlaylistItem_Delete(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
pi := NewPlaylistItem(opts...)
+21 -15
View File
@@ -151,9 +151,12 @@ func (s *Search) Get() ([]*youtube.SearchResult, error) {
call = call.VideoType(s.VideoType)
}
return common.Paginate(s.Fields, call, func(r *youtube.SearchListResponse) ([]*youtube.SearchResult, string) {
return r.Items, r.NextPageToken
}, errGetSearch)
return common.Paginate(
s.Fields, call,
func(r *youtube.SearchListResponse) ([]*youtube.SearchResult, string) {
return r.Items, r.NextPageToken
}, errGetSearch,
)
}
func (s *Search) List(writer io.Writer) error {
@@ -162,18 +165,21 @@ func (s *Search) List(writer io.Writer) error {
return err
}
common.PrintList(s.Output, results, writer, table.Row{"Kind", "Title", "Resource ID"}, func(r *youtube.SearchResult) table.Row {
var resourceId string
switch r.Id.Kind {
case "youtube#video":
resourceId = r.Id.VideoId
case "youtube#channel":
resourceId = r.Id.ChannelId
case "youtube#playlist":
resourceId = r.Id.PlaylistId
}
return table.Row{r.Id.Kind, r.Snippet.Title, resourceId}
})
common.PrintList(
s.Output, results, writer, table.Row{"Kind", "Title", "Resource ID"},
func(r *youtube.SearchResult) table.Row {
var resourceId string
switch r.Id.Kind {
case "youtube#video":
resourceId = r.Id.VideoId
case "youtube#channel":
resourceId = r.Id.ChannelId
case "youtube#playlist":
resourceId = r.Id.PlaylistId
}
return table.Row{r.Id.Kind, r.Snippet.Title, resourceId}
},
)
return err
}
+26 -22
View File
@@ -236,9 +236,9 @@ func TestNewSearch(t *testing.T) {
},
},
want: &Search{
Fields: &common.Fields{MaxResults: 25},
Q: "golang tutorial",
Order: "date",
Fields: &common.Fields{MaxResults: 25},
Q: "golang tutorial",
Order: "date",
RegionCode: "UK",
Types: []string{"video"},
},
@@ -412,21 +412,23 @@ func TestSearch_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"id": {"videoId": "video-1"}, "snippet": {"title": "Video 1"}}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
s := NewSearch(opts...)
@@ -486,11 +488,12 @@ func TestSearch_Get_Pagination(t *testing.T) {
}
func TestSearch_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": {
@@ -503,9 +506,10 @@ func TestSearch_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
+25 -16
View File
@@ -83,9 +83,12 @@ func (s *Subscription) Get() ([]*youtube.Subscription, error) {
call = call.Order(s.Order)
}
return common.Paginate(s.Fields, call, func(r *youtube.SubscriptionListResponse) ([]*youtube.Subscription, string) {
return r.Items, r.NextPageToken
}, errGetSubscription)
return common.Paginate(
s.Fields, call,
func(r *youtube.SubscriptionListResponse) ([]*youtube.Subscription, string) {
return r.Items, r.NextPageToken
}, errGetSubscription,
)
}
func (s *Subscription) List(writer io.Writer) error {
@@ -94,18 +97,22 @@ func (s *Subscription) List(writer io.Writer) error {
return err
}
common.PrintList(s.Output, subscriptions, writer, table.Row{"ID", "Kind", "Resource ID", "Channel Title"}, func(sub *youtube.Subscription) table.Row {
var resourceId string
switch sub.Snippet.ResourceId.Kind {
case "youtube#video":
resourceId = sub.Snippet.ResourceId.VideoId
case "youtube#channel":
resourceId = sub.Snippet.ResourceId.ChannelId
case "youtube#playlist":
resourceId = sub.Snippet.ResourceId.PlaylistId
}
return table.Row{sub.Id, sub.Snippet.ResourceId.Kind, resourceId, sub.Snippet.Title}
})
common.PrintList(
s.Output, subscriptions, writer,
table.Row{"ID", "Kind", "Resource ID", "Channel Title"},
func(sub *youtube.Subscription) table.Row {
var resourceId string
switch sub.Snippet.ResourceId.Kind {
case "youtube#video":
resourceId = sub.Snippet.ResourceId.VideoId
case "youtube#channel":
resourceId = sub.Snippet.ResourceId.ChannelId
case "youtube#playlist":
resourceId = sub.Snippet.ResourceId.PlaylistId
}
return table.Row{sub.Id, sub.Snippet.ResourceId.Kind, resourceId, sub.Snippet.Title}
},
)
return err
}
@@ -130,7 +137,9 @@ func (s *Subscription) Insert(writer io.Writer) error {
return errors.Join(errInsertSubscription, err)
}
common.PrintResult(s.Output, res, writer, "Subscription inserted: %s\n", res.Id)
common.PrintResult(
s.Output, res, writer, "Subscription inserted: %s\n", res.Id,
)
return nil
}
+44 -36
View File
@@ -315,21 +315,23 @@ func TestSubscription_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "sub-1", "snippet": {"title": "Subscription 1"}}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
s := NewSubscription(opts...)
@@ -391,11 +393,12 @@ func TestSubscription_Get_Pagination(t *testing.T) {
}
func TestSubscription_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "sub-1",
@@ -409,9 +412,10 @@ func TestSubscription_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
@@ -533,15 +537,17 @@ func TestSubscription_Insert(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-sub-id"}`))
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-sub-id"}`))
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
s := NewSubscription(opts...)
@@ -580,14 +586,16 @@ func TestSubscription_Delete(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
s := NewSubscription(opts...)
+12 -6
View File
@@ -44,9 +44,12 @@ func (s *SuperChatEvent) Get() ([]*youtube.SuperChatEvent, error) {
call = call.Hl(s.Hl)
}
return common.Paginate(s.Fields, call, func(r *youtube.SuperChatEventListResponse) ([]*youtube.SuperChatEvent, string) {
return r.Items, r.NextPageToken
}, errGetSuperChatEvent)
return common.Paginate(
s.Fields, call,
func(r *youtube.SuperChatEventListResponse) ([]*youtube.SuperChatEvent, string) {
return r.Items, r.NextPageToken
}, errGetSuperChatEvent,
)
}
func (s *SuperChatEvent) List(writer io.Writer) error {
@@ -55,9 +58,12 @@ func (s *SuperChatEvent) List(writer io.Writer) error {
return err
}
common.PrintList(s.Output, events, writer, table.Row{"ID", "Amount", "Comment", "Supporter"}, func(e *youtube.SuperChatEvent) table.Row {
return table.Row{e.Id, e.Snippet.DisplayString, e.Snippet.CommentText, e.Snippet.SupporterDetails.DisplayName}
})
common.PrintList(
s.Output, events, writer, table.Row{"ID", "Amount", "Comment", "Supporter"},
func(e *youtube.SuperChatEvent) table.Row {
return table.Row{e.Id, e.Snippet.DisplayString, e.Snippet.CommentText, e.Snippet.SupporterDetails.DisplayName}
},
)
return err
}
+23 -19
View File
@@ -151,21 +151,23 @@ func TestSuperChatEvent_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "event-1", "snippet": {"displayString": "¥500"}}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
s := NewSuperChatEvent(opts...)
@@ -230,11 +232,12 @@ func TestSuperChatEvent_Get_Pagination(t *testing.T) {
}
func TestSuperChatEvent_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "event-1",
@@ -248,9 +251,10 @@ func TestSuperChatEvent_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
+3 -1
View File
@@ -50,7 +50,9 @@ func (t *Thumbnail) Set(writer io.Writer) error {
return errors.Join(errSetThumbnail, err)
}
common.PrintResult(t.Output, res, writer, "Thumbnail set for video %s", t.VideoId)
common.PrintResult(
t.Output, res, writer, "Thumbnail set for video %s", t.VideoId,
)
return nil
}
+13 -11
View File
@@ -173,21 +173,23 @@ func TestThumbnail_Set(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"default": {"url": "https://example.com/thumb.jpg"}}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
thumb := NewThumbnail(opts...)
+22 -16
View File
@@ -119,9 +119,12 @@ func (v *Video) Get() ([]*youtube.Video, error) {
call = call.OnBehalfOfContentOwner(v.OnBehalfOfContentOwner)
}
return common.Paginate(v.Fields, call, func(r *youtube.VideoListResponse) ([]*youtube.Video, string) {
return r.Items, r.NextPageToken
}, errGetVideo)
return common.Paginate(
v.Fields, call,
func(r *youtube.VideoListResponse) ([]*youtube.Video, string) {
return r.Items, r.NextPageToken
}, errGetVideo,
)
}
func (v *Video) List(writer io.Writer) error {
@@ -130,19 +133,22 @@ func (v *Video) List(writer io.Writer) error {
return err
}
common.PrintList(v.Output, videos, writer, table.Row{"ID", "Title", "Channel ID", "Views"}, func(video *youtube.Video) table.Row {
title := ""
channelId := ""
var views uint64
if video.Snippet != nil {
title = video.Snippet.Title
channelId = video.Snippet.ChannelId
}
if video.Statistics != nil {
views = video.Statistics.ViewCount
}
return table.Row{video.Id, title, channelId, views}
})
common.PrintList(
v.Output, videos, writer, table.Row{"ID", "Title", "Channel ID", "Views"},
func(video *youtube.Video) table.Row {
title := ""
channelId := ""
var views uint64
if video.Snippet != nil {
title = video.Snippet.Title
channelId = video.Snippet.ChannelId
}
if video.Statistics != nil {
views = video.Statistics.ViewCount
}
return table.Row{video.Id, title, channelId, views}
},
)
return err
}
+94 -78
View File
@@ -367,21 +367,23 @@ func TestVideo_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "video-1", "snippet": {"title": "Video 1"}}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
v := NewVideo(opts...)
@@ -441,11 +443,12 @@ func TestVideo_Get_Pagination(t *testing.T) {
}
func TestVideo_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "video-1",
@@ -459,9 +462,10 @@ func TestVideo_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
@@ -645,15 +649,17 @@ func TestVideo_Insert(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-video-id", "snippet": {"title": "New Video"}, "status": {"privacyStatus": "public"}}`))
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"id": "new-video-id", "snippet": {"title": "New Video"}, "status": {"privacyStatus": "public"}}`))
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
v := NewVideo(opts...)
@@ -736,25 +742,27 @@ func TestVideo_Update(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
if r.Method == "GET" {
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "video-id", "snippet": {"title": "Old Title"}}
]
}`),
)
} else {
_, _ = w.Write([]byte(`{"id": "video-id", "snippet": {"title": "Updated Title"}}`))
}
},
))
)
} else {
_, _ = w.Write([]byte(`{"id": "video-id", "snippet": {"title": "Updated Title"}}`))
}
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
v := NewVideo(opts...)
@@ -798,14 +806,16 @@ func TestVideo_Rate(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
v := NewVideo(opts...)
@@ -858,21 +868,23 @@ func TestVideo_GetRating(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"videoId": "video-id", "rating": "like"}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
v := NewVideo(opts...)
@@ -931,14 +943,16 @@ func TestVideo_Delete(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
v := NewVideo(opts...)
@@ -996,14 +1010,16 @@ func TestVideo_ReportAbuse(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
v := NewVideo(opts...)
@@ -60,9 +60,12 @@ func (va *VideoAbuseReportReason) List(writer io.Writer) error {
return err
}
common.PrintList(va.Output, reasons, writer, table.Row{"ID", "Label"}, func(r *youtube.VideoAbuseReportReason) table.Row {
return table.Row{r.Id, r.Snippet.Label}
})
common.PrintList(
va.Output, reasons, writer, table.Row{"ID", "Label"},
func(r *youtube.VideoAbuseReportReason) table.Row {
return table.Row{r.Id, r.Snippet.Label}
},
)
return nil
}
@@ -125,21 +125,23 @@ func TestVideoAbuseReportReason_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "reason-1", "snippet": {"label": "Reason 1"}}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
va := NewVideoAbuseReportReason(opts...)
@@ -163,11 +165,12 @@ func TestVideoAbuseReportReason_Get(t *testing.T) {
}
func TestVideoAbuseReportReason_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "reason-1",
@@ -177,9 +180,10 @@ func TestVideoAbuseReportReason_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
+6 -3
View File
@@ -65,9 +65,12 @@ func (vc *VideoCategory) List(writer io.Writer) error {
return err
}
common.PrintList(vc.Output, categories, writer, table.Row{"ID", "Title", "Assignable"}, func(c *youtube.VideoCategory) table.Row {
return table.Row{c.Id, c.Snippet.Title, c.Snippet.Assignable}
})
common.PrintList(
vc.Output, categories, writer, table.Row{"ID", "Title", "Assignable"},
func(c *youtube.VideoCategory) table.Row {
return table.Row{c.Id, c.Snippet.Title, c.Snippet.Assignable}
},
)
return nil
}
+23 -19
View File
@@ -151,21 +151,23 @@ func TestVideoCategory_Get(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{"id": "category-1", "snippet": {"title": "Category 1"}}
]
}`),
)
},
))
)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
vc := NewVideoCategory(opts...)
@@ -185,11 +187,12 @@ func TestVideoCategory_Get(t *testing.T) {
}
func TestVideoCategory_List(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(
[]byte(`{
"items": [
{
"id": "category-1",
@@ -200,9 +203,10 @@ func TestVideoCategory_List(t *testing.T) {
}
]
}`),
)
},
))
)
},
),
)
tests := []struct {
name string
+20 -16
View File
@@ -187,14 +187,16 @@ func TestWatermark_Set(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
w := NewWatermark(opts...)
@@ -253,14 +255,16 @@ func TestWatermark_Unset(t *testing.T) {
for _, tt := range tests {
t.Run(
tt.name, func(t *testing.T) {
svc := common.NewTestService(t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
))
svc := common.NewTestService(
t, http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
if tt.verify != nil {
tt.verify(r)
}
w.WriteHeader(http.StatusNoContent)
},
),
)
opts := append([]Option{WithService(svc)}, tt.opts...)
w := NewWatermark(opts...)
+9 -2
View File
@@ -6,7 +6,9 @@
"icons": [
{
"mimeType": "image/svg+xml",
"sizes": ["any"],
"sizes": [
"any"
],
"src": "https://raw.githubusercontent.com/eat-pray-ai/yutu/main/assets/yutu-ico.svg"
}
],
@@ -97,7 +99,12 @@
"isSecret": false,
"name": "YUTU_LOG_LEVEL",
"default": "INFO",
"choices": ["DEBUG", "INFO", "WARNING", "ERROR"]
"choices": [
"DEBUG",
"INFO",
"WARNING",
"ERROR"
]
}
]
}