views.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. import bleach
  2. from allauth.socialaccount.models import SocialApp
  3. from django.conf import settings
  4. from django.contrib import messages
  5. from django.contrib.auth import logout
  6. from django.contrib.auth.decorators import login_required
  7. from django.contrib.auth.models import User
  8. from django.contrib.sites.models import Site
  9. from django.db.models import Q
  10. from django.http import HttpResponse
  11. from django.shortcuts import redirect, render
  12. from django.template import loader
  13. from django.views.decorators.http import require_POST
  14. from backend.main.models import Playlist
  15. from ..general.utils.misc import print_
  16. from .models import Untube
  17. # Create your views here.
  18. def index(request):
  19. if Untube.objects.all().count() == 0:
  20. untube = Untube.objects.create()
  21. untube.save()
  22. if settings.GOOGLE_OAUTH_CLIENT_ID is NotImplemented or settings.GOOGLE_OAUTH_CLIENT_SECRET is NotImplemented:
  23. messages.error(request, 'Please fill in your Google OAuth credentials in the local/settings.dev.py file')
  24. else:
  25. # messages.success(request, 'Thanks for filling in the YouTube API key')
  26. if not Site.objects.filter(domain=settings.GOOGLE_OAUTH_URI).exists():
  27. Site.objects.create(domain=settings.GOOGLE_OAUTH_URI, name=settings.GOOGLE_OAUTH_URI)
  28. if not SocialApp.objects.filter(provider='google').exists(): # create google OAuth app
  29. print('Creating Google social app...')
  30. app = SocialApp.objects.create(
  31. provider='google',
  32. name='UnTube OAuth',
  33. client_id=settings.GOOGLE_OAUTH_CLIENT_ID,
  34. secret=settings.GOOGLE_OAUTH_CLIENT_SECRET
  35. )
  36. site_uri = Site.objects.get(domain=settings.GOOGLE_OAUTH_URI)
  37. app.sites.add(site_uri)
  38. if not request.session.exists(request.session.session_key):
  39. request.session.create()
  40. request.session['liked_untube'] = False
  41. return render(
  42. request, 'index.html', {
  43. 'likes': Untube.objects.all().first().page_likes,
  44. 'users_joined': User.objects.all().count()
  45. }
  46. )
  47. # if request.user.is_anonymous:
  48. # return render(request, 'index.html', {'likes': Untube.objects.all().first().page_likes,
  49. # 'users_joined': User.objects.all().count()})
  50. # else:
  51. # return redirect('home')
  52. def about(request):
  53. return render(request, 'about.html')
  54. @login_required
  55. def profile(request):
  56. user_playlists = request.user.playlists.all()
  57. watching = user_playlists.filter(marked_as='watching')
  58. total_num_playlists = user_playlists.count()
  59. statistics = {'public_x': 0, 'private_x': 0, 'favorites_x': 0, 'watching_x': 0, 'imported_x': 0}
  60. if total_num_playlists != 0:
  61. # x means percentage
  62. statistics['public_x'
  63. ] = round(user_playlists.filter(is_private_on_yt=False).count() / total_num_playlists, 1) * 100
  64. statistics['private_x'
  65. ] = round(user_playlists.filter(is_private_on_yt=True).count() / total_num_playlists, 1) * 100
  66. statistics['favorites_x'
  67. ] = round(user_playlists.filter(is_favorite=True).count() / total_num_playlists, 1) * 100
  68. statistics['watching_x'
  69. ] = round(user_playlists.filter(marked_as='watching').count() / total_num_playlists, 1) * 100
  70. statistics['imported_x'
  71. ] = round(user_playlists.filter(is_user_owned=False).count() / total_num_playlists, 1) * 100
  72. return render(
  73. request, 'profile.html', {
  74. 'total_num_playlists': total_num_playlists,
  75. 'statistics': statistics,
  76. 'watching': watching
  77. }
  78. )
  79. @login_required
  80. def user_settings(request):
  81. return render(request, 'settings.html')
  82. @require_POST
  83. def update_settings(request):
  84. print(request.POST)
  85. user = request.user
  86. username_input = bleach.clean(request.POST['username'].strip())
  87. message_content = 'Saved!'
  88. # message_type = 'success'
  89. if username_input != user.username:
  90. if User.objects.filter(username__exact=username_input).count() != 0:
  91. # message_type = 'danger'
  92. message_content = f'Username {username_input} already taken'
  93. messages.error(request, message_content)
  94. else:
  95. user.username = username_input
  96. # user.save()
  97. message_content = f'Username updated to {username_input}!'
  98. messages.success(request, message_content)
  99. if 'open search in new tab' in request.POST and user.profile.open_search_new_tab is False:
  100. user.profile.open_search_new_tab = True
  101. elif 'open search in new tab' not in request.POST and user.profile.open_search_new_tab is True:
  102. user.profile.open_search_new_tab = False
  103. if 'enable gradient bg' in request.POST and user.profile.enable_gradient_bg is False:
  104. user.profile.enable_gradient_bg = True
  105. elif 'enable gradient bg' not in request.POST and user.profile.enable_gradient_bg is True:
  106. user.profile.enable_gradient_bg = False
  107. if 'confirm before deleting' in request.POST and user.profile.confirm_before_deleting is False:
  108. user.profile.confirm_before_deleting = True
  109. elif 'confirm before deleting' not in request.POST and user.profile.confirm_before_deleting is True:
  110. user.profile.confirm_before_deleting = False
  111. if 'hide videos' in request.POST and user.profile.hide_unavailable_videos is False:
  112. user.profile.hide_unavailable_videos = True
  113. elif 'hide videos' not in request.POST and user.profile.hide_unavailable_videos is True:
  114. user.profile.hide_unavailable_videos = False
  115. user.save()
  116. if message_content == 'Saved!':
  117. messages.success(request, message_content)
  118. return redirect('settings')
  119. @login_required
  120. def delete_account(request):
  121. request.user.playlists.all().delete()
  122. request.user.videos.all().delete()
  123. request.user.playlist_tags.all().delete()
  124. request.user.profile.delete()
  125. request.user.delete()
  126. request.session.flush()
  127. messages.success(request, 'Account data deleted successfully.')
  128. return redirect('index')
  129. @login_required
  130. def log_out(request):
  131. request.session.flush() # delete all stored session keys
  132. logout(request) # log out authenticated user
  133. if 'troll' in request.GET:
  134. print('TROLLED')
  135. messages.success(request, 'Hee Hee')
  136. else:
  137. messages.success(request, 'Successfully logged out. Hope to see you back again!')
  138. return redirect('/')
  139. @login_required
  140. def cancel_import(request):
  141. user_profile = request.user.profile
  142. user_profile.imported_yt_playlists = False
  143. user_profile.show_import_page = False
  144. user_profile.save()
  145. return redirect('home')
  146. @login_required
  147. def import_user_yt_playlists(request):
  148. request.user.profile.show_import_page = True
  149. request.user.profile.save(update_fields=['show_import_page'])
  150. return render(request, 'import_in_progress.html')
  151. @login_required
  152. def start_import(request):
  153. """
  154. Initializes only the user's playlist data in the database. Returns the progress bar, which will
  155. keep calling continue_import
  156. """
  157. user_profile = request.user.profile
  158. result = Playlist.objects.initializePlaylist(request.user)
  159. if result['status'] == -1:
  160. print('User has no YT channel')
  161. return HttpResponse(
  162. loader.get_template('intercooler/progress_bar.html').render({
  163. 'channel_found': False,
  164. 'error_message': result['error_message']
  165. })
  166. )
  167. elif result['status'] == -2:
  168. user_profile.import_in_progress = False
  169. user_profile.imported_yt_playlists = True
  170. user_profile.show_import_page = True
  171. user_profile.save()
  172. print('User has no playlists on YT')
  173. # if request.user.profile.yt_channel_id == '':
  174. # Playlist.objects.getUserYTChannelID(request.user)
  175. Playlist.objects.initializePlaylist(request.user, 'LL')
  176. return HttpResponse(
  177. loader.get_template('intercooler/progress_bar.html').render({
  178. 'total_playlists': 0,
  179. 'playlists_imported': 0,
  180. 'done': True,
  181. 'progress': 100,
  182. 'channel_found': True
  183. })
  184. )
  185. else:
  186. # if request.user.profile.yt_channel_id == '':
  187. # Playlist.objects.getUserYTChannelID(request.user)
  188. Playlist.objects.initializePlaylist(request.user, 'LL')
  189. user_profile.import_in_progress = True
  190. user_profile.save()
  191. return HttpResponse(
  192. loader.get_template('intercooler/progress_bar.html').render({
  193. 'total_playlists': result['num_of_playlists'],
  194. 'playlist_name': result['first_playlist_name'],
  195. 'playlists_imported': 0,
  196. 'progress': 0,
  197. 'channel_found': True
  198. })
  199. )
  200. @login_required
  201. def continue_import(request):
  202. if request.user.profile.import_in_progress is False:
  203. return redirect('home')
  204. num_of_playlists = request.user.playlists.filter(Q(is_user_owned=True)).exclude(playlist_id='LL').count()
  205. print_('NUM OF PLAYLISTS', num_of_playlists)
  206. try:
  207. remaining_playlists = request.user.playlists.filter(Q(is_user_owned=True) &
  208. Q(is_in_db=False)).exclude(playlist_id='LL')
  209. print_(remaining_playlists.count(), 'REMAINING PLAYLISTS')
  210. playlists_imported = num_of_playlists - remaining_playlists.count() + 1
  211. playlist = remaining_playlists.order_by('created_at')[0]
  212. playlist_name = playlist.name
  213. playlist_id = playlist.playlist_id
  214. Playlist.objects.getAllVideosForPlaylist(request.user, playlist_id)
  215. except Exception:
  216. print_('NO REMAINING PLAYLISTS')
  217. playlist_id = -1
  218. if playlist_id != -1:
  219. return HttpResponse(
  220. loader.get_template('intercooler/progress_bar.html').render({
  221. 'total_playlists': num_of_playlists,
  222. 'playlists_imported': playlists_imported,
  223. 'playlist_name': playlist_name,
  224. 'progress': round((playlists_imported / num_of_playlists) * 100, 1),
  225. 'channel_found': True
  226. })
  227. )
  228. else:
  229. # request.user.profile.just_joined = False
  230. request.user.profile.import_in_progress = False
  231. request.user.profile.imported_yt_playlists = True
  232. request.user.profile.show_import_page = True # set back to true again so as to show users the welcome screen on 'home'
  233. request.user.save()
  234. user_pl_count = request.user.playlists.filter(Q(is_user_owned=True) &
  235. Q(is_in_db=True)).exclude(playlist_id='LL').count()
  236. return HttpResponse(
  237. loader.get_template('intercooler/progress_bar.html').render({
  238. 'total_playlists': user_pl_count,
  239. 'playlists_imported': user_pl_count,
  240. 'done': True,
  241. 'progress': 100,
  242. 'channel_found': True
  243. })
  244. )
  245. @login_required
  246. def user_playlists_updates(request, action):
  247. """
  248. Gets all user created playlist's ids from YouTube and checks them with the user playlists imported on UnTube.
  249. If any playlist id is on UnTube but not on YouTube, deletes the playlist from YouTube.
  250. If any new playlist id, imports it to UnTube
  251. """
  252. if action == 'check-for-updates':
  253. user_playlists_on_UnTube = request.user.playlists.filter(Q(is_user_owned=True) &
  254. Q(is_in_db=True)).exclude(playlist_id='LL')
  255. result = Playlist.objects.initializePlaylist(request.user)
  256. print_(result)
  257. youtube_playlist_ids = result['playlist_ids']
  258. untube_playlist_ids = []
  259. for playlist in user_playlists_on_UnTube:
  260. untube_playlist_ids.append(playlist.playlist_id)
  261. deleted_playlist_ids = []
  262. deleted_playlist_names = []
  263. for pl_id in untube_playlist_ids:
  264. if pl_id not in youtube_playlist_ids: # ie this playlist was deleted on youtube
  265. deleted_playlist_ids.append(pl_id)
  266. pl = request.user.playlists.get(playlist_id__exact=pl_id)
  267. deleted_playlist_names.append(f'{pl.name} (had {pl.video_count} videos)')
  268. pl.delete()
  269. if result['num_of_playlists'] == user_playlists_on_UnTube.count() and len(deleted_playlist_ids) == 0:
  270. print_('No new updates')
  271. playlists = []
  272. else:
  273. playlists = request.user.playlists.filter(Q(is_user_owned=True) &
  274. Q(is_in_db=False)).exclude(playlist_id='LL')
  275. print_(
  276. f'New updates found! {playlists.count()} newly added and {len(deleted_playlist_ids)} playlists deleted!'
  277. )
  278. print_(deleted_playlist_names)
  279. return HttpResponse(
  280. loader.get_template('intercooler/user_playlist_updates.html').render({
  281. 'playlists': playlists,
  282. 'deleted_playlist_names': deleted_playlist_names
  283. })
  284. )
  285. elif action == 'init-update':
  286. unimported_playlists = request.user.playlists.filter(Q(is_user_owned=True) &
  287. Q(is_in_db=False)).exclude(playlist_id='LL').count()
  288. return HttpResponse(
  289. f"""
  290. <div hx-get='/updates/user-playlists/start-update' hx-trigger='load' hx-target='#user-pl-updates'>
  291. <div class='alert alert-dismissible fade show' role='alert' style='background-color: cadetblue'>
  292. <div class='d-flex justify-content-center mt-4 mb-3 ms-2' id='loading-sign' >
  293. <img src='/static/svg-loaders/spinning-circles.svg' width='40' height='40'>
  294. <h5 class='mt-2 ms-2 text-black'>Importing {unimported_playlists} new playlists into UnTube, please wait!</h5>
  295. </div>
  296. </div>
  297. </div>
  298. """
  299. )
  300. elif action == 'start-update':
  301. unimported_playlists = request.user.playlists.filter(Q(is_user_owned=True) &
  302. Q(is_in_db=False)).exclude(playlist_id='LL')
  303. for playlist in unimported_playlists:
  304. Playlist.objects.getAllVideosForPlaylist(request.user, playlist.playlist_id)
  305. return HttpResponse(
  306. """
  307. <div class='alert alert-success alert-dismissible fade show d-flex justify-content-center' role='alert'>
  308. <h4 class=''>Successfully imported new playlists into UnTube!</h4>
  309. <meta http-equiv='refresh' content='0;url=/home/#recent-playlists' />
  310. <meta http-equiv='refresh' content='2;url=/home/' />
  311. <button type='button' class='btn-close' data-bs-dismiss='alert' aria-la bel='Close'></button>
  312. </div>
  313. """
  314. )
  315. @login_required
  316. def get_user_liked_videos_playlist(request):
  317. if not request.user.playlists.filter(Q(playlist_id='LL') & Q(is_in_db=True)).exists():
  318. Playlist.objects.initializePlaylist(request.user, 'LL')
  319. Playlist.objects.getAllVideosForPlaylist(request.user, 'LL')
  320. messages.success(request, 'Successfully imported your Liked Videos playlist!')
  321. return HttpResponse("""
  322. <script>
  323. window.location.reload();
  324. </script>
  325. """)
  326. # FOR INDEX.HTML
  327. @require_POST
  328. def like_untube(request):
  329. untube = Untube.objects.all().first()
  330. untube.page_likes += 1
  331. untube.save()
  332. request.session['liked_untube'] = True
  333. request.session.save()
  334. return HttpResponse(
  335. f"""
  336. <a hx-post='/unlike-untube/' hx-swap='outerHTML' style='text-decoration: none; color: black'>
  337. <i class='fas fa-heart' style='color: #d02e2e'></i> {untube.page_likes} likes (p.s glad you liked it!)
  338. </a>
  339. """
  340. )
  341. @require_POST
  342. def unlike_untube(request):
  343. untube = Untube.objects.all().first()
  344. untube.page_likes -= 1
  345. untube.save()
  346. request.session['liked_untube'] = False
  347. request.session.save()
  348. return HttpResponse(
  349. f"""
  350. <a hx-post='/like-untube/' hx-swap='outerHTML' style='text-decoration: none; color: black'>
  351. <i class='fas fa-heart'></i> {untube.page_likes} likes (p.s :/)
  352. </a>
  353. """
  354. )