[
  {
    "path": "GoogleAuthManager.py",
    "content": "from pydrive.auth import GoogleAuth\nfrom pydrive.drive import GoogleDrive\nfrom os import path\n\n\ndef create_credential():\n    from GoogleAuthV1 import auth_and_save_credential\n    auth_and_save_credential()\n\n\n# Authentication + token creation\ndef create_drive_manager():\n    gAuth = GoogleAuth()\n    typeOfAuth = None\n    if not path.exists(\"credentials.txt\"):\n        typeOfAuth = input(\"type save if you want to keep a credential file, else type nothing\")\n    bool = True if typeOfAuth == \"save\" or path.exists(\"credentials.txt\") else False\n    authorize_from_credential(gAuth, bool)\n    drive: GoogleDrive = GoogleDrive(gAuth)\n    return drive\n\n\ndef authorize_from_credential(gAuth, isSaved):\n    if not isSaved: #no credential.txt wanted\n        from GoogleAuthV1 import auth_no_save\n        auth_no_save(gAuth)\n    if isSaved and not path.exists(\"credentials.txt\"):\n        create_credential()\n        gAuth.LoadCredentialsFile(\"credentials.txt\")\n    if isSaved and gAuth.access_token_expired:\n        gAuth.LoadCredentialsFile(\"credentials.txt\")\n        gAuth.Refresh()\n        print(\"token refreshed!\")\n        gAuth.SaveCredentialsFile(\"credentials.txt\")\n    gAuth.Authorize()\n    print(\"authorized access to google drive API!\")\n"
  },
  {
    "path": "GoogleAuthV1.py",
    "content": "from pydrive.auth import GoogleAuth\ndef auth_and_save_credential():\n    gAuth = GoogleAuth()\n    gAuth.LocalWebserverAuth()\n    gAuth.SaveCredentialsFile(\"credentials.txt\")\ndef auth_no_save(gAuth):\n    gAuth.LocalWebserverAuth()"
  },
  {
    "path": "GoogleAuthV2.py",
    "content": "from pydrive.auth import GoogleAuth\nimport webbrowser\n\n# https://realpython.com/flask-google-login/#creating-a-google-client\nfrom flask import Flask, redirect, request, url_for\nfrom flask_login import LoginManager\nfrom oauthlib.oauth2 import WebApplicationClient\nimport requests\n\nGOOGLE_DISCOVERY_URL = (\n    \"https://accounts.google.com/.well-known/openid-configuration\"\n)\n\n\nclass FlaskModified(Flask):\n    def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):\n        with self.app_context():\n            webbrowser.open('https://127.0.0.1:5000') #default url and port for Flask app\n            pass\n        super(FlaskModified, self).run(host=host, port=port, debug=debug, load_dotenv=load_dotenv, **options)\n\n\napp = FlaskModified(__name__)\n\ngAuth = GoogleAuth()\nauth_url = gAuth.GetAuthUrl()\n\n\n@app.route(\"/\")\ndef index():\n    return '<a class=\"button\" href=\"/login\">Google Login</a>'\n\n\n@app.route(\"/login\")\ndef login():\n    return redirect(auth_url)\n\n\n@app.route(\"/login/callback\")\ndef callback():\n    code = request.args.get(\"code\")\n    print(\"code : \" + code)\n    gAuth.Auth(code)\n    return redirect(url_for(\"index\"))\n\n\ndef auth_and_save_credential():\n    app.run(debug=\"true\", ssl_context=\"adhoc\")\n\n\n#app.run(debug=\"true\", ssl_context=\"adhoc\")\n"
  },
  {
    "path": "README.md",
    "content": "# googledrive-copy-downloader\nThis python script allow you to download google drive files even if the daily limit of download has excedeed using google drive API easily from the download link(s).\nIt automatically copy the file to the google drive of the account that is provided, download the file and delete it, and it supports multiple links at once.\n\n\n## How to use : \nFirst, you need to add a Google application credentials(clients_secrets.json) that can access the Google drive API from your account.\n\nTo do that, the quickest way is to :\n* Go to : https://developers.google.com/drive/api/v3/quickstart/python\n* Click on enable drive API\n* Download client configuration\n* Rename this file client_secrets.json\n* Replace the one in the folder with the script (in the same place as the .exe)\n* (Optional) After the first launch, you can change settings in the config.ini file (download location, dowloading directly the links from the clipboard or (hasn't been tested) modify folderId with the ID of a folder on a google drive team)\n\n#### You can launch it directly using python :\nUse preferably python 3.6, install the requirement on the requirements.txt file with pip and launch gDriveCopyDownloader.py.\n#### You can use the release (Windows Only):\nYou just need to download the .zip, and launch the exe.\n#### You can make yourself the .exe :\nUse auto-py-to-exe or pyinstaller, select gDriveCopyDownloader as the input.\n\n\nThen you can follow the instructions on the script. Careful if you store the credentials (to avoid log-in every time), it keeps them in plain text and it can give access to your google drive(If you don't store anything sensitive and you don't care if your google drive is hacked it isn't a problem).\nAlso, the clipboard feature supports multiple hyperlinks.\n\n\n### Common issues : \n* crash on startup :\nCheck the How to use section, the client secret could be missing.\n* \"This app has not been verified yet\" or any other issues with client_secrets : \nCheck the How to use section.\n* Space issues :\nthe script could crash itself if there is not enough space on your google drive, be sure to check the bin.\n* Authentication problems  :\nIf you have any app that communicate through localhost:8080 (like Kodi), the authentication with google servers may not works.\n\n\n### Known limitations :\n* clipboard feature only works on Windows.\n* There might be somme issues with linux(getting the default download folder for example).\n* Currently, the script only support links with that end with : /d/XXXX, /id=XXX and /folders/XXXX .\n* The script doesn't display the speed of download in real-time.\n* It doesn't work with google files(sheet, docs,...).\n\n\n"
  },
  {
    "path": "gDriveCopyDownloader.py",
    "content": "import os\n\nimport GoogleAuthManager\nimport gDriveLibrary\nimport configparser\nimport asyncio\n\nCONFIG = configparser.ConfigParser()\nCONFIG.optionxform=str\nDATABASE = CONFIG['DEFAULT']\n\ndef get_default_download_location():\n    if os.name == 'nt':  # if windows\n        import winreg\n        sub_key = r'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders'\n        downloads_guid = '{374DE290-123F-4565-9164-39C4925E467B}'\n        with winreg.OpenKey(winreg.HKEY_CURRENT_USER, sub_key) as key:\n            location = winreg.QueryValueEx(key, downloads_guid)[0]\n        return location\n    else:\n        return os.path.join(os.path.expanduser('~'), 'downloads')\n\ndef read_config():\n    CONFIG.read('config.ini')\n\ndef write_config():\n    if 'ClipboardDetection' not in DATABASE:\n        DATABASE['ClipboardDetection'] = \"0\"\n    with open(\"config.ini\", \"w+\") as file:\n        CONFIG.write(file)\n\ndef get_location():\n    read_config()\n    if 'DownloadPath' in DATABASE and os.path.exists(DATABASE['DownloadPath'] ):\n        return DATABASE['DownloadPath']\n    print(\"Default location is : \" + get_default_download_location())\n    while 1:\n        otherPath = input(\"\\nif you want another location, write it here or else press enter\\n\")\n        if os.path.exists(otherPath):\n            break\n        if otherPath == \"\":\n            otherPath = get_default_download_location()\n            break\n    DATABASE['DownloadPath'] = otherPath\n    write_config()\n    return otherPath\n\ndef get_folder_id():\n    if not 'FolderId' in DATABASE:\n        DATABASE['FolderId'] = 'root'\n        write_config()\n    return DATABASE['FolderId']\n\ndef Copy_dwnld_from_links(links, drive):\n    for fileID in gDriveLibrary.extract_files_id(links, drive):\n        copiedFile = gDriveLibrary.copy_file(drive, fileID, get_folder_id())\n        gDriveLibrary.download_file(drive, copiedFile, get_location())\n        gDriveLibrary.delete_file(drive, copiedFile['id'])\n\n\nasync def Check_clipboard_links(drive): #Only works for windows\n    # Ressource : https://stackoverflow.com/questions/55698762/how-to-copy-html-code-to-clipboard-using-python\n    if os.name == 'nt':\n        import win32clipboard\n    else:\n        raise OSError(\"os isn't windows !\")\n    CF_HTML = win32clipboard.RegisterClipboardFormat(\"HTML Format\")\n    cacheClipboard = \"\"\n    while 1:\n        await asyncio.sleep(1)\n        win32clipboard.OpenClipboard(0)\n        try:\n            src = win32clipboard.GetClipboardData(CF_HTML).decode(\"UTF-8\")\n        except TypeError:#if not html\n            try:\n                src = win32clipboard.GetClipboardData(win32clipboard.CF_TEXT).decode(\"UTF-8\")\n            except TypeError:\n                src = \"\"\n        win32clipboard.CloseClipboard()\n        if(src != cacheClipboard): #avoid downloading infinite loop if still in clipboard\n            cacheClipboard = src\n            Copy_dwnld_from_links(src, drive)\n\nprint(\" Some infos : \\n\"\n      \"You can put the links directly from google drive ('https://drive.google.com') but also those behind a \"\n      \"redirection(like the one from igg).\\n\"\n      \"Temporary files in google drive of your download will be stored on 'Temp folder for script', you can delete it \"\n      \"after the downloads. \\n\"\n      \"Settings are stored in the config.ini file.\\n\"\n      \"If you want to put the google drive folder in a custom folder(to use your google team account), \"\n      \"edit the FolderId field in config.ini and replace 'root' with the google drive team folder id.\\n\"\n      \"If you use Windows, you can go on config.ini and change ClipboardDetection to ClipboardDetection=1,\"\n      \"you just have to Ctrl+C the links to download and it should handle the rest.\\n\"\n      \"The download percentage status is updated about every 100 MB, so wait a little if it appears to be stuck.\\n\"\n      \"You can put multiple links at the same time\\n\")\nprint(\"###############\")\nprint(\"\\n Careful, if you choose to save the credentials : \"\n      \" An access to your google drive will be/is stored on 'credentials.txt'. \\n IT COULD BE USED BY SOMEONE ELSE\"\n      \" TO ACCESS, DOWNLOAD OR DELETE FILES FROM YOUR GOOGLE DRIVE. \\n I'm not responsible if anything bad happen\"\n      \" in your drive.\\n\")\nprint(\"###############\")\ndrive = GoogleAuthManager.create_drive_manager()\nget_location()  # ask for path if not set\nget_folder_id() # create folder id txt file\nwhile 1:\n    if 'ClipboardDetection' in DATABASE and DATABASE['ClipboardDetection'] == \"1\" and os.name == 'nt':\n        print(\"The script now captures your links from your clipboard !\")\n        asyncio.run(Check_clipboard_links(drive))\n    else:\n        links = input(\"paste the link(s) to download : \\n\")\n        Copy_dwnld_from_links(links, drive)"
  },
  {
    "path": "gDriveLibrary.py",
    "content": "\"\"\"\nDocumentation :\nexplain client secret(how to replace it if needed) : https://developers.google.com/drive/api/v3/quickstart/python\ndownload : the ile is chucked with pieces of ~100MB, need to download at least this amount beore appearing on screen\n\"\"\"\nimport re\nimport os\nfrom googleapiclient.http import MediaIoBaseDownload\nfrom tqdm import tqdm\n\n\n# get download folder from user :\n# https://stackoverflow.com/questions/35851281/python-finding-the-users-downloads-folder\n\n\ndef get_Gdrive_folder_id(drive, driveService, name, parent=\"root\"):  # return ID of folder, create it if missing\n    body = {'title': name,\n            'mimeType': \"application/vnd.google-apps.folder\"\n            }\n    query = \"title='Temp folder for script' and mimeType='application/vnd.google-apps.folder'\" \\\n            \" and '\" + parent + \"' in parents and trashed=false\"\n    if parent != \"root\":\n        query += \"and driveId='\" + parent + \"' and includeItemsFromAllDrives=true and supportsAllDrives = true\"\n    listFolders = drive.ListFile({'q': query})\n    for subList in listFolders:\n        if subList == []:  # if folder doesn't exist, create it\n            folder = driveService.files().insert(body=body).execute()\n            break\n        else:\n            folder = subList[0]  # if one folder with the correct name exist, pick it\n\n    return folder['id']\n\n\ndef extract_file_ids_from_folder(drive, folderID):\n    files = drive.ListFile({'q': \"'\" + folderID + \"' in parents\"}).GetList()\n    fileIDs = []\n    for file in files :\n        fileIDs.append(file['id'])\n    return fileIDs\n\n\ndef extract_files_id(links, drive):\n    # copy of google drive file from google drive link :\n    links = re.findall(r\"\\b(?:https?:\\/\\/)?(?:drive\\.google\\.com[-_&?=a-zA-Z\\/\\d]+)\",\n                       links)  # extract google drive links\n    try:\n        fileIDs = [re.search(r\"(?<=/d/|id=|rs/).+?(?=/|$)\", link)[0] for link in links]  # extract the fileIDs\n        for fileID in fileIDs:\n            if drive.auth.service.files().get(fileId=fileID).execute()['mimeType'] == \"application/vnd.google-apps.folder\":\n                fileIDs.extend(extract_file_ids_from_folder(drive, fileID))\n                fileIDs.remove(fileID)\n        return fileIDs\n    except Exception as error:\n        print(\"error : \" + str(error))\n        print(\"Link is probably invalid\")\n        print(links)\n\n\ndef copy_file(drive, fileId, parentFolder = \"root\"): #if different parentFolder, input the folder ID\n    fileOriginMetaData = drive.auth.service.files().get(fileId=fileId).execute()\n    \"\"\"remove 4 last characters of the original file name \n    and add file extension(should be .rar) in case the file extension is missing from the name \"\"\"\n    nameNoExtension = \".\".join(fileOriginMetaData['originalFilename'].split(\".\")[:-1])\n    newFileName = nameNoExtension + \".\" + fileOriginMetaData['fileExtension']\n    print(\"Name of the file on your google drive and on the disk: \" + newFileName)\n    folderID = get_Gdrive_folder_id(drive, drive.auth.service, \"Temp folder for script\", parentFolder)\n    copiedFileMetaData = {\"parents\": [{\"id\": str(folderID)}], 'title': newFileName}  # ID of destination folder\n    copiedFile = drive.auth.service.files().copy(\n        fileId=fileId,\n        body=copiedFileMetaData\n    ).execute()\n    return copiedFile\n\n\ndef download_file(drive, file, destFolder):\n    copiedFileMedia = drive.auth.service.files().get_media(fileId=file['id'])\n    newFileName = file['title']\n    defaultPath = destFolder + \"\\\\\" + newFileName\n    fullPath = generate_path_with_unique_filename(destFolder, newFileName)\n    if defaultPath != fullPath :\n        print(\"File already exist in the disk, new path: \" + fullPath)\n    print(\"Download in progress. File size: \" + sizeof_file(int(file['fileSize'])))\n\n    step = 104857600//1048576\n    fsize = int(file['fileSize'])//1048576\n\n    file = open(fullPath, \"wb+\")\n    downloader = MediaIoBaseDownload(file, copiedFileMedia, chunksize=104857600)  # change chunksize here\n    done = False\n\n    pbar = tqdm(desc='Downloading', unit='MB', total=fsize)\n    while done is False:\n        status, done = downloader.next_chunk()\n        pbar.update(step)\n    pbar.close()\n    file.close()\n    print(\"\\nDownload completed : \" + newFileName)\n\ndef delete_file(drive, id):\n    drive.auth.service.files().delete(fileId=id).execute()\n\n#https://stackoverflow.com/questions/1094841/reusable-library-to-get-human-readable-version-of-file-size\ndef sizeof_file(num, suffix='B'):\n    for unit in ['','K','M','G','Ti','Pi','Ei','Zi']:\n        if abs(num) < 1024.0:\n            return \"%3.1f%s%s\" % (num, unit, suffix)\n        num /= 1024.0\n    return \"%.1f%s%s\" % (num, 'Yi', suffix)\n\ndef generate_path_with_unique_filename(folder, filename):\n    fullpath = folder + \"\\\\\" + filename\n    if not os.path.exists(fullpath):\n        return fullpath\n    fileNumber = 1\n    while(os.path.exists(fullpath)):\n        fullpath = folder + \"\\\\\" + str(fileNumber) + filename\n        fileNumber+=1\n    return fullpath"
  },
  {
    "path": "requirements.txt",
    "content": "altgraph==0.17\nauto-py-to-exe==2.6.6\nbottle==0.12.18\nbottle-websocket==0.2.9\ncachetools==4.0.0\ncertifi==2019.11.28\ncffi==1.13.2\nchardet==3.0.4\nClick==7.0\ncryptography==2.8\nEel==0.11.0\nFlask==1.1.1\nFlask-Login==0.4.1\nfuture==0.18.2\ngevent==1.4.0\ngevent-websocket==0.10.1\ngoogle-api-python-client==1.7.11\ngoogle-auth==1.10.0\ngoogle-auth-httplib2==0.0.3\ngreenlet==0.4.15\nhttplib2==0.15.0\nidna==2.8\nitsdangerous==1.1.0\nJinja2==2.10.3\nMarkupSafe==1.1.1\noauth2==1.9.0.post1\noauth2client==4.1.3\noauthlib==3.1.0\npefile==2019.4.18\npyasn1==0.4.8\npyasn1-modules==0.2.7\npycparser==2.19\nPyDrive==1.3.1\nPyInstaller==3.6\npyOpenSSL==19.1.0\npywin32==227\npywin32-ctypes==0.2.0\nPyYAML==5.2\nrequests==2.22.0\nrsa==4.0\nsix==1.13.0\ntqdm==4.43.0\nuritemplate==3.0.1\nurllib3==1.25.7\nWerkzeug==0.16.0\nwhichcraft==0.6.1\n"
  }
]