@@ -2,7 +2,7 @@ | |||
# Contributor: Timofey Titovets <nefelim4ag@gmail.com> | |||
pkgname=drive-git | |||
pkgver=r263.45c89dc | |||
pkgver=r292.7dbd8bf | |||
pkgrel=1 | |||
pkgdesc="Drive is a tiny program to pull or push Google Drive files. You need go1.2 installed in order to build the program." | |||
arch=('any') | |||
@@ -27,4 +27,9 @@ build() { | |||
package() { | |||
mkdir -p "$pkgdir/usr/bin" | |||
install -p -m755 "$srcdir/bin/"* "$pkgdir/usr/bin" | |||
cd "$srcdir/$pkgname" | |||
mkdir -p $pkgdir/usr/share/licenses/$pkgname | |||
install -m 0644 LICENSE $pkgdir/usr/share/licenses/$pkgname/ | |||
} |
@@ -1,9 +1,10 @@ | |||
0e5b686eea21f21ddd51db1c178539fb7362267d not-for-merge branch 'diff-prettify' of https://github.com/odeke-em/drive | |||
45c89dcaf4eb2de00104b01c5440b92e63a3a0f9 not-for-merge branch 'master' of https://github.com/odeke-em/drive | |||
fda55c7cb0997e39cc222e659f52a75f92acf785 not-for-merge branch 'checksum-off-by-default' of https://github.com/odeke-em/drive | |||
8559df2388c01e4f4b712a476cb7631e1c5cb464 not-for-merge branch 'file-stream-progress' of https://github.com/odeke-em/drive | |||
7dbd8bf4629805fc257be30bc932be0aaa9efea1 not-for-merge branch 'master' of https://github.com/odeke-em/drive | |||
8487a598b42192c351c0d6b731068cc69ac6169f not-for-merge branch 'matches' of https://github.com/odeke-em/drive | |||
3b5799f37559941189774333f5a9c62f20faec56 not-for-merge branch 'oauth2-migration' of https://github.com/odeke-em/drive | |||
3b3ef5ce68116fdeca80f2fee953f27c24a2713a not-for-merge branch 'quiet' of https://github.com/odeke-em/drive | |||
a5e91ef2e2adf7755a00b7009a3ec8b151eef198 not-for-merge branch 'resolve-conflict-toggles' of https://github.com/odeke-em/drive | |||
d490b3733671f07cfc837ae34691a1cbacb49d18 not-for-merge branch 'sorting' of https://github.com/odeke-em/drive | |||
d44059edf4893bfd5901de9336cc5b1612f6bff4 not-for-merge branch 'sorting' of https://github.com/odeke-em/drive | |||
fcf32398966cd63e284a8011677f373517ff3426 not-for-merge branch 'warn-on-dups' of https://github.com/odeke-em/drive | |||
16201967d6a52e2ff635e84e6a274f2dd50c5f0b not-for-merge 'refs/pull/1/head' of https://github.com/odeke-em/drive | |||
85bcf23f56f18bfc0613c96166f11b0bb7fbc95e not-for-merge 'refs/pull/101/head' of https://github.com/odeke-em/drive | |||
eed22f67a2d457ad6fa4592f7637448369c73615 not-for-merge 'refs/pull/102/head' of https://github.com/odeke-em/drive | |||
@@ -13,13 +14,18 @@ fc09bdc5976d3eb6786c89b2b1066e4fa7ce7d50 not-for-merge 'refs/pull/111/head' of h | |||
3903fc4d718e32ab3972e199436b1aaaf601c493 not-for-merge 'refs/pull/113/head' of https://github.com/odeke-em/drive | |||
f79228840e94b14a533545bfd4a34e49da627722 not-for-merge 'refs/pull/120/head' of https://github.com/odeke-em/drive | |||
8d560e18d02bf11b2ecc1140147ca4f6804cc3df not-for-merge 'refs/pull/120/merge' of https://github.com/odeke-em/drive | |||
a5e91ef2e2adf7755a00b7009a3ec8b151eef198 not-for-merge 'refs/pull/121/head' of https://github.com/odeke-em/drive | |||
a4f6b59cf3206c1d57a23ba9aad94cdcd331bc3a not-for-merge 'refs/pull/121/merge' of https://github.com/odeke-em/drive | |||
8f515bccedf61968b71281e23e5892146aa057e8 not-for-merge 'refs/pull/121/head' of https://github.com/odeke-em/drive | |||
0e5b686eea21f21ddd51db1c178539fb7362267d not-for-merge 'refs/pull/122/head' of https://github.com/odeke-em/drive | |||
70f97d161c342b040d9d18c1a6f353fa72d41576 not-for-merge 'refs/pull/122/merge' of https://github.com/odeke-em/drive | |||
3b3ef5ce68116fdeca80f2fee953f27c24a2713a not-for-merge 'refs/pull/123/head' of https://github.com/odeke-em/drive | |||
2099241e31e5e3faa7cb9d32ab0856cf82ae6e1e not-for-merge 'refs/pull/123/merge' of https://github.com/odeke-em/drive | |||
e667aa254efcbcd240c3341e558015ef5ffeb6ad not-for-merge 'refs/pull/126/head' of https://github.com/odeke-em/drive | |||
a659f363f37f44aa7015319fe9c82a4431819a99 not-for-merge 'refs/pull/127/head' of https://github.com/odeke-em/drive | |||
fda55c7cb0997e39cc222e659f52a75f92acf785 not-for-merge 'refs/pull/128/head' of https://github.com/odeke-em/drive | |||
ba44041c9639d55b5c0de84ea7884eb8b76a4bd5 not-for-merge 'refs/pull/128/merge' of https://github.com/odeke-em/drive | |||
65d690b2f6bd22f014b439f91cfd977d0ea3cd86 not-for-merge 'refs/pull/13/head' of https://github.com/odeke-em/drive | |||
83e0ac2c8b0b5d828499b6d1678e0de1f7e90734 not-for-merge 'refs/pull/131/head' of https://github.com/odeke-em/drive | |||
2d59fe66eb6bfa149c56596c485a616cf151daac not-for-merge 'refs/pull/133/head' of https://github.com/odeke-em/drive | |||
8b36577ea1fd924812d4c140e72554d76befe33e not-for-merge 'refs/pull/144/head' of https://github.com/odeke-em/drive | |||
8487a598b42192c351c0d6b731068cc69ac6169f not-for-merge 'refs/pull/146/head' of https://github.com/odeke-em/drive | |||
a9538461ae2e746625d9f84e04d7d41a20475205 not-for-merge 'refs/pull/16/head' of https://github.com/odeke-em/drive | |||
0471cf89b87baa4160bcc27ca2c8f21b10da3f28 not-for-merge 'refs/pull/17/head' of https://github.com/odeke-em/drive | |||
a2ba65854ad640e6f95761ac869fd7c2edb288e2 not-for-merge 'refs/pull/21/head' of https://github.com/odeke-em/drive | |||
@@ -62,3 +68,4 @@ b322dc12ef9b5d51088a0e56e19d03a5c78908ba not-for-merge tag 'v0.1.2' of https://g | |||
1a2f8374321d1d8d0bfb0f2f7ffd6a5e691cad19 not-for-merge tag 'v0.1.3' of https://github.com/odeke-em/drive | |||
29d4fdbbaafbb37eb2c48a9b1c495fadbe4aa2b1 not-for-merge tag 'v0.1.4' of https://github.com/odeke-em/drive | |||
63615fc71d122f85923210490d0f883588aa3ba3 not-for-merge tag 'v0.1.5' of https://github.com/odeke-em/drive | |||
e9c9e47a6a47bcd2524d26d7cd8ac85faab18704 not-for-merge tag 'v0.1.6' of https://github.com/odeke-em/drive |
@@ -0,0 +1 @@ | |||
fda55c7cb0997e39cc222e659f52a75f92acf785 |
@@ -1 +0,0 @@ | |||
0e5b686eea21f21ddd51db1c178539fb7362267d |
@@ -0,0 +1 @@ | |||
8559df2388c01e4f4b712a476cb7631e1c5cb464 |
@@ -1 +1 @@ | |||
45c89dcaf4eb2de00104b01c5440b92e63a3a0f9 | |||
7dbd8bf4629805fc257be30bc932be0aaa9efea1 |
@@ -0,0 +1 @@ | |||
8487a598b42192c351c0d6b731068cc69ac6169f |
@@ -1 +0,0 @@ | |||
3b3ef5ce68116fdeca80f2fee953f27c24a2713a |
@@ -1 +0,0 @@ | |||
a5e91ef2e2adf7755a00b7009a3ec8b151eef198 |
@@ -0,0 +1 @@ | |||
d44059edf4893bfd5901de9336cc5b1612f6bff4 |
@@ -0,0 +1 @@ | |||
fcf32398966cd63e284a8011677f373517ff3426 |
@@ -1 +1 @@ | |||
a5e91ef2e2adf7755a00b7009a3ec8b151eef198 | |||
8f515bccedf61968b71281e23e5892146aa057e8 |
@@ -1 +0,0 @@ | |||
a4f6b59cf3206c1d57a23ba9aad94cdcd331bc3a |
@@ -1 +0,0 @@ | |||
70f97d161c342b040d9d18c1a6f353fa72d41576 |
@@ -1 +0,0 @@ | |||
2099241e31e5e3faa7cb9d32ab0856cf82ae6e1e |
@@ -0,0 +1 @@ | |||
e667aa254efcbcd240c3341e558015ef5ffeb6ad |
@@ -0,0 +1 @@ | |||
a659f363f37f44aa7015319fe9c82a4431819a99 |
@@ -0,0 +1 @@ | |||
fda55c7cb0997e39cc222e659f52a75f92acf785 |
@@ -0,0 +1 @@ | |||
ba44041c9639d55b5c0de84ea7884eb8b76a4bd5 |
@@ -0,0 +1 @@ | |||
83e0ac2c8b0b5d828499b6d1678e0de1f7e90734 |
@@ -0,0 +1 @@ | |||
2d59fe66eb6bfa149c56596c485a616cf151daac |
@@ -0,0 +1 @@ | |||
8b36577ea1fd924812d4c140e72554d76befe33e |
@@ -0,0 +1 @@ | |||
8487a598b42192c351c0d6b731068cc69ac6169f |
@@ -0,0 +1 @@ | |||
e9c9e47a6a47bcd2524d26d7cd8ac85faab18704 |
@@ -1,13 +1,13 @@ | |||
# Generated by makepkg 4.2.1 | |||
# using fakeroot version 1.20.2 | |||
# Tue Mar 24 22:07:42 UTC 2015 | |||
# Mon Apr 6 03:08:26 UTC 2015 | |||
pkgname = drive-git | |||
pkgver = r263.45c89dc-1 | |||
pkgver = r292.7dbd8bf-1 | |||
pkgdesc = Drive is a tiny program to pull or push Google Drive files. You need go1.2 installed in order to build the program. | |||
url = https://github.com/odeke-em/drive | |||
builddate = 1427234862 | |||
builddate = 1428289706 | |||
packager = Unknown Packager | |||
size = 8659968 | |||
size = 8682496 | |||
arch = any | |||
license = Apache | |||
conflict = drive |
@@ -0,0 +1,177 @@ | |||
Apache License | |||
Version 2.0, January 2004 | |||
http://www.apache.org/licenses/ | |||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||
1. Definitions. | |||
"License" shall mean the terms and conditions for use, reproduction, | |||
and distribution as defined by Sections 1 through 9 of this document. | |||
"Licensor" shall mean the copyright owner or entity authorized by | |||
the copyright owner that is granting the License. | |||
"Legal Entity" shall mean the union of the acting entity and all | |||
other entities that control, are controlled by, or are under common | |||
control with that entity. For the purposes of this definition, | |||
"control" means (i) the power, direct or indirect, to cause the | |||
direction or management of such entity, whether by contract or | |||
otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||
outstanding shares, or (iii) beneficial ownership of such entity. | |||
"You" (or "Your") shall mean an individual or Legal Entity | |||
exercising permissions granted by this License. | |||
"Source" form shall mean the preferred form for making modifications, | |||
including but not limited to software source code, documentation | |||
source, and configuration files. | |||
"Object" form shall mean any form resulting from mechanical | |||
transformation or translation of a Source form, including but | |||
not limited to compiled object code, generated documentation, | |||
and conversions to other media types. | |||
"Work" shall mean the work of authorship, whether in Source or | |||
Object form, made available under the License, as indicated by a | |||
copyright notice that is included in or attached to the work | |||
(an example is provided in the Appendix below). | |||
"Derivative Works" shall mean any work, whether in Source or Object | |||
form, that is based on (or derived from) the Work and for which the | |||
editorial revisions, annotations, elaborations, or other modifications | |||
represent, as a whole, an original work of authorship. For the purposes | |||
of this License, Derivative Works shall not include works that remain | |||
separable from, or merely link (or bind by name) to the interfaces of, | |||
the Work and Derivative Works thereof. | |||
"Contribution" shall mean any work of authorship, including | |||
the original version of the Work and any modifications or additions | |||
to that Work or Derivative Works thereof, that is intentionally | |||
submitted to Licensor for inclusion in the Work by the copyright owner | |||
or by an individual or Legal Entity authorized to submit on behalf of | |||
the copyright owner. For the purposes of this definition, "submitted" | |||
means any form of electronic, verbal, or written communication sent | |||
to the Licensor or its representatives, including but not limited to | |||
communication on electronic mailing lists, source code control systems, | |||
and issue tracking systems that are managed by, or on behalf of, the | |||
Licensor for the purpose of discussing and improving the Work, but | |||
excluding communication that is conspicuously marked or otherwise | |||
designated in writing by the copyright owner as "Not a Contribution." | |||
"Contributor" shall mean Licensor and any individual or Legal Entity | |||
on behalf of whom a Contribution has been received by Licensor and | |||
subsequently incorporated within the Work. | |||
2. Grant of Copyright License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
copyright license to reproduce, prepare Derivative Works of, | |||
publicly display, publicly perform, sublicense, and distribute the | |||
Work and such Derivative Works in Source or Object form. | |||
3. Grant of Patent License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
(except as stated in this section) patent license to make, have made, | |||
use, offer to sell, sell, import, and otherwise transfer the Work, | |||
where such license applies only to those patent claims licensable | |||
by such Contributor that are necessarily infringed by their | |||
Contribution(s) alone or by combination of their Contribution(s) | |||
with the Work to which such Contribution(s) was submitted. If You | |||
institute patent litigation against any entity (including a | |||
cross-claim or counterclaim in a lawsuit) alleging that the Work | |||
or a Contribution incorporated within the Work constitutes direct | |||
or contributory patent infringement, then any patent licenses | |||
granted to You under this License for that Work shall terminate | |||
as of the date such litigation is filed. | |||
4. Redistribution. You may reproduce and distribute copies of the | |||
Work or Derivative Works thereof in any medium, with or without | |||
modifications, and in Source or Object form, provided that You | |||
meet the following conditions: | |||
(a) You must give any other recipients of the Work or | |||
Derivative Works a copy of this License; and | |||
(b) You must cause any modified files to carry prominent notices | |||
stating that You changed the files; and | |||
(c) You must retain, in the Source form of any Derivative Works | |||
that You distribute, all copyright, patent, trademark, and | |||
attribution notices from the Source form of the Work, | |||
excluding those notices that do not pertain to any part of | |||
the Derivative Works; and | |||
(d) If the Work includes a "NOTICE" text file as part of its | |||
distribution, then any Derivative Works that You distribute must | |||
include a readable copy of the attribution notices contained | |||
within such NOTICE file, excluding those notices that do not | |||
pertain to any part of the Derivative Works, in at least one | |||
of the following places: within a NOTICE text file distributed | |||
as part of the Derivative Works; within the Source form or | |||
documentation, if provided along with the Derivative Works; or, | |||
within a display generated by the Derivative Works, if and | |||
wherever such third-party notices normally appear. The contents | |||
of the NOTICE file are for informational purposes only and | |||
do not modify the License. You may add Your own attribution | |||
notices within Derivative Works that You distribute, alongside | |||
or as an addendum to the NOTICE text from the Work, provided | |||
that such additional attribution notices cannot be construed | |||
as modifying the License. | |||
You may add Your own copyright statement to Your modifications and | |||
may provide additional or different license terms and conditions | |||
for use, reproduction, or distribution of Your modifications, or | |||
for any such Derivative Works as a whole, provided Your use, | |||
reproduction, and distribution of the Work otherwise complies with | |||
the conditions stated in this License. | |||
5. Submission of Contributions. Unless You explicitly state otherwise, | |||
any Contribution intentionally submitted for inclusion in the Work | |||
by You to the Licensor shall be under the terms and conditions of | |||
this License, without any additional terms or conditions. | |||
Notwithstanding the above, nothing herein shall supersede or modify | |||
the terms of any separate license agreement you may have executed | |||
with Licensor regarding such Contributions. | |||
6. Trademarks. This License does not grant permission to use the trade | |||
names, trademarks, service marks, or product names of the Licensor, | |||
except as required for reasonable and customary use in describing the | |||
origin of the Work and reproducing the content of the NOTICE file. | |||
7. Disclaimer of Warranty. Unless required by applicable law or | |||
agreed to in writing, Licensor provides the Work (and each | |||
Contributor provides its Contributions) on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
implied, including, without limitation, any warranties or conditions | |||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||
PARTICULAR PURPOSE. You are solely responsible for determining the | |||
appropriateness of using or redistributing the Work and assume any | |||
risks associated with Your exercise of permissions under this License. | |||
8. Limitation of Liability. In no event and under no legal theory, | |||
whether in tort (including negligence), contract, or otherwise, | |||
unless required by applicable law (such as deliberate and grossly | |||
negligent acts) or agreed to in writing, shall any Contributor be | |||
liable to You for damages, including any direct, indirect, special, | |||
incidental, or consequential damages of any character arising as a | |||
result of this License or out of the use or inability to use the | |||
Work (including but not limited to damages for loss of goodwill, | |||
work stoppage, computer failure or malfunction, or any and all | |||
other commercial damages or losses), even if such Contributor | |||
has been advised of the possibility of such damages. | |||
9. Accepting Warranty or Additional Liability. While redistributing | |||
the Work or Derivative Works thereof, You may choose to offer, | |||
and charge a fee for, acceptance of support, warranty, indemnity, | |||
or other liability obligations and/or rights consistent with this | |||
License. However, in accepting such obligations, You may act only | |||
on Your own behalf and on Your sole responsibility, not on behalf | |||
of any other Contributor, and only if You agree to indemnify, | |||
defend, and hold each Contributor harmless for any liability | |||
incurred by, or claims asserted against, such Contributor by reason | |||
of your accepting any such warranty or additional liability. | |||
END OF TERMS AND CONDITIONS |
@@ -31,9 +31,11 @@ build() { | |||
} | |||
package() { | |||
cd "$pkgname-$pkgver/cmd/drive" | |||
cd "$srcdir/$pkgname-$pkgver/cmd/drive" | |||
install -Dm755 "$pkgname" "$pkgdir/usr/bin/$pkgname" | |||
cd "$srcdir/$pkgname-$pkgver" | |||
mkdir -p $pkgdir/usr/share/licenses/$pkgname | |||
install -m 0644 LICENSE $pkgdir/usr/share/licenses/$pkgname/ | |||
} | |||
# vim:set ts=2 sw=2 et: |
@@ -1,13 +1,13 @@ | |||
# Generated by makepkg 4.2.1 | |||
# using fakeroot version 1.20.2 | |||
# Sun Mar 29 04:02:37 UTC 2015 | |||
# Mon Apr 6 03:08:58 UTC 2015 | |||
pkgname = drive | |||
pkgver = 0.1.6-1 | |||
pkgdesc = Pull or push Google Drive files | |||
url = http://github.com/odeke-em/drive | |||
builddate = 1427601757 | |||
builddate = 1428289738 | |||
packager = Unknown Packager | |||
size = 8685568 | |||
size = 8708096 | |||
arch = x86_64 | |||
license = Apache | |||
conflict = drive-git |
@@ -0,0 +1,177 @@ | |||
Apache License | |||
Version 2.0, January 2004 | |||
http://www.apache.org/licenses/ | |||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||
1. Definitions. | |||
"License" shall mean the terms and conditions for use, reproduction, | |||
and distribution as defined by Sections 1 through 9 of this document. | |||
"Licensor" shall mean the copyright owner or entity authorized by | |||
the copyright owner that is granting the License. | |||
"Legal Entity" shall mean the union of the acting entity and all | |||
other entities that control, are controlled by, or are under common | |||
control with that entity. For the purposes of this definition, | |||
"control" means (i) the power, direct or indirect, to cause the | |||
direction or management of such entity, whether by contract or | |||
otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||
outstanding shares, or (iii) beneficial ownership of such entity. | |||
"You" (or "Your") shall mean an individual or Legal Entity | |||
exercising permissions granted by this License. | |||
"Source" form shall mean the preferred form for making modifications, | |||
including but not limited to software source code, documentation | |||
source, and configuration files. | |||
"Object" form shall mean any form resulting from mechanical | |||
transformation or translation of a Source form, including but | |||
not limited to compiled object code, generated documentation, | |||
and conversions to other media types. | |||
"Work" shall mean the work of authorship, whether in Source or | |||
Object form, made available under the License, as indicated by a | |||
copyright notice that is included in or attached to the work | |||
(an example is provided in the Appendix below). | |||
"Derivative Works" shall mean any work, whether in Source or Object | |||
form, that is based on (or derived from) the Work and for which the | |||
editorial revisions, annotations, elaborations, or other modifications | |||
represent, as a whole, an original work of authorship. For the purposes | |||
of this License, Derivative Works shall not include works that remain | |||
separable from, or merely link (or bind by name) to the interfaces of, | |||
the Work and Derivative Works thereof. | |||
"Contribution" shall mean any work of authorship, including | |||
the original version of the Work and any modifications or additions | |||
to that Work or Derivative Works thereof, that is intentionally | |||
submitted to Licensor for inclusion in the Work by the copyright owner | |||
or by an individual or Legal Entity authorized to submit on behalf of | |||
the copyright owner. For the purposes of this definition, "submitted" | |||
means any form of electronic, verbal, or written communication sent | |||
to the Licensor or its representatives, including but not limited to | |||
communication on electronic mailing lists, source code control systems, | |||
and issue tracking systems that are managed by, or on behalf of, the | |||
Licensor for the purpose of discussing and improving the Work, but | |||
excluding communication that is conspicuously marked or otherwise | |||
designated in writing by the copyright owner as "Not a Contribution." | |||
"Contributor" shall mean Licensor and any individual or Legal Entity | |||
on behalf of whom a Contribution has been received by Licensor and | |||
subsequently incorporated within the Work. | |||
2. Grant of Copyright License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
copyright license to reproduce, prepare Derivative Works of, | |||
publicly display, publicly perform, sublicense, and distribute the | |||
Work and such Derivative Works in Source or Object form. | |||
3. Grant of Patent License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
(except as stated in this section) patent license to make, have made, | |||
use, offer to sell, sell, import, and otherwise transfer the Work, | |||
where such license applies only to those patent claims licensable | |||
by such Contributor that are necessarily infringed by their | |||
Contribution(s) alone or by combination of their Contribution(s) | |||
with the Work to which such Contribution(s) was submitted. If You | |||
institute patent litigation against any entity (including a | |||
cross-claim or counterclaim in a lawsuit) alleging that the Work | |||
or a Contribution incorporated within the Work constitutes direct | |||
or contributory patent infringement, then any patent licenses | |||
granted to You under this License for that Work shall terminate | |||
as of the date such litigation is filed. | |||
4. Redistribution. You may reproduce and distribute copies of the | |||
Work or Derivative Works thereof in any medium, with or without | |||
modifications, and in Source or Object form, provided that You | |||
meet the following conditions: | |||
(a) You must give any other recipients of the Work or | |||
Derivative Works a copy of this License; and | |||
(b) You must cause any modified files to carry prominent notices | |||
stating that You changed the files; and | |||
(c) You must retain, in the Source form of any Derivative Works | |||
that You distribute, all copyright, patent, trademark, and | |||
attribution notices from the Source form of the Work, | |||
excluding those notices that do not pertain to any part of | |||
the Derivative Works; and | |||
(d) If the Work includes a "NOTICE" text file as part of its | |||
distribution, then any Derivative Works that You distribute must | |||
include a readable copy of the attribution notices contained | |||
within such NOTICE file, excluding those notices that do not | |||
pertain to any part of the Derivative Works, in at least one | |||
of the following places: within a NOTICE text file distributed | |||
as part of the Derivative Works; within the Source form or | |||
documentation, if provided along with the Derivative Works; or, | |||
within a display generated by the Derivative Works, if and | |||
wherever such third-party notices normally appear. The contents | |||
of the NOTICE file are for informational purposes only and | |||
do not modify the License. You may add Your own attribution | |||
notices within Derivative Works that You distribute, alongside | |||
or as an addendum to the NOTICE text from the Work, provided | |||
that such additional attribution notices cannot be construed | |||
as modifying the License. | |||
You may add Your own copyright statement to Your modifications and | |||
may provide additional or different license terms and conditions | |||
for use, reproduction, or distribution of Your modifications, or | |||
for any such Derivative Works as a whole, provided Your use, | |||
reproduction, and distribution of the Work otherwise complies with | |||
the conditions stated in this License. | |||
5. Submission of Contributions. Unless You explicitly state otherwise, | |||
any Contribution intentionally submitted for inclusion in the Work | |||
by You to the Licensor shall be under the terms and conditions of | |||
this License, without any additional terms or conditions. | |||
Notwithstanding the above, nothing herein shall supersede or modify | |||
the terms of any separate license agreement you may have executed | |||
with Licensor regarding such Contributions. | |||
6. Trademarks. This License does not grant permission to use the trade | |||
names, trademarks, service marks, or product names of the Licensor, | |||
except as required for reasonable and customary use in describing the | |||
origin of the Work and reproducing the content of the NOTICE file. | |||
7. Disclaimer of Warranty. Unless required by applicable law or | |||
agreed to in writing, Licensor provides the Work (and each | |||
Contributor provides its Contributions) on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
implied, including, without limitation, any warranties or conditions | |||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||
PARTICULAR PURPOSE. You are solely responsible for determining the | |||
appropriateness of using or redistributing the Work and assume any | |||
risks associated with Your exercise of permissions under this License. | |||
8. Limitation of Liability. In no event and under no legal theory, | |||
whether in tort (including negligence), contract, or otherwise, | |||
unless required by applicable law (such as deliberate and grossly | |||
negligent acts) or agreed to in writing, shall any Contributor be | |||
liable to You for damages, including any direct, indirect, special, | |||
incidental, or consequential damages of any character arising as a | |||
result of this License or out of the use or inability to use the | |||
Work (including but not limited to damages for loss of goodwill, | |||
work stoppage, computer failure or malfunction, or any and all | |||
other commercial damages or losses), even if such Contributor | |||
has been advised of the possibility of such damages. | |||
9. Accepting Warranty or Additional Liability. While redistributing | |||
the Work or Derivative Works thereof, You may choose to offer, | |||
and charge a fee for, acceptance of support, warranty, indemnity, | |||
or other liability obligations and/or rights consistent with this | |||
License. However, in accepting such obligations, You may act only | |||
on Your own behalf and on Your sole responsibility, not on behalf | |||
of any other Contributor, and only if You agree to indemnify, | |||
defend, and hold each Contributor harmless for any liability | |||
incurred by, or claims asserted against, such Contributor by reason | |||
of your accepting any such warranty or additional liability. | |||
END OF TERMS AND CONDITIONS |
@@ -1,7 +1,7 @@ | |||
# Maintainer: John Jenkins twodopeshaggy@gmail.com | |||
pkgname=rtv-git | |||
pkgver=r226.6d1ac5d | |||
pkgver=r273.6036d90 | |||
pkgrel=1 | |||
pkgdesc="Browse Reddit from your terminal" | |||
arch=('any') | |||
@@ -19,4 +19,6 @@ pkgver() { | |||
package() { | |||
cd "$srcdir/rtv" | |||
python setup.py install --root="$pkgdir/" --optimize=1 | |||
mkdir -p $pkgdir/usr/share/licenses/$pkgname | |||
install -m 0644 LICENSE $pkgdir/usr/share/licenses/$pkgname/ | |||
} |
@@ -1,13 +1,13 @@ | |||
# Generated by makepkg 4.2.1 | |||
# using fakeroot version 1.20.2 | |||
# Thu Apr 2 03:20:06 UTC 2015 | |||
# Mon Apr 6 03:01:16 UTC 2015 | |||
pkgname = rtv-git | |||
pkgver = r226.ca41b77-1 | |||
pkgver = r273.6036d90-1 | |||
pkgdesc = Browse Reddit from your terminal | |||
url = https://github.com/michael-lazar/rtv | |||
builddate = 1427944806 | |||
builddate = 1428289276 | |||
packager = Unknown Packager | |||
size = 201728 | |||
size = 230400 | |||
arch = any | |||
license = MIT | |||
depend = ncurses |
@@ -1,10 +1,10 @@ | |||
#!/bin/python | |||
# EASY-INSTALL-ENTRY-SCRIPT: 'rtv==1.1.2','console_scripts','rtv' | |||
__requires__ = 'rtv==1.1.2' | |||
# EASY-INSTALL-ENTRY-SCRIPT: 'rtv==1.2','console_scripts','rtv' | |||
__requires__ = 'rtv==1.2' | |||
import sys | |||
from pkg_resources import load_entry_point | |||
if __name__ == '__main__': | |||
sys.exit( | |||
load_entry_point('rtv==1.1.2', 'console_scripts', 'rtv')() | |||
load_entry_point('rtv==1.2', 'console_scripts', 'rtv')() | |||
) |
@@ -15,6 +15,7 @@ from .curses_helpers import curses_session | |||
from .submission import SubmissionPage | |||
from .subreddit import SubredditPage | |||
from .docs import * | |||
from .__version__ import __version__ | |||
__all__ = [] | |||
@@ -25,14 +26,28 @@ def load_config(): | |||
saved settings for things like the username and password. | |||
""" | |||
config_path = os.path.join(os.path.expanduser('~'), '.rtv') | |||
config = configparser.ConfigParser() | |||
config.read(config_path) | |||
HOME = os.path.expanduser('~') | |||
XDG_CONFIG_HOME = os.getenv('XDG_CONFIG_HOME', os.path.join(HOME, '.config')) | |||
config_paths = [ | |||
os.path.join(XDG_CONFIG_HOME, 'rtv', 'rtv.cfg'), | |||
os.path.join(HOME, '.rtv') | |||
] | |||
# read only the first existing config file | |||
for config_path in config_paths: | |||
if os.path.exists(config_path): | |||
config.read(config_path) | |||
break | |||
defaults = {} | |||
if config.has_section('rtv'): | |||
defaults = dict(config.items('rtv')) | |||
if 'unicode' in defaults: | |||
defaults['unicode'] = config.getboolean('rtv', 'unicode') | |||
return defaults | |||
@@ -68,6 +83,13 @@ def main(): | |||
args = command_line() | |||
local_config = load_config() | |||
# set the terminal title | |||
title = 'rtv {0}'.format(__version__) | |||
if os.name == 'nt': | |||
os.system('title {0}'.format(title)) | |||
else: | |||
sys.stdout.write("\x1b]2;{0}\x07".format(title)) | |||
# Fill in empty arguments with config file values. Paramaters explicitly | |||
# typed on the command line will take priority over config file params. | |||
for key, val in local_config.items(): |
@@ -1 +1 @@ | |||
__version__ = '1.1.2' | |||
__version__ = '1.2' |
@@ -3,7 +3,7 @@ import textwrap | |||
import praw | |||
import requests | |||
from .exceptions import SubmissionError, SubredditError | |||
from .exceptions import SubmissionError, SubredditError, AccountError | |||
from .helpers import humanize_timestamp, wrap_text, strip_subreddit_url | |||
__all__ = ['SubredditContent', 'SubmissionContent'] | |||
@@ -79,6 +79,7 @@ class BaseContent(object): | |||
flair = comment.author_flair_text | |||
data['flair'] = (flair if flair else '') | |||
data['likes'] = comment.likes | |||
data['gold'] = comment.gilded > 0 | |||
return data | |||
@@ -107,23 +108,18 @@ class BaseContent(object): | |||
data['url_full'] = sub.url | |||
data['url'] = ('selfpost' if is_selfpost(sub.url) else sub.url) | |||
data['likes'] = sub.likes | |||
data['gold'] = sub.gilded > 0 | |||
return data | |||
class SubmissionContent(BaseContent): | |||
""" | |||
Grab a submission from PRAW and lazily store comments to an internal | |||
list for repeat access. | |||
""" | |||
def __init__( | |||
self, | |||
submission, | |||
loader, | |||
indent_size=2, | |||
max_indent_level=4): | |||
def __init__(self, submission, loader, indent_size=2, max_indent_level=4): | |||
self.indent_size = indent_size | |||
self.max_indent_level = max_indent_level | |||
@@ -136,13 +132,7 @@ class SubmissionContent(BaseContent): | |||
self._comment_data = [self.strip_praw_comment(c) for c in comments] | |||
@classmethod | |||
def from_url( | |||
cls, | |||
reddit, | |||
url, | |||
loader, | |||
indent_size=2, | |||
max_indent_level=4): | |||
def from_url(cls, reddit, url, loader, indent_size=2, max_indent_level=4): | |||
try: | |||
with loader(): | |||
@@ -163,10 +153,9 @@ class SubmissionContent(BaseContent): | |||
elif index == -1: | |||
data = self._submission_data | |||
data['split_title'] = textwrap.wrap(data['title'], | |||
width=n_cols -2) | |||
data['split_title'] = textwrap.wrap(data['title'], width=n_cols -2) | |||
data['split_text'] = wrap_text(data['text'], width=n_cols - 2) | |||
data['n_rows'] = len(data['split_title']) + len(data['split_text']) + 5 | |||
data['n_rows'] = len(data['split_title'] + data['split_text']) + 5 | |||
data['offset'] = 0 | |||
else: | |||
@@ -231,9 +220,8 @@ class SubmissionContent(BaseContent): | |||
class SubredditContent(BaseContent): | |||
""" | |||
Grabs a subreddit from PRAW and lazily stores submissions to an internal | |||
Grab a subreddit from PRAW and lazily stores submissions to an internal | |||
list for repeat access. | |||
""" | |||
@@ -244,81 +232,78 @@ class SubredditContent(BaseContent): | |||
self._submissions = submissions | |||
self._submission_data = [] | |||
# Verify that content exists for the given submission generator. | |||
# This is necessary because PRAW loads submissions lazily, and | |||
# there is is no other way to check things like multireddits that | |||
# don't have a real corresponding subreddit object. | |||
try: | |||
self.get(0) | |||
except (praw.errors.APIException, requests.HTTPError, | |||
praw.errors.RedirectException): | |||
raise SubredditError(display_name) | |||
@classmethod | |||
def from_name(cls, reddit, name, loader, order='hot', search=None): | |||
def from_name(cls, reddit, name, loader, order='hot', query=None): | |||
if name is None: | |||
name = 'front' | |||
if order not in ['hot', 'top', 'rising', 'new', 'controversial']: | |||
raise SubredditError(display_name) | |||
name = name.strip(' /') # Strip leading and trailing backslashes | |||
if name.startswith('r/'): | |||
name = name[2:] | |||
# Grab the display type e.g. "python/new" | |||
# Grab the display order e.g. "python/new" | |||
if '/' in name: | |||
name, order = name.split('/') | |||
if order == 'hot': | |||
display_name = '/r/{}'.format(name) | |||
else: | |||
display_name = '/r/{}/{}'.format(name, order) | |||
if name == 'front': | |||
if search: | |||
submissions = reddit.search(search, None, order) | |||
elif order == 'hot': | |||
submissions = reddit.get_front_page(limit=None) | |||
elif order == 'top': | |||
submissions = reddit.get_top(limit=None) | |||
elif order == 'rising': | |||
submissions = reddit.get_rising(limit=None) | |||
elif order == 'new': | |||
submissions = reddit.get_new(limit=None) | |||
elif order == 'controversial': | |||
submissions = reddit.get_controversial(limit=None) | |||
else: | |||
raise SubredditError(display_name) | |||
else: | |||
subreddit = reddit.get_subreddit(name) | |||
if search: | |||
submissions = reddit.search(search, name, order) | |||
elif order == 'hot': | |||
submissions = subreddit.get_hot(limit=None) | |||
elif order == 'top': | |||
submissions = subreddit.get_top(limit=None) | |||
elif order == 'rising': | |||
submissions = subreddit.get_rising(limit=None) | |||
elif order == 'new': | |||
submissions = subreddit.get_new(limit=None) | |||
elif order == 'controversial': | |||
submissions = subreddit.get_controversial(limit=None) | |||
display_name = display_name = '/r/{}'.format(name) | |||
if order != 'hot': | |||
display_name += '/{}'.format(order) | |||
if name == 'me': | |||
if not reddit.is_logged_in(): | |||
raise AccountError | |||
else: | |||
raise SubredditError(display_name) | |||
submissions = reddit.user.get_submitted(sort=order) | |||
# Verify that content exists for the given submission generator. | |||
# This is necessary because PRAW loads submissions lazily, and | |||
# there is is no other way to check things like multireddits that | |||
# don't have a real corresponding subreddit object. | |||
content = cls(display_name, submissions, loader) | |||
try: | |||
content.get(0) | |||
except (praw.errors.APIException, requests.HTTPError, | |||
praw.errors.RedirectException): | |||
raise SubredditError(display_name) | |||
elif query: | |||
if name == 'front': | |||
submissions = reddit.search(query, subreddit=None, sort=order) | |||
else: | |||
submissions = reddit.search(query, subreddit=name, sort=order) | |||
return content | |||
else: | |||
if name == 'front': | |||
dispatch = { | |||
'hot': reddit.get_front_page, | |||
'top': reddit.get_top, | |||
'rising': reddit.get_rising, | |||
'new': reddit.get_new, | |||
'controversial': reddit.get_controversial, | |||
} | |||
else: | |||
subreddit = reddit.get_subreddit(name) | |||
dispatch = { | |||
'hot': subreddit.get_hot, | |||
'top': subreddit.get_top, | |||
'rising': subreddit.get_rising, | |||
'new': subreddit.get_new, | |||
'controversial': subreddit.get_controversial, | |||
} | |||
submissions = dispatch[order](limit=None) | |||
return cls(display_name, submissions, loader) | |||
def get(self, index, n_cols=70): | |||
""" | |||
Grab the `i`th submission, with the title field formatted to fit inside | |||
of a window of width `n` | |||
of a window of width `n_cols` | |||
""" | |||
if index < 0: | |||
raise IndexError | |||
while index >= len(self._submission_data): | |||
try: | |||
with self._loader(): | |||
submission = next(self._submissions) | |||
@@ -334,4 +319,4 @@ class SubredditContent(BaseContent): | |||
data['n_rows'] = len(data['split_title']) + 3 | |||
data['offset'] = 0 | |||
return data | |||
return data |
@@ -22,6 +22,7 @@ ESCAPE = 27 | |||
UARROW = u'\u25b2'.encode('utf-8') | |||
DARROW = u'\u25bc'.encode('utf-8') | |||
BULLET = u'\u2022'.encode('utf-8') | |||
GOLD = u'\u272A'.encode('utf-8') | |||
def show_notification(stdscr, message): | |||
@@ -37,10 +38,10 @@ def show_notification(stdscr, message): | |||
box_width = max(map(len, message)) + 2 | |||
box_height = len(message) + 2 | |||
# Make sure the window is large enough to fit the message | |||
if (box_width > n_cols) or (box_height > n_rows): | |||
curses.flash() | |||
return | |||
# Cut off the lines of the message that don't fit on the screen | |||
box_width = min(box_width, n_cols) | |||
box_height = min(box_height, n_rows) | |||
message = message[:box_height-2] | |||
s_row = (n_rows - box_height) // 2 | |||
s_col = (n_cols - box_width) // 2 | |||
@@ -50,20 +51,23 @@ def show_notification(stdscr, message): | |||
window.border() | |||
for index, line in enumerate(message, start=1): | |||
window.addstr(index, 1, line) | |||
window.addnstr(index, 1, line, box_width - 2) | |||
window.refresh() | |||
stdscr.getch() | |||
ch = stdscr.getch() | |||
window.clear() | |||
window = None | |||
stdscr.refresh() | |||
return ch | |||
def show_help(stdscr): | |||
""" | |||
Overlay a message box with the help screen. | |||
""" | |||
show_notification(stdscr, HELP.split("\n")) | |||
show_notification(stdscr, HELP.splitlines()) | |||
class LoadScreen(object): |
@@ -1,8 +1,11 @@ | |||
from .__version__ import __version__ | |||
__all__ = ['AGENT', 'SUMMARY', 'AUTH', 'CONTROLS', 'HELP'] | |||
__all__ = ['AGENT', 'SUMMARY', 'AUTH', 'CONTROLS', 'HELP', 'COMMENT_FILE', | |||
'SUBMISSION_FILE'] | |||
AGENT = "desktop:https://github.com/michael-lazar/rtv:{} (by /u/civilization_phaze_3)".format(__version__) | |||
AGENT = """\ | |||
desktop:https://github.com/michael-lazar/rtv:{} (by /u/civilization_phaze_3)\ | |||
""".format(__version__) | |||
SUMMARY = """ | |||
Reddit Terminal Viewer is a lightweight browser for www.reddit.com built into a | |||
@@ -28,20 +31,22 @@ HELP = """ | |||
Global Commands | |||
`UP/DOWN` or `j/k` : Scroll to the prev/next item | |||
`a/z` : Upvote/downvote the selected item | |||
`r` : Refresh the current page | |||
`q` : Quit the program | |||
`ENTER` or `o` : Open the selected item in the default web browser | |||
`r` : Refresh the current page | |||
`u` : Login/logout of your user account | |||
`?` : Show this help message | |||
`q` : Quit the program | |||
Subreddit Mode | |||
`RIGHT` or `l` : View comments for the selected submission | |||
`/` : Open a prompt to switch subreddits | |||
`f` : Open a prompt to search the current subreddit | |||
`p` : Post a new submission to the current subreddit | |||
Submission Mode | |||
`LEFT` or `h` : Return to subreddit mode | |||
`RIGHT` or `l` : Fold the selected comment, or load additional comments | |||
`c` : Comment/reply on the selected item | |||
`c` : Post a new comment on the selected item | |||
""" | |||
COMMENT_FILE = """ | |||
@@ -51,3 +56,13 @@ COMMENT_FILE = """ | |||
# Replying to {author}'s {type} | |||
{content} | |||
""" | |||
SUBMISSION_FILE = """ | |||
# Please enter your submission. Lines starting with '#' will be ignored, | |||
# and an empty field aborts the submission. | |||
# | |||
# The first line will be interpreted as the title | |||
# The following lines will be interpreted as the content | |||
# | |||
# Posting to /r/{name} | |||
""" |
@@ -1,23 +1,31 @@ | |||
class SubmissionError(Exception): | |||
"""Submission could not be loaded""" | |||
class EscapeInterrupt(Exception): | |||
"Signal that the ESC key has been pressed" | |||
class RTVError(Exception): | |||
"Base RTV error class" | |||
class AccountError(RTVError): | |||
"Could not access user account" | |||
class SubmissionError(RTVError): | |||
"Submission could not be loaded" | |||
def __init__(self, url): | |||
self.url = url | |||
class SubredditError(Exception): | |||
"""Subreddit could not be reached""" | |||
class SubredditError(RTVError): | |||
"Subreddit could not be reached" | |||
def __init__(self, name): | |||
self.name = name | |||
class ProgramError(Exception): | |||
"""Problem executing an external program""" | |||
class ProgramError(RTVError): | |||
"Problem executing an external program" | |||
def __init__(self, name): | |||
self.name = name | |||
class EscapeInterrupt(Exception): | |||
"""Signal that the ESC key has been pressed""" |
@@ -6,12 +6,12 @@ import praw.errors | |||
from .helpers import clean | |||
from .curses_helpers import Color, show_notification, show_help, text_input | |||
from .docs import AGENT | |||
__all__ = ['Navigator'] | |||
class Navigator(object): | |||
""" | |||
Handles math behind cursor movement and screen paging. | |||
""" | |||
@@ -86,6 +86,7 @@ class Navigator(object): | |||
def flip(self, n_windows): | |||
"Flip the orientation of the page" | |||
self.page_index += (self.step * n_windows) | |||
self.cursor_index = n_windows | |||
self.inverted = not self.inverted | |||
@@ -102,7 +103,6 @@ class Navigator(object): | |||
class BaseController(object): | |||
""" | |||
Event handler for triggering functions with curses keypresses. | |||
@@ -152,7 +152,6 @@ class BaseController(object): | |||
class BasePage(object): | |||
""" | |||
Base terminal viewer incorperates a cursor to navigate content | |||
""" | |||
@@ -177,7 +176,7 @@ class BasePage(object): | |||
@BaseController.register('?') | |||
def help(self): | |||
show_help(self.stdscr) | |||
show_help(self._content_window) | |||
@BaseController.register(curses.KEY_UP, 'k') | |||
def move_cursor_up(self): | |||
@@ -191,6 +190,7 @@ class BasePage(object): | |||
def clear_input_queue(self): | |||
"Clear excessive input caused by the scroll wheel or holding down a key" | |||
self.stdscr.nodelay(1) | |||
while self.stdscr.getch() != -1: | |||
continue | |||
@@ -209,7 +209,7 @@ class BasePage(object): | |||
data['object'].upvote() | |||
data['likes'] = True | |||
except praw.errors.LoginOrScopeRequired: | |||
show_notification(self.stdscr, ['Login to vote']) | |||
show_notification(self.stdscr, ['Not logged in']) | |||
@BaseController.register('z') | |||
def downvote(self): | |||
@@ -224,19 +224,61 @@ class BasePage(object): | |||
data['object'].downvote() | |||
data['likes'] = False | |||
except praw.errors.LoginOrScopeRequired: | |||
show_notification(self.stdscr, ['Login to vote']) | |||
show_notification(self.stdscr, ['Not logged in']) | |||
@BaseController.register('u') | |||
def login(self): | |||
""" | |||
Prompt to log into the user's account, or log out of the current | |||
account. | |||
""" | |||
if self.reddit.is_logged_in(): | |||
self.logout() | |||
return | |||
username = self.prompt_input('Enter username:') | |||
password = self.prompt_input('Enter password:', hide=True) | |||
if not username or not password: | |||
curses.flash() | |||
return | |||
try: | |||
with self.loader(): | |||
self.reddit.login(username, password) | |||
except praw.errors.InvalidUserPass: | |||
show_notification(self.stdscr, ['Invalid user/pass']) | |||
else: | |||
show_notification(self.stdscr, ['Welcome {}'.format(username)]) | |||
def logout(self): | |||
"Prompt to log out of the user's account." | |||
ch = self.prompt_input("Log out? (y/n):") | |||
if ch == 'y': | |||
self.reddit.clear_authentication() | |||
show_notification(self.stdscr, ['Logged out']) | |||
elif ch != 'n': | |||
curses.flash() | |||
def prompt_input(self, prompt, hide=False): | |||
"Prompt the user for input" | |||
def prompt_input(self, prompt): | |||
"""Prompt the user for input""" | |||
attr = curses.A_BOLD | Color.CYAN | |||
n_rows, n_cols = self.stdscr.getmaxyx() | |||
self.stdscr.addstr(n_rows - 1, 0, prompt, attr) | |||
self.stdscr.refresh() | |||
window = self.stdscr.derwin(1, n_cols - len(prompt), | |||
n_rows - 1, len(prompt)) | |||
window.attrset(attr) | |||
out = text_input(window) | |||
if hide: | |||
prompt += ' ' * (n_cols - len(prompt) - 1) | |||
self.stdscr.addstr(n_rows-1, 0, prompt, attr) | |||
out = self.stdscr.getstr(n_rows-1, 1) | |||
else: | |||
self.stdscr.addstr(n_rows - 1, 0, prompt, attr) | |||
self.stdscr.refresh() | |||
window = self.stdscr.derwin(1, n_cols - len(prompt), | |||
n_rows - 1, len(prompt)) | |||
window.attrset(attr) | |||
out = text_input(window) | |||
return out | |||
def draw(self): | |||
@@ -354,4 +396,4 @@ class BasePage(object): | |||
for row in range(n_rows): | |||
window.chgat(row, 0, 1, attribute) | |||
window.refresh() | |||
window.refresh() |
@@ -1,18 +1,20 @@ | |||
import curses | |||
import sys | |||
import time | |||
import logging | |||
import praw.errors | |||
from .content import SubmissionContent | |||
from .page import BasePage, Navigator, BaseController | |||
from .helpers import clean, open_browser, open_editor | |||
from .curses_helpers import (BULLET, UARROW, DARROW, Color, LoadScreen, | |||
from .curses_helpers import (BULLET, UARROW, DARROW, GOLD, Color, LoadScreen, | |||
show_notification, text_input) | |||
from .docs import COMMENT_FILE | |||
__all__ = ['SubmissionController', 'SubmissionPage'] | |||
_logger = logging.getLogger(__name__) | |||
class SubmissionController(BaseController): | |||
character_map = {} | |||
@@ -24,17 +26,19 @@ class SubmissionPage(BasePage): | |||
self.controller = SubmissionController(self) | |||
self.loader = LoadScreen(stdscr) | |||
if url is not None: | |||
if url: | |||
content = SubmissionContent.from_url(reddit, url, self.loader) | |||
elif submission is not None: | |||
elif submission: | |||
content = SubmissionContent(submission, self.loader) | |||
else: | |||
raise ValueError('Must specify url or submission') | |||
super(SubmissionPage, self).__init__(stdscr, reddit, content, | |||
page_index=-1) | |||
super(SubmissionPage, self).__init__(stdscr, reddit, | |||
content, page_index=-1) | |||
def loop(self): | |||
"Main control loop" | |||
self.active = True | |||
while self.active: | |||
self.draw() | |||
@@ -43,6 +47,8 @@ class SubmissionPage(BasePage): | |||
@SubmissionController.register(curses.KEY_RIGHT, 'l') | |||
def toggle_comment(self): | |||
"Toggle the selected comment tree between visible and hidden" | |||
current_index = self.nav.absolute_index | |||
self.content.toggle(current_index) | |||
if self.nav.inverted: | |||
@@ -53,20 +59,24 @@ class SubmissionPage(BasePage): | |||
@SubmissionController.register(curses.KEY_LEFT, 'h') | |||
def exit_submission(self): | |||
"Close the submission and return to the subreddit page" | |||
self.active = False | |||
@SubmissionController.register(curses.KEY_F5, 'r') | |||
def refresh_content(self): | |||
url = self.content.name | |||
"Re-download comments reset the page index" | |||
self.content = SubmissionContent.from_url( | |||
self.reddit, | |||
url, | |||
self.content.name, | |||
self.loader) | |||
self.nav = Navigator(self.content.get, page_index=-1) | |||
@SubmissionController.register(curses.KEY_ENTER, 10, 'o') | |||
def open_link(self): | |||
# Always open the page for the submission | |||
"Open the current submission page with the webbrowser" | |||
# May want to expand at some point to open comment permalinks | |||
url = self.content.get(-1)['permalink'] | |||
open_browser(url) | |||
@@ -74,11 +84,12 @@ class SubmissionPage(BasePage): | |||
@SubmissionController.register('c') | |||
def add_comment(self): | |||
""" | |||
Add a comment on the submission if a header is selected. | |||
Reply to a comment if the comment is selected. | |||
Add a top-level comment if the submission is selected, or reply to the | |||
selected comment. | |||
""" | |||
if not self.reddit.is_logged_in(): | |||
show_notification(self.stdscr, ["Login to reply"]) | |||
show_notification(self.stdscr, ['Login to post']) | |||
return | |||
data = self.content.get(self.nav.absolute_index) | |||
@@ -100,19 +111,25 @@ class SubmissionPage(BasePage): | |||
curses.endwin() | |||
comment_text = open_editor(comment_info) | |||
curses.doupdate() | |||
if not comment_text: | |||
curses.flash() | |||
show_notification(self.stdscr, ['Comment canceled']) | |||
return | |||
try: | |||
if data['type'] == 'Submission': | |||
data['object'].add_comment(comment_text) | |||
else: | |||
data['object'].reply(comment_text) | |||
except praw.errors.APIException as e: | |||
show_notification(self.stdscr, [e.message]) | |||
except praw.errors.APIException: | |||
message = ['Error: {}'.format(e.error_type), e.message] | |||
show_notification(self.stdscr, message) | |||
_logger.exception(e) | |||
except requests.HTTPError as e: | |||
show_notification(self.stdscr, ['Unexpected Error']) | |||
_logger.exception(e) | |||
else: | |||
time.sleep(0.5) | |||
with self.loader(delay=0, message='Posting'): | |||
time.sleep(2.0) | |||
self.refresh_content() | |||
def draw_item(self, win, data, inverted=False): | |||
@@ -139,13 +156,13 @@ class SubmissionPage(BasePage): | |||
row = offset | |||
if row in valid_rows: | |||
text = clean('{author} '.format(**data)) | |||
text = clean(u'{author} '.format(**data)) | |||
attr = curses.A_BOLD | |||
attr |= (Color.BLUE if not data['is_author'] else Color.GREEN) | |||
win.addnstr(row, 1, text, n_cols - 1, attr) | |||
if data['flair']: | |||
text = clean('{flair} '.format(**data)) | |||
text = clean(u'{flair} '.format(**data)) | |||
attr = curses.A_BOLD | Color.YELLOW | |||
win.addnstr(text, n_cols - win.getyx()[1], attr) | |||
@@ -157,9 +174,13 @@ class SubmissionPage(BasePage): | |||
text, attr = DARROW, (curses.A_BOLD | Color.RED) | |||
win.addnstr(text, n_cols - win.getyx()[1], attr) | |||
text = clean(' {score} {created}'.format(**data)) | |||
text = clean(u' {score} {created} '.format(**data)) | |||
win.addnstr(text, n_cols - win.getyx()[1]) | |||
if data['gold']: | |||
text, attr = GOLD, (curses.A_BOLD | Color.YELLOW) | |||
win.addnstr(text, n_cols - win.getyx()[1], attr) | |||
n_body = len(data['split_body']) | |||
for row, text in enumerate(data['split_body'], start=offset + 1): | |||
if row in valid_rows: | |||
@@ -187,9 +208,9 @@ class SubmissionPage(BasePage): | |||
n_rows, n_cols = win.getmaxyx() | |||
n_cols -= 1 | |||
text = clean('{body}'.format(**data)) | |||
text = clean(u'{body}'.format(**data)) | |||
win.addnstr(0, 1, text, n_cols - 1) | |||
text = clean(' [{count}]'.format(**data)) | |||
text = clean(u' [{count}]'.format(**data)) | |||
win.addnstr(text, n_cols - win.getyx()[1], curses.A_BOLD) | |||
# Unfortunately vline() doesn't support custom color so we have to | |||
@@ -211,17 +232,17 @@ class SubmissionPage(BasePage): | |||
row = len(data['split_title']) + 1 | |||
attr = curses.A_BOLD | Color.GREEN | |||
text = clean('{author}'.format(**data)) | |||
text = clean(u'{author}'.format(**data)) | |||
win.addnstr(row, 1, text, n_cols, attr) | |||
attr = curses.A_BOLD | Color.YELLOW | |||
text = clean(' {flair}'.format(**data)) | |||
text = clean(u' {flair}'.format(**data)) | |||
win.addnstr(text, n_cols - win.getyx()[1], attr) | |||
text = clean(' {created} {subreddit}'.format(**data)) | |||
text = clean(u' {created} {subreddit}'.format(**data)) | |||
win.addnstr(text, n_cols - win.getyx()[1]) | |||
row = len(data['split_title']) + 2 | |||
attr = curses.A_UNDERLINE | Color.BLUE | |||
text = clean('{url}'.format(**data)) | |||
text = clean(u'{url}'.format(**data)) | |||
win.addnstr(row, 1, text, n_cols, attr) | |||
offset = len(data['split_title']) + 3 | |||
@@ -237,7 +258,11 @@ class SubmissionPage(BasePage): | |||
win.addnstr(row, 1, text, n_cols) | |||
row = len(data['split_title']) + len(split_text) + 3 | |||
text = clean('{score} {comments}'.format(**data)) | |||
text = clean(u'{score} {comments} '.format(**data)) | |||
win.addnstr(row, 1, text, n_cols, curses.A_BOLD) | |||
win.border() | |||
if data['gold']: | |||
text, attr = GOLD, (curses.A_BOLD | Color.YELLOW) | |||
win.addnstr(text, n_cols - win.getyx()[1], attr) | |||
win.border() |
@@ -1,17 +1,23 @@ | |||
import curses | |||
import time | |||
import logging | |||
import requests | |||
import praw | |||
from .exceptions import SubredditError | |||
from .exceptions import SubredditError, AccountError | |||
from .page import BasePage, Navigator, BaseController | |||
from .submission import SubmissionPage | |||
from .content import SubredditContent | |||
from .helpers import clean, open_browser | |||
from .curses_helpers import (BULLET, UARROW, DARROW, Color, LoadScreen, | |||
show_notification) | |||
from .helpers import clean, open_browser, open_editor | |||
from .docs import SUBMISSION_FILE | |||
from .curses_helpers import (BULLET, UARROW, DARROW, GOLD, Color, | |||
LoadScreen, show_notification) | |||
__all__ = ['opened_links', 'SubredditController', 'SubredditPage'] | |||
_logger = logging.getLogger(__name__) | |||
# Used to keep track of browsing history across the current session | |||
opened_links = set() | |||
@@ -31,6 +37,8 @@ class SubredditPage(BasePage): | |||
super(SubredditPage, self).__init__(stdscr, reddit, content) | |||
def loop(self): | |||
"Main control loop" | |||
while True: | |||
self.draw() | |||
cmd = self.stdscr.getch() | |||
@@ -38,11 +46,14 @@ class SubredditPage(BasePage): | |||
@SubredditController.register(curses.KEY_F5, 'r') | |||
def refresh_content(self, name=None): | |||
name = name or self.content.name | |||
"Re-download all submissions and reset the page index" | |||
name = name or self.content.name | |||
try: | |||
self.content = SubredditContent.from_name( | |||
self.reddit, name, self.loader) | |||
except AccountError: | |||
show_notification(self.stdscr, ['Not logged in']) | |||
except SubredditError: | |||
show_notification(self.stdscr, ['Invalid subreddit']) | |||
except requests.HTTPError: | |||
@@ -52,21 +63,25 @@ class SubredditPage(BasePage): | |||
@SubredditController.register('f') | |||
def search_subreddit(self, name=None): | |||
"""Open a prompt to search the subreddit""" | |||
"Open a prompt to search the given subreddit" | |||
name = name or self.content.name | |||
prompt = 'Search this Subreddit: ' | |||
search = self.prompt_input(prompt) | |||
if search is not None: | |||
try: | |||
self.nav.cursor_index = 0 | |||
self.content = SubredditContent.from_name(self.reddit, name, | |||
self.loader, search=search) | |||
except IndexError: # if there are no submissions | |||
show_notification(self.stdscr, ['No results found']) | |||
prompt = 'Search {}:'.format(name) | |||
query = self.prompt_input(prompt) | |||
if query is None: | |||
return | |||
try: | |||
self.content = SubredditContent.from_name( | |||
self.reddit, name, self.loader, query=query) | |||
except IndexError: # if there are no submissions | |||
show_notification(self.stdscr, ['No results found']) | |||
else: | |||
self.nav = Navigator(self.content.get) | |||
@SubredditController.register('/') | |||
def prompt_subreddit(self): | |||
"""Open a prompt to type in a new subreddit""" | |||
"Open a prompt to navigate to a different subreddit" | |||
prompt = 'Enter Subreddit: /r/' | |||
name = self.prompt_input(prompt) | |||
if name is not None: | |||
@@ -94,6 +109,52 @@ class SubredditPage(BasePage): | |||
global opened_links | |||
opened_links.add(url) | |||
@SubredditController.register('p') | |||
def post_submission(self): | |||
"Post a new submission to the given subreddit" | |||
if not self.reddit.is_logged_in(): | |||
show_notification(self.stdscr, ['Login to post']) | |||
return | |||
# Strips the subreddit to just the name | |||
# Make sure it is a valid subreddit for submission | |||
subreddit = self.reddit.get_subreddit(self.content.name) | |||
sub = str(subreddit).split('/')[2] | |||
if '+' in sub or sub in ('all', 'front', 'me'): | |||
show_notification(self.stdscr, ['Invalid subreddit']) | |||
return | |||
# Open the submission window | |||
submission_info = SUBMISSION_FILE.format(name=sub) | |||
curses.endwin() | |||
submission_text = open_editor(submission_info) | |||
curses.doupdate() | |||
# Validate the submission content | |||
if not submission_text: | |||
show_notification(self.stdscr, ['Post canceled']) | |||
return | |||
if '\n' not in submission_text: | |||
show_notification(self.stdscr, ['No content']) | |||
return | |||
try: | |||
title, content = submission_text.split('\n', 1) | |||
self.reddit.submit(sub, title, text=content) | |||
except praw.errors.APIException as e: | |||
message = ['Error: {}'.format(e.error_type), e.message] | |||
show_notification(self.stdscr, message) | |||
_logger.exception(e) | |||
except requests.HTTPError as e: | |||
show_notification(self.stdscr, ['Unexpected Error']) | |||
_logger.exception(e) | |||
else: | |||
with self.loader(delay=0, message='Posting'): | |||
time.sleep(2.0) | |||
self.refresh_content() | |||
@staticmethod | |||
def draw_item(win, data, inverted=False): | |||
@@ -115,12 +176,12 @@ class SubredditPage(BasePage): | |||
seen = (data['url_full'] in opened_links) | |||
link_color = Color.MAGENTA if seen else Color.BLUE | |||
attr = curses.A_UNDERLINE | link_color | |||
text = clean('{url}'.format(**data)) | |||
text = clean(u'{url}'.format(**data)) | |||
win.addnstr(row, 1, text, n_cols - 1, attr) | |||
row = n_title + offset + 1 | |||
if row in valid_rows: | |||
text = clean('{score} '.format(**data)) | |||
text = clean(u'{score} '.format(**data)) | |||
win.addnstr(row, 1, text, n_cols - 1) | |||
if data['likes'] is None: | |||
@@ -131,14 +192,18 @@ class SubredditPage(BasePage): | |||
text, attr = DARROW, curses.A_BOLD | Color.RED | |||
win.addnstr(text, n_cols - win.getyx()[1], attr) | |||
text = clean(' {created} {comments}'.format(**data)) | |||
text = clean(u' {created} {comments} '.format(**data)) | |||
win.addnstr(text, n_cols - win.getyx()[1]) | |||
if data['gold']: | |||
text, attr = GOLD, (curses.A_BOLD | Color.YELLOW) | |||
win.addnstr(text, n_cols - win.getyx()[1], attr) | |||
row = n_title + offset + 2 | |||
if row in valid_rows: | |||
text = clean('{author}'.format(**data)) | |||
text = clean(u'{author}'.format(**data)) | |||
win.addnstr(row, 1, text, n_cols - 1, curses.A_BOLD) | |||
text = clean(' {subreddit}'.format(**data)) | |||
text = clean(u' {subreddit}'.format(**data)) | |||
win.addnstr(text, n_cols - win.getyx()[1], Color.YELLOW) | |||
text = clean(' {flair}'.format(**data)) | |||
win.addnstr(text, n_cols - win.getyx()[1], Color.RED) | |||
text = clean(u' {flair}'.format(**data)) | |||
win.addnstr(text, n_cols - win.getyx()[1], Color.RED) |
@@ -0,0 +1,22 @@ | |||
The MIT License (MIT) | |||
Copyright (c) 2015 michael-lazar | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE. | |||
@@ -1,44 +0,0 @@ | |||
c4d97f939fad823f6575a9e812f3dc54fad3ad5b not-for-merge branch 'controller' of https://github.com/michael-lazar/rtv | |||
ca41b77d83abc415cd60060c43b65a3580d24ed4 not-for-merge branch 'master' of https://github.com/michael-lazar/rtv | |||
d5f68215cdbc5906c19a64c0bab2e3b737a9bb29 not-for-merge branch 'pypi' of https://github.com/michael-lazar/rtv | |||
7c7606d288b0ec306577cb1c79b3ea9cb90e4caf not-for-merge 'refs/pull/1/head' of https://github.com/michael-lazar/rtv | |||
98fc02e28e0147509ddfe4e601a97859a0e47cfc not-for-merge 'refs/pull/11/head' of https://github.com/michael-lazar/rtv | |||
6a5da9f1ec1305ce2fdf4a1e3ec3f90afc6772cb not-for-merge 'refs/pull/13/head' of https://github.com/michael-lazar/rtv | |||
9fcb16b3e48467566140db4a37817cbe82116eb9 not-for-merge 'refs/pull/14/head' of https://github.com/michael-lazar/rtv | |||
b5bfd40b1a0e1119cdbd7e6d5b13b11fcb260fc5 not-for-merge 'refs/pull/16/head' of https://github.com/michael-lazar/rtv | |||
f1d4b5909b8490551b6fc8ff70cc322cab42ce48 not-for-merge 'refs/pull/16/merge' of https://github.com/michael-lazar/rtv | |||
cb5d730d39943b130abec75d4c8c20e54ec7ac54 not-for-merge 'refs/pull/18/head' of https://github.com/michael-lazar/rtv | |||
505a49552dd3f016ec4993f237a7bbe2b407b9cf not-for-merge 'refs/pull/18/merge' of https://github.com/michael-lazar/rtv | |||
73af45f4a20cda0392dad41dd3e6f37d4c470ec3 not-for-merge 'refs/pull/19/head' of https://github.com/michael-lazar/rtv | |||
f25ab62d48f62c027e1f8d6424d0d2ddfccd4fa2 not-for-merge 'refs/pull/20/head' of https://github.com/michael-lazar/rtv | |||
f08693988fd087a316456306459f114e637b4be6 not-for-merge 'refs/pull/22/head' of https://github.com/michael-lazar/rtv | |||
09ed21b75c7bf0291b163b8a72c5da62272b2f12 not-for-merge 'refs/pull/29/head' of https://github.com/michael-lazar/rtv | |||
3379a99e0d9502d9205dc30e69fc56f585dc7a25 not-for-merge 'refs/pull/30/head' of https://github.com/michael-lazar/rtv | |||
af86a3ad67747ea42fe2ed6a40d582c10fce319e not-for-merge 'refs/pull/31/head' of https://github.com/michael-lazar/rtv | |||
abadbd7037503973ce009b7c5e8c79150b1c375d not-for-merge 'refs/pull/33/head' of https://github.com/michael-lazar/rtv | |||
34dfa53b9f4f05a8afd051eda55dc4c79371b6dc not-for-merge 'refs/pull/34/head' of https://github.com/michael-lazar/rtv | |||
9744641d32e971f50aa90ce8f564ead70f9a1788 not-for-merge 'refs/pull/37/head' of https://github.com/michael-lazar/rtv | |||
2a21064438cb97d633696b7ea2a1d3a42a35fc76 not-for-merge 'refs/pull/37/merge' of https://github.com/michael-lazar/rtv | |||
da851ec0485f22e3e3df0332ce55c0ee650a9d9c not-for-merge 'refs/pull/38/head' of https://github.com/michael-lazar/rtv | |||
60dfc858552940d7902812e316f16383b811aa88 not-for-merge 'refs/pull/40/head' of https://github.com/michael-lazar/rtv | |||
eea306a980461d1aefff908aded5a78decaf1b36 not-for-merge 'refs/pull/41/head' of https://github.com/michael-lazar/rtv | |||
571e1e82c57519b661b1a7bdc02a541985c34f4d not-for-merge 'refs/pull/43/head' of https://github.com/michael-lazar/rtv | |||
15aba8b0eb91d5182cfa459ec516ed774834dc61 not-for-merge 'refs/pull/44/head' of https://github.com/michael-lazar/rtv | |||
32fd689544397653c8e0eca58d5d082a12075057 not-for-merge 'refs/pull/45/head' of https://github.com/michael-lazar/rtv | |||
7d461ffc58be34b4bbf671eddb5f5899e630ab38 not-for-merge 'refs/pull/46/head' of https://github.com/michael-lazar/rtv | |||
80cdf036ef3051c326ddcb19c193c0088fab09c8 not-for-merge 'refs/pull/48/head' of https://github.com/michael-lazar/rtv | |||
601336ad37f4d4d357af8e3e02e3cb19bfb8696f not-for-merge 'refs/pull/49/head' of https://github.com/michael-lazar/rtv | |||
bf512d22f5dac79f355adfee20fe827bff7d55cd not-for-merge 'refs/pull/49/merge' of https://github.com/michael-lazar/rtv | |||
e33c4315126dcd3d5c05584f2815c9c5f4a680d0 not-for-merge 'refs/pull/51/head' of https://github.com/michael-lazar/rtv | |||
bdecf095ef593745b8a725b0eec6511d03587642 not-for-merge 'refs/pull/51/merge' of https://github.com/michael-lazar/rtv | |||
e31997d392b10e763a52f26f4ad19dbdfefd8a4e not-for-merge 'refs/pull/52/head' of https://github.com/michael-lazar/rtv | |||
ac98f564a38ad61fe93a18dd802332af7383c2da not-for-merge 'refs/pull/53/head' of https://github.com/michael-lazar/rtv | |||
bdce9cb4b5484db9abda70f908798f4d7a2c7da0 not-for-merge 'refs/pull/54/head' of https://github.com/michael-lazar/rtv | |||
37c43559df699d7119df9e83d8c39bbd9194d63f not-for-merge 'refs/pull/56/head' of https://github.com/michael-lazar/rtv | |||
49977151d1f0e3ca6b14eb1e63bf7380021c76ce not-for-merge 'refs/pull/58/head' of https://github.com/michael-lazar/rtv | |||
9c7d8a34be8861eaca6b8335676a79af0d373342 not-for-merge 'refs/pull/58/merge' of https://github.com/michael-lazar/rtv | |||
edcee5c82a14ecfc6d9eca039f06580c1165c147 not-for-merge 'refs/pull/59/head' of https://github.com/michael-lazar/rtv | |||
cac1a41951439825c40274ac3c6aee638e2c4da9 not-for-merge 'refs/pull/59/merge' of https://github.com/michael-lazar/rtv | |||
dd1aa1bd4fac1ffe1c0ac72c84949cc6545a5ce4 not-for-merge 'refs/pull/60/head' of https://github.com/michael-lazar/rtv | |||
59b657de37a26d7c86fe47a517b60b576e11186a not-for-merge 'refs/pull/61/head' of https://github.com/michael-lazar/rtv | |||
8558475ae4b4f58c34a3f25a324ea8c917a35ac7 not-for-merge 'refs/pull/61/merge' of https://github.com/michael-lazar/rtv |
@@ -1,6 +1,6 @@ | |||
# pack-refs with: peeled fully-peeled | |||
c4d97f939fad823f6575a9e812f3dc54fad3ad5b refs/heads/controller | |||
ca41b77d83abc415cd60060c43b65a3580d24ed4 refs/heads/master | |||
6036d90e127e1f0fe96ecff31957b7c30946d2d7 refs/heads/master | |||
d5f68215cdbc5906c19a64c0bab2e3b737a9bb29 refs/heads/pypi | |||
7c7606d288b0ec306577cb1c79b3ea9cb90e4caf refs/pull/1/head | |||
98fc02e28e0147509ddfe4e601a97859a0e47cfc refs/pull/11/head | |||
@@ -37,9 +37,21 @@ ac98f564a38ad61fe93a18dd802332af7383c2da refs/pull/53/head | |||
bdce9cb4b5484db9abda70f908798f4d7a2c7da0 refs/pull/54/head | |||
37c43559df699d7119df9e83d8c39bbd9194d63f refs/pull/56/head | |||
49977151d1f0e3ca6b14eb1e63bf7380021c76ce refs/pull/58/head | |||
9c7d8a34be8861eaca6b8335676a79af0d373342 refs/pull/58/merge | |||
edcee5c82a14ecfc6d9eca039f06580c1165c147 refs/pull/59/head | |||
cac1a41951439825c40274ac3c6aee638e2c4da9 refs/pull/59/merge | |||
dd1aa1bd4fac1ffe1c0ac72c84949cc6545a5ce4 refs/pull/60/head | |||
59b657de37a26d7c86fe47a517b60b576e11186a refs/pull/61/head | |||
8558475ae4b4f58c34a3f25a324ea8c917a35ac7 refs/pull/61/merge | |||
c7a760791a6e9c168f5ff73dc614853bfdc89d0f refs/pull/61/head | |||
8f14be279713920eda7432d721b159cdad6b468e refs/pull/63/head | |||
f1f5a7db21ab3b50b1082cd5f85c18d2602adc77 refs/pull/66/head | |||
ce12b5cbf2d2ab1fb4bc27112c84c48b39372e4f refs/pull/66/merge | |||
2f4f706d0c78cf5f6eb88ef9e9b77e5925e190ba refs/pull/68/head | |||
ee9d5bc23d1b827bab17cc47d5fc43c60542f1a6 refs/pull/68/merge | |||
dcdfd643cc70d0dc6561d517d4200b43ab350002 refs/pull/69/head | |||
601c13338f535cf540bce346cc282e6a48ba020e refs/pull/72/head | |||
b2bf27d242a2735dcfe97df7a6d9873b010889cf refs/pull/72/merge | |||
a294010e27568460f65d556709a1cfb1c5df5dc5 refs/pull/73/head | |||
4b326cf218292910a98db62a66edc488d71efba0 refs/pull/74/head | |||
b0124560be42f7c4f85ffb7d467dff7b2ac63cf3 refs/pull/75/head | |||
f60e85ef54a3b9385bd0793837ca00e2e7f6fcdb refs/pull/79/head | |||
8c382320c1810b095935cacdce768fa5e345cb1c refs/pull/79/merge | |||
6036d90e127e1f0fe96ecff31957b7c30946d2d7 refs/tags/v1.2 |
@@ -1,7 +1,7 @@ | |||
# Maintainer: John Jenkins twodopeshaggy@gmail.com | |||
pkgname=rtv | |||
pkgver=1.2 | |||
pkgver=1.2.1 | |||
pkgrel=1 | |||
pkgdesc="Browse Reddit from your terminal" | |||
arch=('any') | |||
@@ -9,9 +9,11 @@ url="https://github.com/michael-lazar/rtv" | |||
license=('MIT') | |||
depends=('ncurses' 'python' 'python-six' 'python-requests' 'python-praw' 'python-setuptools') | |||
source=(https://github.com/michael-lazar/rtv/archive/v$pkgver.tar.gz) | |||
md5sums=('b67388a428ae06e45e8522b0f56037b1') | |||
md5sums=('10f2f66a289c04ef7c3522726b8f0823') | |||
package() { | |||
cd "$srcdir/$pkgname-$pkgver" | |||
python setup.py install --root="$pkgdir/" --optimize=1 | |||
mkdir -p $pkgdir/usr/share/licenses/$pkgname | |||
install -m 0644 LICENSE $pkgdir/usr/share/licenses/$pkgname/ | |||
} |
@@ -1,13 +1,13 @@ | |||
# Generated by makepkg 4.2.1 | |||
# using fakeroot version 1.20.2 | |||
# Mon Apr 6 02:40:49 UTC 2015 | |||
# Mon Apr 6 04:59:04 UTC 2015 | |||
pkgname = rtv | |||
pkgver = 1.2-1 | |||
pkgver = 1.2.1-1 | |||
pkgdesc = Browse Reddit from your terminal | |||
url = https://github.com/michael-lazar/rtv | |||
builddate = 1428288049 | |||
builddate = 1428296344 | |||
packager = Unknown Packager | |||
size = 217088 | |||
size = 230400 | |||
arch = any | |||
license = MIT | |||
depend = ncurses |
@@ -1,10 +1,10 @@ | |||
#!/bin/python | |||
# EASY-INSTALL-ENTRY-SCRIPT: 'rtv==1.2','console_scripts','rtv' | |||
__requires__ = 'rtv==1.2' | |||
# EASY-INSTALL-ENTRY-SCRIPT: 'rtv==1.2.1','console_scripts','rtv' | |||
__requires__ = 'rtv==1.2.1' | |||
import sys | |||
from pkg_resources import load_entry_point | |||
if __name__ == '__main__': | |||
sys.exit( | |||
load_entry_point('rtv==1.2', 'console_scripts', 'rtv')() | |||
load_entry_point('rtv==1.2.1', 'console_scripts', 'rtv')() | |||
) |
@@ -1,6 +1,6 @@ | |||
Metadata-Version: 1.1 | |||
Name: rtv | |||
Version: 1.1.2 | |||
Version: 1.2.1 | |||
Summary: A simple terminal viewer for Reddit (Reddit Terminal Viewer) | |||
Home-page: https://github.com/michael-lazar/rtv | |||
Author: Michael Lazar | |||
@@ -69,7 +69,8 @@ Description: .. image:: https://pypip.in/version/rtv/badge.svg?text=version&styl | |||
:``a``/``z``: Upvote/downvote the selected item | |||
:``ENTER`` or ``o``: Open the selected item in the default web browser | |||
:``r``: Refresh the current page | |||
:``?``: Show the help message | |||
:``u``: Login and logout of your user account | |||
:``?``: Show the help screen | |||
:``q``: Quit | |||
**Subreddit Mode** | |||
@@ -79,6 +80,7 @@ Description: .. image:: https://pypip.in/version/rtv/badge.svg?text=version&styl | |||
:``►`` or ``l``: View comments for the selected submission | |||
:``/``: Open a prompt to switch subreddits | |||
:``f``: Open a prompt to search the current subreddit | |||
:``p``: Post a new submission to the current subreddit | |||
The ``/`` prompt accepts subreddits in the following formats | |||
@@ -86,6 +88,7 @@ Description: .. image:: https://pypip.in/version/rtv/badge.svg?text=version&styl | |||
* ``/r/python/new`` | |||
* ``/r/python+linux`` supports multireddits | |||
* ``/r/front`` will redirect to the front page | |||
* ``/r/me`` will display your submissions | |||
**Submission Mode** | |||
@@ -93,29 +96,37 @@ Description: .. image:: https://pypip.in/version/rtv/badge.svg?text=version&styl | |||
:``◄`` or ``h``: Return to subreddit mode | |||
:``►`` or ``l``: Fold the selected comment, or load additional comments | |||
:``c``: Comment/reply on the selected item | |||
:``c``: Post a new comment on the selected item | |||
------------- | |||
Configuration | |||
------------- | |||
RTV will read a configuration file located at **~/.rtv**. | |||
RTV will read a configuration file located at ``$XDG_CONFIG_HOME/rtv/rtv.cfg`` or ``~/.config/rtv/rtv.cfg`` if ``$XDG_CONFIG_HOME`` is not set. | |||
This can be used to avoid having to re-enter login credentials every time the program is launched. | |||
Each line in the file will replace the corresponding default argument in the launch script. | |||
Example config: | |||
**~/.rtv** | |||
.. code-block:: ini | |||
[rtv] | |||
username=MyUsername | |||
password=MySecretPassword | |||
# Log file location | |||
log=/tmp/rtv.log | |||
# Default subreddit | |||
subreddit=CollegeBasketball | |||
# Default submission link - will be opened every time the program starts | |||
# link=http://www.reddit.com/r/CollegeBasketball/comments/31irjq | |||
# Enable unicode characters (experimental) | |||
# This is known to be unstable with east asian wide character sets | |||
# unicode=true | |||
RTV allows users to compose comments and replys using their preferred text editor (**vi**, **nano**, **gedit**, etc). | |||
Set the environment variable ``RTV_EDITOR`` to specify which editor the program should use. | |||