views.py 16 KB

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