├── .gitignore
├── LICENSE.md
├── README.md
├── pocket2notion.py
├── requirements.txt
└── settings.py
/.gitignore:
--------------------------------------------------------------------------------
1 | config.py
2 | .env
3 | *__pycache__
4 | *.html
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Jeffrey Jacob
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
PickPocket
5 |
6 | A python script to transfer all your Pocket links to a database in Notion.
7 |
8 | Explore the docs
9 | ·
10 | File issues and feature requests here
11 |
12 |
13 |
14 |
15 |
16 |
17 | ## Table of Contents
18 |
19 | - [Table of Contents](#table-of-contents)
20 | - [About The Project](#about-the-project)
21 | - [Getting Started](#getting-started)
22 | - [Prerequisites](#prerequisites)
23 | - [Setup & Installation](#setup--installation)
24 | - [Usage](#usage)
25 | - [Roadmap](#roadmap)
26 | - [Contributing](#contributing)
27 | - [License](#license)
28 | - [Contact](#contact)
29 | - [Acknowledgements](#acknowledgements)
30 |
31 |
32 |
33 |
34 | ## About The Project
35 |
36 | A python script to copy all your Pocket saves to a database in Notion. Current iteration is based off the HTML export option provided by the folks at Pocket.
37 |
38 | **Intended for**
39 | - Those who are looking to transfer their reading lists from Pocket to Notion.
40 | - Those who want to keep track and analyze what they read.
41 |
42 |
43 |
44 | ## Getting Started
45 |
46 | To get a local copy up and running follow these simple steps.
47 |
48 | ### Prerequisites
49 |
50 | * A Pocket account to retreive your saved content from.
51 | * A Notion account to store your links.
52 | * Python 3 on your system to run the code.
53 |
54 | ### Setup & Installation
55 |
56 | 1. Clone this repository.
57 | ```sh
58 | git clone https://github.com/paperboi/PickPocket.git
59 | ```
60 | 2. Navigate to the directory and install the pre-requisite packages using
61 | ```sh
62 | pip install -r requirements.txt
63 | ```
64 |
65 |
66 | ## Usage
67 | 1. Export your Pocket saves from [here](https://help.getpocket.com/article/1015-exporting-your-pocket-list).
68 | 2. Duplicate this [database template](https://www.notion.so/personaljeff/e4a0751a114842c6b2b238218e52e7d2?v=062127a6aa4341fb98e6d74b0eadfc4c) to your Notion workspace.
69 | 3. Since this code requires access of non-public pages, an authentication token from your Notion page is required. This token is stored in the `token_v2` cookie. This can be found in the *Storage* tab of your browser's developer tools.
70 | - For Chrome: Open Developer Tools (*Menu > Other tools > Developer Tools*), navigate to Application tab and go to *Storage\Cookies* to find the token.
71 | 4. Store the path to your HTML file, the address to your database and the `token_v2` value as `PATH_POCKET_FILE`, `NOTION_TABLE_ID` and `NOTION_TOKEN` respectively in a `.env` file in the same directory you have cloned this repository to.
72 | 5. To execute the script, navigate to the directory and run
73 | ```sh
74 | python pocket2notion.py
75 | ```
76 |
77 |
78 | ## Roadmap
79 |
80 | See the [open issues](https://github.com/paperboi/PickPocket/issues) for a list of proposed features (and known issues).
81 |
82 |
83 |
84 | ## Contributing
85 |
86 |
87 | Any contributions you make are **greatly appreciated**.
88 |
89 | 1. Fork the Project
90 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
91 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
92 | 4. Push to the Branch (`git push origin feature/AmazingFeature`)
93 | 5. Open a Pull Request
94 |
95 |
96 |
97 |
98 | ## License
99 |
100 | Distributed under the MIT License. See [LICENSE](https://github.com/paperboi/PickPocket/blob/master/LICENSE.md) for more information.
101 |
102 |
103 |
104 |
105 | ## Contact
106 |
107 | Jeffrey Jacob - [@DullBlackWall](https://twitter.com/DullBlackWall) - jeffreysamjacob@gmail.com
108 |
109 | Project Link: [https://github.com/paperboi/PickPocket](https://github.com/paperboi/PickPocket)
110 |
111 |
112 |
113 |
114 | ## Acknowledgements
115 |
116 | * [K.P. Govind](https://github.com/reisub0) for clearing my doubts every step of the way.
117 | * [Jamie Alexandre](https://github.com/jamalex/) for the powerful [notion-py](https://github.com/jamalex/notion-py) API.
118 |
--------------------------------------------------------------------------------
/pocket2notion.py:
--------------------------------------------------------------------------------
1 | import requests
2 | from time import sleep
3 | from bs4 import BeautifulSoup
4 | from settings import PATH_POCKET_FILE, NOTION_TOKEN, NOTION_TABLE_ID
5 |
6 | from random import choice
7 | from uuid import uuid1
8 |
9 | from datetime import datetime
10 | from notion.client import NotionClient
11 | from notion.collection import NotionDate
12 |
13 | class PocketListItem:
14 | title = ""
15 | url = ""
16 | tags = []
17 | addedOn = 0
18 | readStatus = None
19 |
20 | def __init__(self, title, url, tags, addedOn, readStatus):
21 | self.title = title
22 | self.url = url
23 | self.tags = tags
24 | self.addedOn = addedOn
25 | self.readStatus = readStatus
26 | if self.title == self.url:
27 | self._fetchTitleFromURL()
28 | self._addToNotion()
29 |
30 | def _fetchTitleFromURL(self):
31 | r = requests.get(self.url)
32 | sleep(1)
33 | soup = BeautifulSoup(r.content, 'lxml')
34 | self.title = soup.select_one('title').text
35 |
36 | def _addToNotion(self):
37 | row = cv.collection.add_row()
38 | row.title = self.title
39 | row.url = self.url
40 | self._setTag(row, 'prop', self.tags)
41 | row.added_on = NotionDate(datetime.fromtimestamp(self.addedOn))
42 | row.read = self.readStatus
43 | # print(f"{index}/{len(allPocketListItems)} added")
44 |
45 | def _addNewTag(self, schema, prop, tag):
46 | dupe = next(
47 | (o for o in prop["options"] if o["value"] == tag), None
48 | )
49 | if dupe:
50 | raise ValueError(f'{tag} already exists in the schema!')
51 |
52 | prop["options"].append(
53 | {"id": str(uuid1()), "value": tag, "color": choice(colors)}
54 | )
55 | try:
56 | cv.collection.set("schema", schema)
57 | except (RecursionError, UnicodeEncodeError):
58 | pass
59 |
60 | def _setTag(self, page, prop, new_values):
61 | schema = cv.collection.get("schema")
62 | new_values_set = set(new_values)
63 |
64 | if new_values == ['']:
65 | return []
66 |
67 | prop = next(
68 | (v for k, v in schema.items() if v["name"] == 'Tags'), None
69 | )
70 |
71 | if "options" not in prop: prop["options"] = []
72 |
73 | current_options_set = set(
74 | [o["value"] for o in prop["options"]]
75 | )
76 | intersection = new_values_set.intersection(current_options_set)
77 |
78 | if len(new_values_set) > len(intersection):
79 | difference = [v for v in new_values_set if v not in intersection]
80 | for d in difference:
81 | self._addNewTag(schema, prop, d)
82 | page.set_property('Tags', new_values)
83 |
84 | def itemAlreadyExists(itemURL):
85 | global allRows
86 | if allRows == []:
87 | return False
88 | for eachRow in allRows:
89 | if itemURL == eachRow.url:
90 | return True
91 | print(f"Adding {itemURL} to the list")
92 | return False
93 |
94 | def retrieveAllPocketItems():
95 | with open(PATH_POCKET_FILE, encoding='utf8', errors='ignore') as fp:
96 | soup = BeautifulSoup(fp,'html.parser')
97 | itemList = soup.h1.find_next("h1")
98 |
99 | articles = itemList.find_all_previous("a")
100 | for eachItem in articles:
101 | if itemAlreadyExists(eachItem['href']):
102 | continue
103 | url = eachItem['href']
104 | title = eachItem.get_text()
105 | tags = eachItem['tags'].split(',')
106 | addedOn = int(eachItem['time_added'])
107 | readStatus = False
108 | PocketListItem(title,url,tags,addedOn,readStatus)
109 |
110 | # Retreiving the items from the user's Archive list next.
111 | articles = itemList.find_all_next("a")
112 | for eachItem in articles:
113 | if itemAlreadyExists(eachItem['href']):
114 | continue
115 | url = eachItem['href']
116 | title = eachItem.get_text()
117 | tags = eachItem['tags'].split(',')
118 | addedOn = int(eachItem['time_added'])
119 | readStatus = True
120 | PocketListItem(title,url,tags,addedOn,readStatus)
121 |
122 | colors = ['default', 'gray', 'brown', 'orange', 'yellow', 'green', 'blue', 'purple', 'pink', 'red']
123 |
124 | client = NotionClient(token_v2= NOTION_TOKEN)
125 | cv = client.get_collection_view(NOTION_TABLE_ID)
126 | allRows = cv.collection.get_rows()
127 |
128 | print(cv.parent.views)
129 |
130 | retrieveAllPocketItems()
131 | print("Transfer successfully completed")
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paperboi/PickPocket/e3f39b135bffeaf376b962a7dff520fdcef0ae96/requirements.txt
--------------------------------------------------------------------------------
/settings.py:
--------------------------------------------------------------------------------
1 | import os
2 | from decouple import config
3 |
4 | # Store these values in an .env file in this directory
5 | PATH_POCKET_FILE = config('PATH_POCKET_FILE')
6 | NOTION_TOKEN = config('NOTION_TOKEN')
7 | NOTION_TABLE_ID = config('NOTION_TABLE_ID')
--------------------------------------------------------------------------------