Compare commits

..

1 Commits

Author SHA1 Message Date
pluja 3eae6f61e3 Initial commit 2020-08-05 09:20:23 +02:00
5 changed files with 73 additions and 62 deletions

View File

@ -1,13 +1,9 @@
# Parasitter
<p align="center"> <img width="150" src="app/static/img/logo.png"> </img></p>
<p align="center"> Twitter and Youtube via RSS with privacy </p>
<h3 align="center"> THIS IS A MIRROR FOR THE <a href="https://github.com/pluja/Parasitter/">MAIN REPO</a></h3>
<br>
> Important note: A [new branch](https://git.nixnet.xyz/pluja/Parasitter/src/branch/dev-indep) is being developed as I'm breaking the dependance with Invidious. You can now test it.
Parasitter allows you to follow your favorite Twitter and YouTube accounts with full privacy. Parasitter uses [Nitter's](https://nitter.net/) and [Invidious](invidio.us) rss feeds in order to gather the latest content from your favourite accounts and builds a *beautiful* feed. We will never connect you to Twitter or YouTube in any way, so your privacy is safe when using Parasitter. Parasitter is written in Python and Flask and uses Semantic-UI as its CSS framework.
Parasitter doesn't try to compete with Nitter nor Invidious. It serves as a complement, as it beneficiates from them. Parasitter is not a Twitter viewer as Nitter is or a YouTube frontend as Invidious. Instead Parasitter gathers all your accounts in one place so you can stay tuned with their latest content.
Parasitter allows you to follow your favorite Twitter and YouTube accounts with full privacy using rss feeds in order to gather the latest content from your favourite accounts and builds a *beautiful* feed so you can read them. Parasitter is written in Python and Flask and uses Semantic-UI as its CSS framework.
Parasitter is possible thanks to several open-source projects that are listed on the [Powered by](#powered-by) section. Make sure to check out those awesome projects!
@ -28,7 +24,6 @@ Parasitter is possible thanks to several open-source projects that are listed on
* Uses RSS feeds (could be expanded to more social networks)
* Follow Twitter accounts.
* Follow Youtube accounts.
* Watch Youtube videos.
* Save your favourite Tweets.
* Save your favourite Youtube videos [Coming soon!]
* Tor-friendly.
@ -43,18 +38,18 @@ Only the hash of your password is stored on the database. Also no personal infor
I always recommend self-hosting, as you will be the only person with access to the data.
## Privacy
Parasitter cares about your privacy, and for this it will never make any connection to Twitter or Youtube. We make use of rss feeds to fetch all the tweets and videos from your followed accounts. If you want to use a specific Nitter or Invidious instance you can replace it at the top of the file `app/routes.py`.
Parasitter cares about your privacy, and for this it will never make any connection to Twitter or Youtube. We make use pf rss feeds to fetch all the tweets from your followed accounts. If you want to use a specific Nitter or Invidious instance you can replace it on the file `app/routes.py`.
It is always recommended to set up a self-hosted instance. It is quite easy and conveninent and will give you full control over your data. The only data that is stored on the Database is:
* Hash of the password
* Username
* Email (Will be deprecated soon!)
* Email (we won't send you any mails so you can make up the mail) - This is for future versions.
* List of followed users
* List of saved posts
# Self hosting
### Local
> IMPORTANT: Connections to googlevideo will be made to stream the videos (as Invidious did). It is recommended to use a VPS server to preserve your privacy.
You don't need a server to run Parasitter. You can run it on your computer locally and own your (little) data. The installation process is done on a GNU/Linux environment, but should be pretty similar on other platforms.
1. Install `python3`, `pip3`, `python3-venv` (optional) and `git`.
@ -139,8 +134,8 @@ Another option is to host a Parasitter server so you can access it from anywhere
#### TO BE CONTINUED!
### Powered by:
* [Nitter](https://nitter.net)
* [Invidious](https://invidio.us)
* [RSSBridge](https://github.com/RSS-Bridge/rss-bridge)
* [youtube-dl](https://github.com/ytdl-org/youtube-dl)
* [Flask](https://flask.palletsprojects.com/)
* [SQLAlchemy](https://docs.sqlalchemy.org/en/13/)
* [Semantic-UI](https://semantic-ui.com)

View File

@ -6,6 +6,7 @@ from requests_futures.sessions import FuturesSession
from concurrent.futures import as_completed
from werkzeug.urls import url_parse
from bs4 import BeautifulSoup
from youtube_dl import YoutubeDL
from app import app, db
import random, string
import time, datetime
@ -17,7 +18,8 @@ import re
# Instances - Format must be instance.tld (No '/' and no 'https://')
nitterInstance = "https://nitter.net/"
nitterInstanceII = "https://nitter.mastodont.cat/"
invidiousInstance = "invidious.snopyta.org"
invidiousInstance = "invidio.us"
proxy = "" #eg. socks5://IP:PORT
#########################
#### Twitter Logic ######
@ -301,20 +303,24 @@ def ytunfollow(channelId):
@app.route('/video/<id>', methods=['POST', 'GET'])
@login_required
def video(id):
data = requests.get('https://{instance}/api/v1/videos/{id}'.format(instance=invidiousInstance, id=id))
data = json.loads(data.content)
if proxy:
ydl_opts = {
'proxy':proxy
}
else:
ydl_opts = {}
ydl = YoutubeDL(ydl_opts)
data = ydl.extract_info("{id}".format(id=id), download=False)
video = {
'title':data['title'],
'description':Markup(data['descriptionHtml']),
'viewCount':data['viewCount'],
'likeCount':data['likeCount'],
'dislikeCount':data['dislikeCount'],
'authorThumb':data['authorThumbnails'][4]['url'],
'author':data['author'],
'authorUrl':data['authorUrl'],
'instance':invidiousInstance,
'id':id
'description':Markup(data['description']),
'viewCount':data['view_count'],
'author':data['uploader'],
'authorUrl':data['uploader_url'],
'id':id,
'url': data['formats'][-1]['url'],
'averageRating': str((float(data['average_rating'])/5)*100)
}
return render_template("video.html", video=video)
@ -500,6 +506,8 @@ def getPosts(account):
def getInvidiousPosts(ids):
videos = []
ydl = YoutubeDL()
link = ydl.extract_info("https://www.youtube.com/watch?v=XCSfoiD8wUA", download=False)['formats'][-1]['url']
with FuturesSession() as session:
futures = [session.get('https://{instance}/feed/channel/{id}'.format(instance=invidiousInstance, id=id.channelId)) for id in ids]
for future in as_completed(futures):
@ -511,7 +519,6 @@ def getInvidiousPosts(ids):
video.timeStamp = getTimeDiff(vid.published_parsed)
video.channelName = vid.author_detail.name
video.channelUrl = vid.author_detail.href
video.videoUrl = vid.link
video.id = vid.link.split("?v=")[1]
video.videoTitle = vid.title
video.videoThumb = vid.media_thumbnail[0]['url']

20
app/static/styles.css Normal file
View File

@ -0,0 +1,20 @@
.para-light-grey{
background-color: rgb(250, 82, 82);
}
.para-green{
background-color: lightgreen;
}
.twitter{
color: rgb(0, 166, 196) !important;
}
.youtube{
color: rgb(224, 32, 32) !important;
}
.video-title{
font-weight: bold;
font-size: 1.15em;
}

View File

@ -9,21 +9,7 @@
<title>Parasitter</title>
{% endif %}
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='semantic/semantic.min.css') }}">
<style>
.twitter{
color: rgb(0, 166, 196) !important;
}
.youtube{
color: rgb(224, 32, 32) !important;
}
.video-title{
font-weight: bold;
font-size: 1.15em;
}
</style>
<link rel="stylesheet" type= "text/css" href="{{ url_for('static',filename='styles.css') }}">
</head>
<body>
<div class="ui stackable menu">

View File

@ -2,38 +2,41 @@
{% block content %}
<div style="margin-top: 2em;" class="ui one column centered grid">
<iframe id='ivplayer' width='640' height='360' src='https://{{video.instance}}/embed/{{video.id}}' style='border:none;'>
</iframe>
<video width="720" height="480" controls>
<source src="{{video.url}}" type="video/mp4">
<track label="English" kind="subtitles" srclang="en" src="captions/vtt/sintel-en.vtt" default>
</video>
</div>
<div style="margin-top: 2em;" class="ui one column center aligned grid">
<a href="https://{{video.instance}}/watch?v={{video.id}}"><h2 class="ui header">{{video.title}}</h2></a>
<h2 class="ui header">{{video.title}}</h2></a>
</div>
<div style="margin-top: 2em;" class="ui one column center aligned grid">
<a target="_blank" href="https://{{video.instance}}{{video.authorUrl}}" class="ui image label">
<img src="{{video.authorThumb}}">
{%if video.author.__len__() > 8%}
{{video.author[0:8]+'...'}}
{%else%}
{{video.author}}
{%endif%}
</a>
<div class="ui label">
<i class="eye icon"></i> {{video.viewCount}}
<div style="margin-top: 2em;" class="ui center aligned grid">
<div class="two wide column">
<a target="_blank" href="{{video.authorUrl}}" class="ui label">
{%if video.author.__len__() > 8%}
<i class="user icon"></i> {{video.author[0:10]+'...'}}
{%else%}
<i class="user icon"></i> {{video.author}}
{%endif%}
</a>
</div>
<div class="ui label">
<i class="thumbs up green icon"></i> {{video.likeCount}}
<div class="two wide column">
<div class="ui label">
<i class="eye icon"></i>{{video.viewCount}}
</div>
</div>
<div class="ui label">
<i class="thumbs down red icon"></i> {{video.dislikeCount}}
<div class="two wide column">
<div class="para-light-grey">
<div class="para-green" style="height:12px;width:{{video.averageRating.split(".")[0]}}%"></div>
</div>
<div class="label"> Rating: {{video.averageRating[0:4]}}% <i class="green thumbs up icon"></i></div>
</div>
</div>
<div style="margin-top: 2em;" class="ui one column center aligned grid">
<div style="margin-bottom: 2em;" class="ui comments">
<h3 class="ui dividing header">Description</h3>